> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ninjatrader.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ninjatrader.com/_mcp/server.

# Entering Calculation Logic

## 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). This is the main method called for indicator calculation, and we will calculate the CCI value and set the conditions used to draw the CCI plot within this method.

The **OnStateChange()** method is called once before any bar data is loaded, and is used to configure the indicator (among other things).

## Initializing the Indicator

The code below is automatically generated by the wizard and added to the **OnStateChange()** method, within **State.SetDefaults**. It configures the indicator for one plot and five lines, and sets the parameters entered in the wizard:

```csharp
AddPlot(Brushes.Orange, "MyPlot");
AddLine(Brushes.DimGray, 200, "Level 2");
AddLine(Brushes.DimGray, 100, "Level 1");
AddLine(Brushes.DimGray, 0, "Zero Line");
AddLine(Brushes.DimGray, -100, "Level -1");
AddLine(Brushes.DimGray, -200, "Level -2");
```

To change the visual properties of the Zero Line, replace the fourth line in the code above with the line below. This will change the color to black and the line style to "dash:"

```csharp
AddLine(new Stroke(Brushes.Black, DashStyleHelper.Dash, 2), 0, "Zero Line");
```

The code above uses an alternative method overload (an alternative set of arguments passed in to the **AddLine()** method), in order to pass in a **Stroke** object rather than a **Brush**. With a Stroke, not only can we still specify a Brush, but we have additional options to change the dash style (via **DashStyleHelper**) and the line width. After this change, your configured lines and plots should look like this:

```csharp
AddPlot(Brushes.Orange, "MyCCI_Plot");
AddLine(Brushes.DimGray, 200, "Level 2");
AddLine(Brushes.DimGray, 100, "Level 1");
AddLine(new Stroke(Brushes.Black, DashStyleHelper.Dash, 2), 0, "Zero Line");
AddLine(Brushes.DimGray, -100, "Level -1");
AddLine(Brushes.DimGray, -200, "Level -2");
```

## Adding Core Indicator Logic

Since this tutorial is meant to cover custom drawing and manually changing properties within an indicator, we will not go too in-depth into the core calculation logic for this custom CCI. Instead, we will copy and paste the core calculation logic from the **@CCI** indicator already built-in to NinjaTrader.

The **@CCI** indicator uses an **SMA** object in its calculations. To add this, copy the line below from **@CCI** into your custom CCI, directly below the class declaration:

```csharp
private SMA sma;
```

Next, copy the following initialization for the **SMA** object into the **OnStateChange()** method, within **State.DataLoaded**:

```csharp

sma = SMA(Typical, Period);
```

Next, copy the core calculation logic from **@CCI** into the **OnBarUpdate()** method of your custom indicator:

```csharp
if (CurrentBar == 0)
   Value[0] = 0;
else
{
   double mean = 0;
   double sma0 = sma[0];

   for (int idx = Math.Min(CurrentBar, Period - 1); idx >= 0; idx--)
       mean += Math.Abs(Typical[idx] - sma0);

   Value[0] = (Typical[0] - sma0) / (mean.ApproxCompare(0) == 0 ? 1 : (0.015 * (mean / Math.Min(Period, CurrentBar + 1))));
}
```

The code for your **MyCCI** class should now look as follows (in addition to the using statements and class declaration):

```csharp

public class MyCCI : Indicator
{
   private SMA sma;

   protected override void OnStateChange()
   {
       if (State == State.SetDefaults)
       {
           Description = @"NinjaScript Custom Drawing Indicator Tutorial";
           Name = "MyCCI";
           Calculate = Calculate.OnBarClose;
           IsOverlay = false;
           DisplayInDataBox = true;
           DrawOnPricePanel = true;
           DrawHorizontalGridLines = true;
           DrawVerticalGridLines = true;
           PaintPriceMarkers = true;
           ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
           IsSuspendedWhileInactive = true;
           Period = 14;
           AddPlot(Brushes.Orange, "MyPlot");
           AddLine(Brushes.DimGray, 200, "Level 2");
           AddLine(Brushes.DimGray, 100, "Level 1");
           AddLine(new Stroke(Brushes.Black, DashStyleHelper.Dash, 2), 0, "Zero Line");
           AddLine(Brushes.DimGray, -100, "Level -1");
           AddLine(Brushes.DimGray, -200, "Level -2");
       }
       else if (State == State.DataLoaded)
       {
           sma = SMA(Typical, Period);
       }
   }

   protected override void OnBarUpdate()
   {
       if (CurrentBar == 0)
           Value[0] = 0;
       else
       {
           double mean = 0;
           double sma0 = sma[0];

           for (int idx = Math.Min(CurrentBar, Period - 1); idx >= 0; idx--)
               mean += Math.Abs(Typical[idx] - sma0);

           Value[0] = (Typical[0] - sma0) / (mean.ApproxCompare(0) == 0 ? 1 : (0.015 * (mean / Math.Min(Period, CurrentBar + 1))));
       }
   }
}
```

## Custom Drawing

Add the following code into the **OnBarUpdate()** method, directly beneath the core calculation logic:

```csharp

// if the plot value is greater than 100, paint the plot green at that bar index
if (Value[0] > 100)
   PlotBrushes[0][0] = Brushes.Green;

// if the plot value is less than -100, paint the plot red at that bar index
if (Value[0] < -100)
   PlotBrushes[0][0] = Brushes.Red;

// if the plot value is between 100 and -100, paint the plot orange at that bar index
if (Value[0] >= -100 && Value[0] <= 100)
   PlotBrushes[0][0] = Brushes.Orange;
```

This will conditionally change the color of the CCI plot (referenced by **Values\[0]**) based on its value. By using **PlotBrushes\[0]\[0]**, we are specifying that we wish to change the color of the first plot in the collection at a specific bar index (the current bar index each time the condition is triggered), and we wish for the plot to remain that color at that index, even if the plot value changes in the future. If instead we wished to change the entire plot color, we could use **Plots\[0].Brush**.

**PlotBrushes** holds a collection of brushes used for the various plots in the indicator. In addition to this, there are several other collections that serve similar purposes, which can be used in the same way. Some examples of these collections are below:

| Collection                                                                                    | Description                                                                     |
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [BackBrushes](/developer/desktop-sdk/references/common/drawing/backbrushes)                   | A collection of Brushes used for chart background color at specific bar indexes |
| [BarBrushes](/developer/desktop-sdk/references/common/drawing/barbrushes)                     | A collection of Brushes used to paint bars at specific indexes                  |
| [CandleOutlineBrushes](/developer/desktop-sdk/references/common/drawing/candleoutlinebrushes) | A collection of Brushes used to paint candle outlines at specific indexes       |

Now that everything is in place, your class code should look as below. You are now ready to [compile the indicator](/developer/desktop-sdk/guides/educational-resources/developing-indicators/advanced-custom-drawing/compiling6) and configure it on a chart.

```csharp

public class MyCCI : Indicator
{
   private SMA sma;

   protected override void OnStateChange()
   {
       if (State == State.SetDefaults)
       {
           Description = @"NinjaScript Custom Drawing Indicator Tutorial";
           Name = "MyCCI";
           Calculate = Calculate.OnBarClose;
           IsOverlay = false;
           DisplayInDataBox = true;
           DrawOnPricePanel = true;
           DrawHorizontalGridLines = true;
           DrawVerticalGridLines = true;
           PaintPriceMarkers = true;
           ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
           IsSuspendedWhileInactive = true;
           Period = 14;
           AddPlot(Brushes.Orange, "MyPlot");
           AddLine(Brushes.DimGray, 200, "Level 2");
           AddLine(Brushes.DimGray, 100, "Level 1");
           AddLine(new Stroke(Brushes.Black, DashStyleHelper.Dash, 2), 0, "Zero Line");
           AddLine(Brushes.DimGray, -100, "Level -1");
           AddLine(Brushes.DimGray, -200, "Level -2");
       }
       else if (State == State.DataLoaded)
       {
           sma = SMA(Typical, Period);
       }
   }

   protected override void OnBarUpdate()
   {
       if (CurrentBar == 0)
           Value[0] = 0;
       else
       {
           double mean = 0;
           double sma0 = sma[0];

           for (int idx = Math.Min(CurrentBar, Period - 1); idx >= 0; idx--)
               mean += Math.Abs(Typical[idx] - sma0);

           Value[0] = (Typical[0] - sma0) / (mean.ApproxCompare(0) == 0 ? 1 : (0.015 * (mean / Math.Min(Period, CurrentBar + 1))));
       }

       if (Value[0] > 100)
           PlotBrushes[0][0] = Brushes.Green;

       if (Value[0] < -100)
           PlotBrushes[0][0] = Brushes.Red;

       if (Value[0] >= -100 && Value[0] <= 100)
           PlotBrushes[0][0] = Brushes.Orange;
   }

   #region Properties
   [NinjaScriptProperty]
   [Range(1, int.MaxValue)]
   [Display(Name="Period", Description="The CCI Period", Order=1, GroupName="Parameters")]
   public int Period
   { get; set; }

   [Browsable(false)]
   [XmlIgnore]
   public Series<double> MyPlot
   {
       get { return Values[0]; }
   }
   #endregion
}
```