Entering Calculation Logic

View as Markdown

Entering Calculation Logic

The OnBarUpdate() method is called for each incoming tick, or on the close of a bar (if enabled) when performing real-time calculations, and is called on each bar of a Bars object when re-calculating the indicator (For example, an indicator would be re-calculated when adding it to an existing chart that has existing price data displayed). Therefore, this is the main method called for indicator calculation and we will use this method to enter the script that will calculate a simple moving average.

Are there enough bars?

Enter the following code into the OnBarUpdate() method in the NinjaScript Editor:

1// Do not calculate if we don't have enough bars
2if (CurrentBar < Period) return;

To calculate a 20 period moving average you will need a minimum of 20 bars of data. The first statement in our OnBarUpdate() method checks to see if there are enough bars of data to perform the moving average calculation. CurrentBar returns the index number of the current bar and this is checked against the user-defined parameter Period. If the current bar number is less than the user-defined period we “return” which skips calculating the moving average.

Getting a sum of closing prices

Enter the following code into the OnBarUpdate() method and below the code snippet you entered above:

1// Get a sum of prices over the specified period
2double sum = 0;
3for (int barsAgo = 0; barsAgo < Period; barsAgo++)
4{
5 sum = sum + Input[barsAgo];
6}

First we must declare a variable that will store our sum total.

1double sum = 0;

The variable sum whose value is of type double will serve as temporary storage.

1for (int barsAgo = 0; barsAgo < Period; barsAgo++)
2{
3 sum = sum + Input[barsAgo];
4}

Next we must calculate the sum. We use a standard “for” loop to skip through prices and add them to the sum variable. Although the command that represents the loop may look intimidating, it’s really quite simple. Let’s look at it in English…

What the loop is saying is:

  1. the number of bars ago is now zero
  2. as long as the number of bars ago is less than the moving average period, then go to line 3 otherwise this loop is finished
  3. get the price Input[number of bars ago] and add it to the running sum total
  4. add one to the number of bars ago (if number of bars ago was zero it will now be one)
  5. go to line 2

You can find more information on how loops work here. Once the loop has finished, it will have calculated the total sum of closing prices for the period of our moving average.

  • We use the value of Input[barsAgo] to get a price to use for our calculation. We could have substituted Close[barsAgo] to use closing prices or High[barsAgo] to use high prices. The reason we use Input[barsAgo] is since this allows flexibility for what the indicator is calculated based off of. Remember users have the option to select a price type (High, Open, Close etc…) from the Indicator Dialog window.

The final calculation

Enter the following code into the OnBarUpdate() method and below the code snippet you entered above:

1// Calculate and set the average value to the 'MyPlot' property
2MyPlot[0] = sum / Period;

We can now calculate the final moving average value and assign its value to the property that represents the plot data. We have just finished coding our simple moving average. The class code in your editor should look identical to the image below. You are now ready to compile the indicator.

1public class MySMA : Indicator
2{
3 protected override void OnStateChange()
4 {
5 if (State == State.SetDefaults)
6 {
7 Description = @"Simple Moving Average";
8 Name = "MySMA";
9 Calculate = Calculate.OnBarClose;
10 IsOverlay = true;
11 DisplayInDataBox = true;
12 DrawOnPricePanel = true;
13 DrawHorizontalGridLines = true;
14 DrawVerticalGridLines = true;
15 PaintPriceMarkers = true;
16 ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
17 //Disable this property if your indicator requires custom values that cumulate with each new market data event.
18 //See Help Guide for additional information.
19 IsSuspendedWhileInactive = true;
20 Period = 20;
21 AddPlot(Brushes.Orange, "MyPlot");
22 }
23 else if (State == State.Configure)
24 {
25 }
26 }
27
28 protected override void OnBarUpdate()
29 {
30 // Do not calculate if we don't have enough bars
31 if (CurrentBar < Period) return;
32
33 // Get a sum of prices over the specified period
34 double sum = 0;
35 for (int barsAgo = 0; barsAgo < Period; barsAgo++)
36 {
37 sum = sum + Input[barsAgo];
38 }
39
40 // Calculate and set the average value to the 'MyPlot' property
41 MyPlot[0] = sum / Period;
42 }
43
44 #region Properties
45 [NinjaScriptProperty]
46 [Range(1, int.MaxValue)]
47 [Display(Name="Period", Description="Number of Periods", Order=1, GroupName="Parameters")]
48 public int Period
49 { get; set; }
50
51 [Browsable(false)]
52 [XmlIgnore]
53 public Series<double> MyPlot
54 {
55 get { return Values[0]; }
56 }
57 #endregion
58}

Alternate Implementation

In this tutorial we are using a “for” loop to iterate through a collection of prices and accumulate a sum value. We chose this approach to demonstrate the use of a loop. A simple moving average can actually be expressed in a more efficient manner using the built-in SUM indicator as shown below.

1// Do not calculate if we don't have enough bars
2if (CurrentBar < Period) return;
3
4// Calculate and set the 'average' value to the 'MyPlot' property
5MyPlot[0] = SUM(Input, Period)[0] / Period;