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
| Phase | What HyperionX does | Put this work here |
|---|---|---|
| Construction | Creates the script, then enters State.SetDefaults. | Keep constructors empty or limited to field initialization. |
SetDefaults | Calls OnStateChanged() and the legacy OnStateChange() alias. | Name, version, safe parameter defaults, calculation mode, and plot definitions. |
Configured | Calls BeforeConfigured(), the state callbacks, then AfterConfigured(). | Runtime series, child indicators, plot data sources, and AddDataSeries(...). |
| Data calculation | Sets Historical, Playback, or RealTime and invokes update callbacks. | Read series, calculate outputs, and make strategy decisions. |
| Transition | Can 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 removal | Uses the owning host's cleanup path. | Strategies: OnStrategyStopping(). Add-ons: OnUnload(). |
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
| Callback | Use |
|---|---|
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.OnBarCloseis the default.CalculateMode.OnEachTickrecalculates for each eligible tick.CalculateMode.OnPriceChangerecalculates 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 inOnUnload(). - An indicator should prefer script-owned series, plots, drawings, and host-managed resources. If it overrides
Clear()for a documented need, it must callbase.Clear(). Ctx.MarketData.SubscribeDepth(...)creates an explicit subscription. Pair it withUnsubscribeDepth()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
BarsInProgressbranch. - 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.