Multi-timeframe
Multi-timeframe
Subscribe to extra data series — a higher timeframe, a different instrument — and read them alongside your primary, knowing which series fired each callback.
On this page
Adding a series
An indicator starts with one series — the chart's, at index 0. Call
ctx.AddDataSeries(BarSpecification spec) to subscribe to another. It returns a
Task<int> that completes with the new series index. Subscribing a spec you already
have is idempotent — the existing index comes back, no second series is allocated.
Build the secondary spec from the primary's instrument (read it off ctx.Bars(0).Spec) so
you stay on the same symbol, then choose any bar spec the provider supports — a
TimeBarSpec, RangeBarSpec, VolumeBarSpec and so on (all in
TradeStrike.Pipeline.Bars).
subscribing a higher timeframeusing TradeStrike.Pipeline.Bars;
using TradeStrike.Pipeline.Indicators;
private int _htf; // index of the 60-minute series
public override void OnInit(IIndicatorContext ctx)
{
base.OnInit(ctx);
// Same instrument as the primary, but a 60-minute period.
string symbol = ctx.Bars(0).Spec.InstrumentId;
var spec = new TimeBarSpec(symbol, System.TimeSpan.FromMinutes(60));
// Blocking on .Result is fine inside OnInit (a historical-phase hook).
_htf = ctx.AddDataSeries(spec).Result;
}
Knowing which series fired
In a callback, ctx.BarsInProgress is the index of the series whose bar just closed. Branch
on it, and read any series with ctx.Bars(index). ctx.CurrentBars[index] gives
the last-bar index of any local series independently (mirrors NinjaScript's CurrentBars
array) — useful for warmup guards on a series that isn't the one that just fired.
routing per seriespublic override void OnBarUpdate(IIndicatorContext ctx)
{
if (ctx.BarsInProgress == 0)
{
// primary series bar closed
double primaryClose = ctx.Bars(0)[0].Close;
// read the most recent CLOSED higher-timeframe bar, if we have enough
if (ctx.CurrentBars[_htf] >= 0)
{
double htfClose = ctx.Bars(_htf)[0].Close;
_aligned.Append(primaryClose - htfClose);
}
}
else if (ctx.BarsInProgress == _htf)
{
// a 60-minute bar just closed — recompute the higher-timeframe trend
}
}
Async backfill & OnDataSeriesReady
A series added via AddDataSeries backfills asynchronously. The runtime calls
OnDataSeriesReady(int seriesIndex, ctx) once that series has finished backfilling and
joined the live stream — after which reads on that index return real bars. Override it when you
need to do a one-time pass over the secondary's history; for most indicators, simply reading the index
in OnBarUpdate (guarded by ctx.CurrentBars[index] >= 0) is enough.
To drop a series later, call ctx.RemoveDataSeries(index). Index 0 (the primary) cannot be
removed.
No look-ahead
Bars(_htf)[0] until it finishes, so an MTF indicator
cannot accidentally peek at the future. That makes the same indicator safe in strategies and
backtests.