> For the complete documentation index, see [llms.txt](https://quantinfra.gitbook.io/quantinfra-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://quantinfra.gitbook.io/quantinfra-docs/strategies/implementing-custom-indicators.md).

# Implementing custom indicators

All indicators must implement the base AbstractIndicator class.

Let us consider a simple moving average as an example:

```c#
public class SimpleMovingAverage : AbstractIndicator
{
    int _period;
    AbstractIndicator _source;

    public SimpleMovingAverage(AbstractIndicator source = null, int period = 9)
    {
        _source = source ?? new Close();
        _period = period;
        RegisterIndicator(_source, _period + 1);

        Id = $"SimpleMovingAverage:{_period}({_source.Id})";
    }


    protected override double? Calculate(IBarStorage bars, double? price = null)
    {
        var previousValue = bars.Count > 1 ? GetValue(bars[1]) : null;
        if (!previousValue.HasValue) return bars.Take(_period).Select(_source.GetValue).Average();

        // this gives a performance boost
        return previousValue + _source.GetValue(bars.CurrentBar) / _period - _source.GetValue(bars[_period]) / _period;
    }
}
```

## Constructor

While the constructor's signature is arbitrary, it's recommended that it take the source indicator and all parameters, with some default values defined.

Since most indicators can be nested, it's recommended that your indicator takes another source indicator as an input. The SMA from the example above may be used as follows:

```c#
var _closeSma = new SimpleMovingAverage(); // Averages bar's close

var momentum = new Momentum();
var momentumSma = new SimpleMovingAverage(momentum); // Averages momentum
```

The custom indicator must call RegisterIndicator for all indicators it uses (received as a parameter or created internally).

The *lookback* parameter of the RegisterIndicator method specifies how much history the current indicator requires to calculate successfully. It affects how much history will be loaded when hydrating a bar storage. In the example above, to calculate an average over *(period)*, we need *(period)* candles to be loaded.

ID must be set to a value that is unique to every instance of the indicator. The recommended format is "\<indicator name>\<list of parameters separated by colon>(\<list of Ids of used indicators separated by comma>)", e.g.:

```csharp
Id = $"SimpleMovingAverage:{_period}({_source.Id})";
// or
Id = $"CMF:{_period}({_high.Id},{_low.Id},{_close.Id})";
```

IsSeparateWindow flag defines if the indicator must be shown in a separate pane in the debug view.

## Calculation

The Calculate() method must be implemented. It must return the indicator value, or null if the indicator cannot be calculated. Keep in mind that the method gets called from the very first candle during the hydration, so that *bars.Count* must be used to determine if there are enough bars in the storage to calculate the indicator.

You can use the values of underlying indicators during the calculation:

```c#
return 3 * _ema.GetValue(bars[0]) - 3 * _doubleEMA.GetValue(bars[0]) + _tripleEMA.GetValue(bars[0]);
```

It is recommended that indicators use recursion (its previous values) as much as possible, since this significantly decreases calculation time (especially during optimization):

```c#
// Calculate Simple Moving Average

var previousValue = bars.Count > 1 ? GetValue(bars[1]) : null;
if (!previousValue.HasValue)
{
    // This is not effective, so use averaging only during the hydration
    return bars.Take(_period).Select(_source.GetValue).Average();
}

// MovingAverage_0 = (x_0 + x_1 + ... + x_N) / N
// MovingAverage_1 = (      x_1 + ... + x_N + x_N+1) / N
// MovingAverage_0 = x_0 / N + MovingAverage_1 - x_N+1 / N
return previousValue + _source.GetValue(bars.CurrentBar) / _period - _source.GetValue(bars[_period]) / _period;
```

## Struct indicators

Some indicators may require persisting internal state. They may inherit from AbstractIndicator\<T> base class.

The difference is another method CalculateAndPersistData() must be implemented. It returns the double? indicator value if available, and a struct T with the internal state. See the Parabolic indicator for an example.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://quantinfra.gitbook.io/quantinfra-docs/strategies/implementing-custom-indicators.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
