Skip to main content

Script Lifecycle and Calculation

Indicators and strategies share the ScriptBase lifecycle, but a state value and a lifecycle callback are not the same promise. Use the callbacks documented below, and do not infer a shutdown event from an enum member alone.

Reliable initialization sequence

PhaseWhat HyperionX doesPut this work here
ConstructionCreates the script, then enters State.SetDefaults.Keep constructors empty or limited to field initialization.
SetDefaultsCalls OnStateChanged() and the legacy OnStateChange() alias.Name, version, safe parameter defaults, calculation mode, and plot definitions.
ConfiguredCalls BeforeConfigured(), the state callbacks, then AfterConfigured().Runtime series, child indicators, plot data sources, and AddDataSeries(...).
Data calculationSets Historical, Playback, or RealTime and invokes update callbacks.Read series, calculate outputs, and make strategy decisions.
TransitionCan notify OnStateChanged() as the host moves between historical and live work.Reconcile environment-specific state; do not submit an entry merely because transition occurred.
Stop or removalUses the owning host's cleanup path.Strategies: OnStrategyStopping(). Add-ons: OnUnload().
Do not wait for Finalized or Terminated

State.Finalized currently detaches the calculation context without setting the public state or notifying OnStateChanged(). The enum also contains Terminated, but the native indicator/strategy host does not establish it as a general cleanup callback. Strategy cleanup belongs in OnStrategyStopping(); add-on cleanup belongs in OnUnload().

State.DataLoaded and State.Calculated exist in the runtime model, but public scripts should not require them for essential initialization or cleanup. Calculated can be assigned by strategy internals without a state callback.

Calculation callbacks

CallbackUse
OnBarUpdate()Main bar-series calculation. Check BarsInProgress for multi-series scripts.
OnTickUpdate(ICandle candle, ICandle tick)Tick delivery made by the calculation engine. Keep it lightweight.
OnMarketData(MarketDataEventArgs)Market-data events supplied by the host.
OnMarketDepth(MarketDepthEventArgs)Depth events supplied by the host.
OnAfterBarUpdate()Work that must follow the main bar update.
OnConnectionStatusChanged(...)Connection transition notification.

The Calculate property controls when the engine performs additional calculation updates:

  • CalculateMode.OnBarClose is the default.
  • CalculateMode.OnEachTick recalculates for each eligible tick.
  • CalculateMode.OnPriceChange recalculates when price changes.

Dedicated tick and market-data callbacks are separate delivery paths. Do not assume OnBarClose disables every tick/depth callback you explicitly subscribed to.

Series indexing

CurrentBar is the number of processed bars. A bars-ago index of 0 means the newest processed value:

if (CurrentBar < Period)
return;

var newestClose = Close[0];
var previousClose = Close[1];

An unavailable series value can read as the type's default rather than throwing. A zero is therefore not proof that data exists. Guard every indexed read.

For a multi-series script:

public override void OnBarUpdate()
{
if (CurrentBars[0] < 50 ||
CurrentBars[1] < 20)
return;

if (BarsInProgress == 0)
{
// Primary-series work.
}
else if (BarsInProgress == 1)
{
// Secondary-series work.
}
}

Add secondary data in State.Configured:

AddDataSeries(DataSeriesType.Minute, 5, "ES");

Secondary updates are scheduled by their close time relative to the primary stream. Code must tolerate unequal history, missing data, and a secondary series that has not produced its first bar.

Resource ownership

Native chart scripts do not expose one universal terminal-state callback.

  • A strategy should stop timers, cancel its own background work, and unsubscribe explicit event/depth subscriptions in OnStrategyStopping().
  • An add-on must reverse successful OnLoad() work in OnUnload().
  • An indicator should prefer script-owned series, plots, drawings, and host-managed resources. If it overrides Clear() for a documented need, it must call base.Clear().
  • Ctx.MarketData.SubscribeDepth(...) creates an explicit subscription. Pair it with UnsubscribeDepth() in the host-specific cleanup path.
  • Never keep chart, account, session, or custom-assembly objects in static fields.

Lifecycle checklist

  • Set safe, deterministic defaults before runtime data exists.
  • Create series and child scripts once in State.Configured.
  • Guard every series and every BarsInProgress branch.
  • Keep bar, tick, depth, and rendering callbacks non-blocking.
  • Do not use a lifecycle transition as a fill or connection guarantee.
  • Give every explicit subscription, task, timer, and disposable an owner and a tested stop path.
  • Recreate the script after compiling to verify initialization and cleanup from a clean instance.

Continue with Script Context, Indicator Patterns, Strategy Patterns, and the ScriptBase reference.