Skip to main content

HyperionXScript Best Practices

Use these rules for every Code Lab review. They apply to code written by people and by AI coding agents.

Use the right lifecycle stage

WorkCorrect place
Name, version, parameter defaults, plot definitionsState.SetDefaults
Runtime series, child indicators, plot data sources, extra data seriesState.Configured
Current-bar calculationsOnBarUpdate()
Tick-specific lightweight logicOnTickUpdate(...) or documented market-data hooks
Custom chart geometryIChartOverlayRenderer.OnRender(...)
Strategy-owned resource cleanupOnStrategyStopping()
Add-on resource acquisitionAddonBase.OnLoad()
Add-on unsubscribe, cancellation, disposalAddonBase.OnUnload()

Do not create a new series, plot, child indicator, timer, control, or service on every bar.

Do not use State.Finalized or State.Terminated as a general native-script cleanup promise. See Script Lifecycle and Calculation.

Guard every indexed read

var requiredBars = System.Math.Max(FastPeriod, SlowPeriod) + 1;
if (CurrentBar < requiredBars)
return;

For multiple series, guard each index:

if (CurrentBars[0] < 50 || CurrentBars[1] < 20)
return;

Then route the callback with BarsInProgress. A primary-series guard does not prove the secondary series is ready.

Keep hot paths small

OnBarUpdate(), tick callbacks, market-depth callbacks, and rendering can run frequently.

Avoid:

  • File or network I/O.
  • Sleep, synchronous waits, or blocking locks.
  • Recalculating all historical candles on every update.
  • Heavy LINQ or repeated allocations in tick/render paths.
  • Creating WPF controls or brushes per update.
  • Logging every tick in normal operation.

Allocate once, retain only the state you need, update incrementally, and constrain rendering loops to visible bars.

Treat time explicitly

Inside an indicator or strategy, DateTime is a bar timestamp series.

System.DateTime barTime = DateTime[0];
System.DateTime localNow = System.DateTime.Now;
System.DateTime utcNow = System.DateTime.UtcNow;

Do not write DateTime.Now when you intend the .NET type; the inherited series name can win name resolution.

Prefer the documented facade

For new indicator and strategy code, use Ctx where it provides the capability:

  • Ctx.Chart for visible range, coordinate conversion, and invalidation.
  • Ctx.MarketData for candles, bid/ask, depth, and bar timing.
  • Ctx.Drawing for script-owned HUD drawings and cleanup.
  • Ctx.Orders for strategy-only managed order actions.
  • Ctx.Account and Ctx.Position for read-only state.
  • Ctx.Log for structured diagnostics.

The facade is narrower than internal platform objects and makes intent easier to review. Use lower-level APIs when the reference explicitly requires them.

Separate signal, execution, and research logic

A maintainable trading system has clear boundaries:

Indicator or signal calculation

Strategy entry/exit decision

Order and execution lifecycle

Money management + commission model

Backtest / validation / optimization evidence

Do not make an indicator place orders. Do not hide broker calls inside a fitness function. Do not select parameters using a cost-free backtest and add fees only afterward.

Track orders as asynchronous state

An order-submission call is not a fill confirmation.

  • Use meaningful and stable signal names.
  • Set managed stops and targets before or with their entry flow.
  • Use OCO groups for paired exits where appropriate.
  • Track accepted, working, filled, canceled, and rejected states in order callbacks.
  • Handle partial fills and quantity changes.
  • Clear stale order references after terminal states.
  • Prevent repeated entry or exit submissions from the same bar condition.

Always validate on playback or simulation with an explicitly selected LocalPaper account before enabling a live account. Playback does not change the selected account, but the current release-candidate guard rejects Broker and Server Paper submissions, changes, and cancellations while Playback is active. Treat that guard as a backstop, not as account selection.

Own and release resources

Any code that subscribes, starts, opens, or allocates must define who stops, closes, unsubscribes, or disposes it.

For add-ons, make OnLoad() transactional: if acquisition fails halfway, roll back partial resources inside OnLoad() before rethrowing. A failed load is not retained and must not rely on a later OnUnload() call.

For chart scripts, remove script-owned drawings and release renderer resources when the documented lifecycle requires it. Do not retain chart or session objects in static fields.

Pair Ctx.MarketData.SubscribeDepth(...) with UnsubscribeDepth() in the owning host's cleanup path.

Keep UI work on the UI thread

Add-ons can use Context.Dispatcher for documented UI integration. Dispatch only the UI mutation; keep I/O and long computation off the dispatcher.

Indicators and strategies should use plot, draw, render-context, and invalidation APIs rather than mutating chart WPF internals.

Use HyperionX APIs, not look-alikes

Do not assume these external-platform concepts exist:

  • Draw.TextFixed
  • TextPosition
  • IsOverlay
  • External namespaces, attributes, lifecycle states, or generated cache regions
  • Order helpers that are absent from the current HyperionX reference

Use NinjaScript Migration when porting an idea, and compile after each converted layer.

Protect secrets and user accounts

  • Never put credentials, tokens, private keys, or seed phrases in source or script parameters.
  • Do not print secrets or full authenticated request payloads.
  • Keep external integrations behind documented permission and API boundaries.
  • Default strategy examples to simulation.
  • Validate quantity, price, account, connection, and provider capabilities before order actions.
  • Degrade safely when market data, depth, or account state is unavailable.

Review checklist

  • Correct category, namespace, and base type.
  • Unique public class and matching file name.
  • Defaults and runtime setup are in the correct states.
  • All indexed reads have sufficient guards.
  • Hot callbacks are non-blocking and allocation-conscious.
  • Parameters have ranges, labels, groups, and safe defaults.
  • Orders handle asynchronous and partial states.
  • Every acquired resource has a cleanup path.
  • Logs are useful, bounded, and secret-free.
  • The full custom project compiles.
  • The extension passes its runtime verification matrix.