Skip to main content

Strategy Development Patterns

A strategy owns execution decisions. Keep signal calculation, order intent, and asynchronous order state separate so historical and live behavior can be compared.

Strategies can route real orders

Select and verify the intended account before enabling a strategy. Develop with historical data, then playback or simulation on an explicitly selected LocalPaper account. Playback does not change the selected account. Treat any build-specific external-account guard as defense in depth, not as permission to leave an external account selected.

Configure once, decide on updates

Set safe defaults and protection in lifecycle setup, then make bar decisions:

public override void OnStateChanged()
{
if (State == State.SetDefaults)
{
Name = "Managed Cross";
Calculate = CalculateMode.OnBarClose;
IsManagedOrderMode = true;
}
else if (State == State.Configured)
{
SetStopLossTicks("Long", 12);
SetProfitTargetTicks("Long", 24);
}
}

public override void OnBarUpdate()
{
if (BarsInProgress != 0 || CurrentBar < 50)
return;

if (Ctx.Position.IsFlat && LongSignal())
EnterLong(1, "Long");
}

Use a stable signalName and the matching fromEntrySignal so managed exits and protection remain attributable to the entry.

Managed order surface

Managed helpers cover:

  • EnterLong and EnterShort market entries.
  • EnterLongLimit, EnterShortLimit.
  • EnterLongStopMarket, EnterShortStopMarket.
  • ExitLong, ExitShort and their limit/stop-market variants.
  • SetStopLoss / SetStopLossTicks.
  • SetProfitTarget / SetProfitTargetTicks.

In managed mode, stored stop/target settings create OCO-linked protection after an entry fill. Configure protection before submitting the entry. Clear or replace a setting deliberately when it should no longer apply.

The Ctx.Orders facade exposes equivalent methods named SetManagedStopLoss... and SetManagedProfitTarget..., but their argument order differs. Prefer the inherited methods above for ordinary strategy code; see the Strategy And Order Reference before using the facade.

Direct submission

Use SubmitOrder(...) when the managed surface cannot express the required ownership and price behavior:

Order SubmitOrder(
int selectedBarsInProgress,
OrderAction orderAction,
OrderType orderType,
double quantity,
double limitPrice,
double stopPrice,
string? oco,
string signalName,
string? comment = null,
bool allowMoneyManagement = false,
Action<Order> beforeFirstOrderUpdate = null);

The current documented/tested public paths are market, limit, and stop-market orders. Do not infer complete provider support merely because the OrderType enum contains another value.

allowMoneyManagement remains in the signature for compatibility but is not an opt-in switch in the reviewed 1.1.12-versioned source snapshot. A selected money-management module runs automatically for Buy and SellShort entries, including managed entries; exits are not resized. See Research Extensions and verify the exact installed build because this remains a Preview contract.

Use CreateOcoGroup(...) for direct paired exits. Keep ownership explicit: strategy-scoped cancellation is safer than account-wide cancellation.

Submission is asynchronous

The returned Order is an identity and current snapshot, not proof of acceptance or fill.

public override void OnOrderUpdate(Order order)
{
// Track accepted, working, canceled, rejected, and filled states.
}

public override void OnExecutionUpdate(Order order)
{
// Reconcile fills and partial fills.
}
  • Prevent the same signal from submitting repeatedly.
  • Handle partial fills, changed quantity, rejection, cancellation, and replacement.
  • Clear local order references only after a terminal state.
  • Reconcile position and working orders after reconnect.
  • Never treat a bar signal as an execution callback.

Runtime fault containment

A live strategy calculation error skips that update. After ten consecutive live calculation errors, HyperionX suspends new entries while leaving exits and protection available.

Inspect:

  • AreEntriesSuspended.
  • LastRuntimeError.
  • ConsecutiveRuntimeErrors.
  • RuntimeStatusText.

Call ResumeEntriesAfterFault() only after correcting the cause and reconciling account, position, and working orders. Historical and validation failures can abort the run instead of applying the live policy.

Stop and recompile behavior

Override OnStrategyStopping() to release explicit subscriptions, timers, and background work. Do not depend on State.Finalized or State.Terminated.

An enabled strategy can remain attached to its old assembly during a compile and is marked out of date. Disable and recreate it after runtime activation. Toggling the same existing object off and on is not proof that the newly compiled type is running.

Historical-to-live position sync

The reviewed 1.1.12-versioned source snapshot includes a Preview HistoricalPositionSyncMode:

  • Disabled is the default.
  • AdoptOwnedExactMatch can adopt only an exact, owned position match in the expected strategy scope.
  • A mismatch blocks new entries; it does not create, flatten, or repair a position.

This documentation audit did not independently test HistoricalPositionSyncMode in the distributed 1.1.12 MSI. Do not make a distributed extension require it until the exact installed build is verified.

Validation ladder

  1. Run a deterministic historical case with written expected trades.
  2. Include supported commission and warm-up settings, use realistic session data, and record or sensitivity-test the current lack of configurable slippage.
  3. Test no-data, disconnect, rejected order, partial fill, and restart paths.
  4. Run playback with an explicitly selected LocalPaper account.
  5. Run simulation with the intended provider and connection.
  6. Compare orders, executions, position, protection, and performance across modes.
  7. Require a separate operational review before enabling a live account.

Continue with the Strategy and Order reference, Backtesting, Validation, and Risk and Security.