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, but the current release-candidate guard rejects strategy order mutations for Broker and Server Paper accounts while Playback is active. Treat that guard as a backstop, 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 active release-candidate source. 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 signed target build because this Preview behavior changed during release preparation.

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 current release-candidate source 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.

Because this surface is still in the unreleased working tree, do not make a distributed 1.1.10 extension require it until the packaged build is frozen and 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.