Using indicators

Strategies

Using indicators

Pull any SDK or built-in indicator into your strategy with a single factory call, and read its value bar by bar.

Using indicators

A strategy does not compute moving averages or ATR by hand — it reuses the same indicator types you build elsewhere in the SDK. Declare one with DeclareIndicator(factory) inside OnInitialize, passing a lambda that constructs and configures the indicator (any IndicatorBase subclass). The framework feeds the instance the same bars your strategy sees, so at runtime you just read its current value.

It takes a factory rather than a ready-made instance because of timing: the factory runs after parameter overrides are applied, so an indicator sized by a parameter picks up that run's value — which is what makes a declared indicator correct under an optimizer sweep, where each pass reconstructs it from that pass's overrides. A factory with a literal period works identically.

C#private StrategyIndicator<Sma> _sma = null!;
private StrategyIndicator<Atr> _atr = null!;

protected override void OnInitialize()
{
    _sma = DeclareIndicator(() => new Sma { Period = _fastPeriod.Value });  // parameter-sized
    _atr = DeclareIndicator(() => new Atr { Period = 14 });
}

protected override void OnBar()
{
    double sma = _sma.Instance.Value;       // Sma exposes a convenience .Value
    double atr = _atr.Instance.Output[0];   // Atr publishes an ISeries<double> Output; [0] = latest
    // ...
}

The handle & readiness

DeclareIndicator returns a typed StrategyIndicator<T> handle — hold it in a field. You cannot be handed the indicator instance at declaration time because it does not exist yet (the factory has not run). By the time OnStart runs, the runner has built and backfilled every declared indicator over the run's pre-run history, so its values are immediately usable. Read .Instance from OnStart onward; reading it earlier (inside OnInitialize, or before the run started) throws rather than returning a half-built indicator. IsReady is true once the instance is bound.

How you read the value depends on the indicator. There is no single universal accessor. Some indicators expose a convenience .Value (e.g. Sma.Value); others publish their result as an ISeries<double> Output read with Output[0] for the latest (e.g. Atr). Multi-plot indicators (MACD, Bollinger) expose one series per line. Check the indicator type for which it offers — many provide both.

Guard your reads during warm-up. IsReady being true means the indicator is built, not that it has enough bars to be meaningful — an SMA over 20 bars is not useful at bar 3. Gate on BarCount before trusting a value.

Parameter-sized indicators are free to optimize. Combine the factory with an optimizable parameter — new Sma { Period = _fastPeriod.Value } — and the optimizer sweeps the indicator's period at no extra cost, since each run reconstructs the indicator from that run's overrides.