Python Forum
in c# create a loop counting from 0 to 5, consecutively
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
in c# create a loop counting from 0 to 5, consecutively
#11
Seems like you're calculation is backwards. I think this is what you are doing:
price = [1, 3, 5, 7, 11, 13, 17]
volume = [1, 1, 2, 3, 5, 8, 13]


def cma_calc(price, volume, start=0):
    total_clovol = 0
    total_volume = 0
    for p, v in zip(price[start:], volume[start:]):
        clovol = p * v
        total_clovol += clovol
        total_volume += v
    return total_clovol / total_volume


cma = []
for start in range(len(price)):
    cma.append(cma_calc(price, volume, start))
print(cma)
Output:
[12.575757575757576, 12.9375, 13.258064516129032, 13.827586206896552, 14.615384615384615, 15.476190476190476, 17.0]
It is very inefficient because you keep computing the cma over and over again for the same data. Computing cma for the 7 points above takes 7 + 6 + 5 + 4 + 3 + 2 + 1 = 28 calculations. The number grows quickly. 1000 points takes 499500 calculations.

If you look at the calculation, cma = sum_clovol / sum volume. Doing the calculations in the order above doesn't make use of clovol or volume data sums already collected. However, if you compute the values in reverse order:

cma[7] = clovol[7] / volume[7]
cma[6, 7] = (clovol[7] + clovol[6]) / (volumn[7] + volume[6])
cma[5, 6, 7] = (clovol[7] + clovol[6] + clovol[5]) / (volumn[7] + volume[6] + volume[5])

You can see that we can compute the next sums using the sum we just calculated.
cma[N] = (clovol_sum(N+1) + clovol[N]) / (volume_sum(N + 1) + volume[N])

This can be written in Python as:
price = [1, 3, 5, 7, 11, 13, 17]
volume = [1, 1, 2, 3, 5, 8, 13]

def cma_calc_2(price, volume):
    clovol_sum = 0
    volume_sum = 0
    cma = []
    for p, v in zip(price[::-1], volume[::-1]):  # Loop backward through the price and volume data
        clovol_sum += p * v
        volume_sum += v
        cma.append(clovol_sum / volume_sum)
    # reverse order of the cma back to the original data
    return cma[::-1]

print(cma_calc_2(price, volume))
Output:
[12.575757575757576, 12.9375, 13.258064516129032, 13.827586206896552, 14.615384615384615, 15.476190476190476, 17.0]
The results are the same, but this code only requires 7 calculations to compute 7 cma values. 1000 points takes 1000 calculations, a 98% reduction over the loop in loop solution.

I would use numpy to do the calculations in C because python loops are slow and numpy has functions for computing cumulative sums and can supports element-wise array multiplication.
import numpy as np

price = [1, 3, 5, 7, 11, 13, 17]
volume = [1, 1, 2, 3, 5, 8, 13]

def cma_calc_3(price, volume):
    price = np.flip(np.array(price))
    volume = np.flip(np.array(volume))
    clovol = price * volume
    clovol_sum = np.cumsum(clovol)
    volume_sum = np.cumsum(volume)
    cma = clovol_sum / volume_sum
    return np.flip(cma)

print(cma_calc_3(price, volume))
Output:
[12.57575758 12.9375 13.25806452 13.82758621 14.61538462 15.47619048 17. ]
I did some timing of all three algorithms. For 1000 points of data:

1000 points
loop in loop time = 16.6 seconds
backward loop time = 0.006 seconds
numpy time = 0.0009 seconds

I started running a test for 10,000 points. After 10 minutes I stopped the test. A rough calculation extrapolating 16.6 seconds for 1000 points returned a time of 1.8 days to calculate 10,000 points.

10,000 points
loop in loop time = 1.8 days (estimated)
backward loop time = 0.017 seconds
numpy time = 0.0019 seconds
Reply
#12
If you want to stop calculating when you match a specific value (probably never going to happen unless you round), you can write the calculation as a generator.
price = [1, 3, 5, 7, 11, 13, 17]
volume = [1, 1, 2, 3, 5, 8, 13]


def cma_generator(price, volume):
    clovol_sum = 0
    volume_sum = 0
    for i in range(len(price) - 1, -1, -1):  # Loop throuh data in reverse order.
        v = volume[i]
        clovol_sum += price[i] * v
        volume_sum += v
        yield clovol_sum / volume_sum


for cma in cma_generator(price, volume):
    print(cma)
Output:
17.0 15.476190476190476 14.615384615384615 13.827586206896552 13.258064516129032 12.9375 12.575757575757576
The cma values are returned in the order they are calculated. The reverse order of the price and volume data
Reply
#13
Quote:A rough calculation extrapolating 16.6 seconds for 1000 points returned a time of 1.8 days to calculate 10,000 points.
Think

numpy.net is the C# equivalent. It's a good solution, I'll take that into account. Now I'm scratching my head how to implement this in ninjascript.

But i think the equivalent would be System.Numerics and LinQ.

using System.Linq;  // For LINQ operations

public class CMA_Generator : Indicator
{
    protected override void OnBarUpdate()
    {
        // Ensure there is enough data (we must have at least the number of bars required)
        if (CurrentBar >= 1)
        {
            // Get the close price of the CurrentBar (the latest completed bar)
            double currentBarClosePrice = Close[0]; // Close of the current bar (the last bar)

            // Arrays to store close prices and volumes for bars
            double[] closePrices = new double[CurrentBar];
            double[] volumes = new double[CurrentBar];

            // Fill the arrays with close prices and volumes for each bar up to the CurrentBar
            for (int i = 0; i < CurrentBar; i++)
            {
                closePrices[i] = Bars.GetClose(i);   // Get close price for the bar at index i
                volumes[i] = Bars.GetVolume(i);      // Get volume for the bar at index i
            }

            // Efficient cumulative sum and weighted sum using LINQ
            double clovolSum = closePrices.Zip(volumes, (price, volume) => price * volume).Sum(); // Weighted sum of close prices and volumes
            double volumeSum = volumes.Sum(); // Total volume

            // Calculate the CMA (Cumulative Moving Average)
            double cma = clovolSum / volumeSum;

            // Calculate the absolute difference between CMA and the current bar's close price
            double difference = Math.Abs(cma - currentBarClosePrice);

            // Print the calculated CMA value for the current bar but not breaking at the right place
            Print("CMA: " + cma);

            // Break the loop if the CMA is closest to the CurrentBar close price but still not breaking at the right place
            if (difference < 0.001)
            {
                Print("Breaking as CMA is equal to CurrentBar Close price.");
            }
        }
    }
}
Thanks
Reply
#14
Oops! That was 100,000 points, not 10,000. 10,000 points would take about 27 minutes. 100,000 points about 44 hours. The backward loop time for 100,000 points is 0.014 seconds. The numpy time is 0.010 seconds.
Reply
#15
Quote:In a cumulative average (CA), the data arrive in an ordered datum stream, and the user would like to get the average of all of the data up until the current datum.

I think what we need here is some clarification of the terminology used.

Maths was never my forté, but I found this:

The Cumulative Average of any stream of data x is:

Quote:n * CAn = x1 + x2 + ... + xn

So, using brute force, save all the data elements x, add them up, then divide by n to get:

Quote:CAn

I presume you have sets of data.

However, the first "Cumulative Average" for the first bar, or any bar you wish to start at, is

Quote:(high price + low price) / 2

for any bar, divided by 1, because it is the first element you consider.

Price and sales volume are not directly related. Perhaps, on a notion of wholesale versus retail, large transactions will enjoy a lower price, but, the price quoted is just that and not necessarily dependent upon the sales volume of a transaction, so I am not sure why that is under consideration, except if you only have information about the total price and volume of each sale.

For any given CAn:

Quote:CAn+1 = (CAn + (xn+1 - CAn ) / (n + 1))

You therefore only need to know the starting point high and low prices for any given bar or data element. That is your first "Cumulative Average" at n = 1. Thereafter, add each data element xn as it comes and divide by n+1 to find your next CAn+1

Your BARs are vertical price ranges in a small time range which may be regarded as a unit. The lower end of the green bars is the lowest price, the higher end of the green bar is the highest price at that time.

There are some calculation steps in between, but the result for the CAn+1 for any n is

Quote:CAn+1 = (CAn + ((low price + high price / 2)n+1 - CAn )) / (n + 1))

If you supply some "bar data", which should be tuples of (high price, low price) I don't think it will be hard to calculate the CA for any given set of bars.

There is also an integral for Continuous Moving Average
Reply
#16
I'm already able to program the CMA. That's not the point. The problem is finding the number of bars needed to have a CMA whose final value is equal to the current price. The idea is to avoid using a nested loop to reduce the amount of data processed. A loop that allows a countdown, for example, 300, 299, 298 to 0 without a nested loop.

 for (int i = 300, 299, 298 ...; i > CurrentBar; i--)
Reply
#17
(Mar-30-2025, 11:16 AM)Frankd Wrote: I'm already able to program the CMA. That's not the point. The problem is finding the number of bars needed to have a CMA whose final value is equal to the current price. The idea is to avoid using a nested loop to reduce the amount of data processed. A loop that allows a countdown, for example, 300, 299, 298 to 0 without a nested loop.

 for (int i = 300, 299, 298 ...; i > CurrentBar; i--)
You have a solution that does this now, correct? Changing the direction used when computing successive cma's lets you reuse the previously calculated cumulative sums and eliminates the need for two loops.

This:
def cma_calc_2(price, volume):
    clovol_sum = 0
    volume_sum = 0
    cma = []
    for p, v in zip(price[::-1], volume[::-1]):  # Loop backward through the price and volume data
        clovol_sum += p * v
        volume_sum += v
        cma.append(clovol_sum / volume_sum)
    # reverse order of the cma back to the original data
    return cma[::-1]


cma = cma_calc_2(price, volume)
Instead of this:
def cma_calc(price, volume, start=0):
    total_clovol = 0
    total_volume = 0
    for p, v in zip(price[start:], volume[start:]):
        clovol = p * v
        total_clovol += clovol
        total_volume += v
    return total_clovol / total_volume
 
 
cma = []
for start in range(len(price)):
    cma.append(cma_calc(price, volume, start))
Reply
#18
The solution works in Python. I'm just having trouble getting it to work in NinjaScript.
Reply
#19
This version takes out all the nice pythonic stuff and uses fixed sized arrays and indexing. Should be easy to translate to C# and is still blindingly fast compared to the inner loop method.
import numpy as np
from time import time

price = np.random.random(1000000)
volume = np.random.randint(1, 100, size=price.shape)


def cma_calc_2(price, volume):
    clovol_sum = 0
    volume_sum = 0
    cma = np.zeros(len(price), dtype=np.float64)
    for index in range(len(price)-1, -1, -1):
        v = volume[index]
        clovol_sum += price[index] * v
        volume_sum += v
        cma[index] = clovol_sum / volume_sum
    return cma


start = time()
cma_calc_2(price, volume)
print(time() - start)
It is still blindingly fast when compared to the inner loop method.
Reply
#20
Not only does it need to be translated into C#, but it also needs to be translated into Ninjascript. This should have been a simple task, but somewhere along the line my second loop needs to include the if statement.
According to Ninjatrader forum you can write Python functions using NumPy and call them from NinjaScript via Python for .NET, which allows interoperability between C# and Python. Dodgy


namespace NinjaTrader.NinjaScript.Indicators
{
	public class MultipleCMACalculation : Indicator
	{
		private double clovolSum = 0;
   		 private double volumeSum = 0;
		
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"Enter the description for your new custom Indicator here.";
				Name										= "MultipleCMACalculation";
				Calculate									= Calculate.OnBarClose;
				IsOverlay									= true;
				DisplayInDataBox							= true;
				DrawOnPricePanel							= true;
				DrawHorizontalGridLines						= true;
				DrawVerticalGridLines						= true;
				PaintPriceMarkers							= true;
				ScaleJustification							= NinjaTrader.Gui.Chart.ScaleJustification.Right;
				//Disable this property if your indicator requires custom values that cumulate with each new market data event. 
				//See Help Guide for additional information.
				IsSuspendedWhileInactive					= true;
				AddPlot(Brushes.Orange, "MultipleCMACalculation");
			}
			
    else if (State == State.DataLoaded)
    {
       // cwma = new Series<double>(this);
    }
		}

		protected override void OnBarUpdate()
		{
		 // Ensure there is enough data (we must have at least the number of bars required)
		        if (CurrentBar >= 1)
		{
		    // Get the close price of the CurrentBar (the latest completed bar)
		    double currentBarClosePrice = Close[0]; // Close of the current bar (the last bar)
		
		    // List to store close prices and volumes for bars
		    List<double> closePrices = new List<double>();
		    List<double> volumes = new List<double>();
		
		    // Fill the lists with close prices and volumes for each bar up to the CurrentBar
		    for (int i = CurrentBar - 1; i >= 0; i--)
		    {
		        closePrices.Add(Close[i]); // Get close price for the bar at index i
		        volumes.Add(Volume[i]);    // Get volume for the bar at index i
		    }
		
		    // Reverse the closePrices list
		    closePrices.Reverse();
		    volumes.Reverse();  // Optionally, reverse volumes if you need them in the same order as close prices
		
		    // Initialize cumulative sums
		    double clovolSum = 0;
		    double volumeSum = 0;
		
		    // Calculate weighted sum and total volume
		    for (int i = 0; i < closePrices.Count; i++)
		    {
		        clovolSum += closePrices[i] * volumes[i]; // Weighted sum of close prices and volumes
		        volumeSum += volumes[i]; // Total volume
		    }
		
		    // Calculate the Cumulative Moving Average (CMA)
		    double cma = clovolSum / volumeSum;
		
		    // Calculate the absolute difference between CMA and the current bar's close price
		    double difference = Math.Abs(cma - currentBarClosePrice);
		
		    // Print the calculated CMA value for the current bar
		    Print("CMA: " + cma);
		    Print("Current Bar Close Price: " + currentBarClosePrice);
		
		    // Output the difference
		    Print("Difference: " + difference);
		
		    // Set the output value for the strategy or indicator
		    Value[0] = cma;
		
		    // Allow for a small tolerance to account for floating-point precision
		    if (difference < 0.0001)
		    {
		        Print("Breaking as CMA is close to CurrentBar Close price.");
		    }
		    else
		    {
		        Print("CMA is not equal to CurrentBar Close price.");
		    }
		}

		}
	}
}
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How to create a variable only for use inside the scope of a while loop? Radical 10 8,247 Nov-07-2023, 09:49 AM
Last Post: buran
  Create Dynamic For Loop quest 3 6,075 Apr-26-2021, 02:03 PM
Last Post: ibreeden
  Python Matplotlib: How do I save (in pdf) all the graphs I create in a loop? JaneTan 1 11,194 Feb-28-2021, 06:20 PM
Last Post: Larz60+
  how to create pythonic codes including for loop and if statement? aupres 1 2,422 Jan-02-2021, 06:10 AM
Last Post: Gribouillis
  create loop of subplot plotly dash without hardcode tonycat 0 4,592 Sep-23-2020, 08:40 AM
Last Post: tonycat
  Create tempfile that use in loop for insert into DB Firsttimepython 2 3,043 May-29-2020, 04:15 PM
Last Post: Firsttimepython
  Create, assign and print variables in loop steven_tr 10 6,466 May-28-2020, 04:26 PM
Last Post: ndc85430
  how to create subplots in for loop? python_newbie09 1 6,848 Sep-25-2019, 02:29 PM
Last Post: stullis
  I can't figure out how to create variables in a while loop MrCag 1 3,075 May-08-2018, 08:56 PM
Last Post: wavic

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020