# [Strategy Development](https://www.hyperionx.support/strategy-development)

> Build managed HyperionX strategies with entries, exits, protective orders, and simulation-first validation.

Strategies are C# classes in `HyperionX.Custom.Strategies`. They derive from `Strategy` and can evaluate signals, submit orders, manage positions, and report performance.

New to Code Lab strategies? Complete [Build Your First Strategy](https://www.hyperionx.support/developers/first-strategy) first. It covers the Code Lab template, a complete source file, compilation, chart loading, `LocalPaper`, and a reusable AI prompt. Use this page for the design model and the [Strategy And Order Reference](https://www.hyperionx.support/reference-strategies-orders) for exact overloads.

:::warning Start in simulation
A strategy can place real orders when enabled against a connected account. Build the logic, backtest it, then test playback or simulation with an explicitly selected `LocalPaper` account before selecting live routing.
:::

## HyperionX Strategy Contract

When adapting generated code, keep these non-negotiable boundaries:

- Namespace: `HyperionX.Custom.Strategies`.
- Base class: `Strategy`.
- Lifecycle: `OnStateChanged()` and `OnBarUpdate()`.
- Normal entries: `EnterLong...` and `EnterShort...`.
- Normal exits: `ExitLong...` and `ExitShort...`.
- Managed protection: `SetStopLoss...` and `SetProfitTarget...`.
- Order state: `OnOrderUpdate(Order)` and `OnExecutionUpdate(Order)`.

Methods such as `Buy(...)`, `Sell(...)`, `ShortSell(...)`, `Cover(...)`, `OnStateChange()`, and `OnData(...)` are not the established HyperionX Code Lab strategy contract. Do not accept an AI-generated method merely because it resembles another trading platform.

## Minimal managed strategy

This example uses the preferred managed order helpers. It sets the stop and target before submitting each entry so protection is associated with the matching entry signal.

```csharp
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using HyperionX.Core.Attributes;
using HyperionX.Core.Enums;
using HyperionX.Custom.Strategies;

namespace HyperionX.Custom.Strategies;

public class MyCrossStrategy : Strategy
{
    private const string Parameters = "Parameters";
    private const string Risk = "Risk";

    [HyperionXProperty]
    [Category(Parameters)]
    [Display(Name = "Fast period", GroupName = Parameters, Order = 1)]
    [Range(1, 200)]
    public int FastPeriod { get; set; }

    [HyperionXProperty]
    [Category(Parameters)]
    [Display(Name = "Slow period", GroupName = Parameters, Order = 2)]
    [Range(2, 500)]
    public int SlowPeriod { get; set; }

    [HyperionXProperty]
    [Category(Risk)]
    [Display(Name = "Quantity", GroupName = Risk, Order = 10)]
    [Range(1, 1000)]
    public int Quantity { get; set; }

    [HyperionXProperty]
    [Category(Risk)]
    [Display(Name = "Stop ticks", GroupName = Risk, Order = 11)]
    [Range(1, 10000)]
    public int StopTicks { get; set; }

    [HyperionXProperty]
    [Category(Risk)]
    [Display(Name = "Target ticks", GroupName = Risk, Order = 12)]
    [Range(1, 10000)]
    public int TargetTicks { get; set; }

    public override void OnStateChanged()
    {
        if (State == State.SetDefaults)
        {
            Name = "My Cross Strategy";
            Version = "1.0";
            FastPeriod = 9;
            SlowPeriod = 21;
            Quantity = 1;
            StopTicks = 20;
            TargetTicks = 40;
        }
    }

    public override void OnBarUpdate()
    {
        if (CurrentBar < SlowPeriod + 1)
            return;

        var fast = AverageClose(FastPeriod);
        var slow = AverageClose(SlowPeriod);
        var priorFast = AverageClose(FastPeriod, 1);
        var priorSlow = AverageClose(SlowPeriod, 1);

        var crossedUp = priorFast <= priorSlow && fast > slow;
        var crossedDown = priorFast >= priorSlow && fast < slow;

        if (Ctx.Position.IsFlat && crossedUp)
        {
            SetStopLossTicks("Long Entry", StopTicks);
            SetProfitTargetTicks("Long Entry", TargetTicks);
            EnterLong(Quantity, "Long Entry");
        }
        else if (Ctx.Position.IsFlat && crossedDown)
        {
            SetStopLossTicks("Short Entry", StopTicks);
            SetProfitTargetTicks("Short Entry", TargetTicks);
            EnterShort(Quantity, "Short Entry");
        }
        else if (Ctx.Position.MarketPosition == MarketPosition.Long && crossedDown)
        {
            ExitLong(0, "Cross Exit", "Long Entry");
        }
        else if (Ctx.Position.MarketPosition == MarketPosition.Short && crossedUp)
        {
            ExitShort(0, "Cross Exit", "Short Entry");
        }
    }

    private double AverageClose(int period, int barsAgo = 0)
    {
        var sum = 0.0;
        for (var index = barsAgo; index < barsAgo + period; index++)
            sum += Close[index];

        return sum / period;
    }
}
```

## Managed order API

Use managed helpers for normal entry, exit, stop, and target workflows.

| Purpose | Helpers |
| --- | --- |
| Market entry | `EnterLong(...)`, `EnterShort(...)` |
| Limit entry | `EnterLongLimit(...)`, `EnterShortLimit(...)` |
| Stop-market entry | `EnterLongStopMarket(...)`, `EnterShortStopMarket(...)` |
| Market exit | `ExitLong(...)`, `ExitShort(...)` |
| Limit exit | `ExitLongLimit(...)`, `ExitShortLimit(...)` |
| Stop-market exit | `ExitLongStopMarket(...)`, `ExitShortStopMarket(...)` |
| Protective stop | `SetStopLoss(...)`, `SetStopLossTicks(...)` |
| Profit target | `SetProfitTarget(...)`, `SetProfitTargetTicks(...)` |
| Clear protection | `ClearStopLoss(...)`, `ClearProfitTarget(...)` |

Passing `0` as a managed exit quantity means “resolve the available position quantity.” Use `fromEntrySignal` to tie an exit or protective order to a specific entry.

`Ctx.Orders` provides a second documented managed facade for explicit market, limit, stop, cancel, change, flatten, and OCO operations. See [Strategy and Order Reference](https://www.hyperionx.support/reference-strategies-orders).

The inherited and `Ctx.Orders` protection methods use different argument order:

```csharp
SetStopLossTicks("Long Entry", 20);
Ctx.Orders.SetManagedStopLossTicks(20, "Long Entry");
```

Prefer the inherited form in ordinary managed strategies.

## Stops and targets

Register managed protection before the entry call:

```csharp
SetStopLossTicks("Breakout", StopTicks);
SetProfitTargetTicks("Breakout", TargetTicks);
EnterLong(Quantity, "Breakout");
```

Signal names are part of the relationship. `"Breakout"` must match exactly. Use different names when independently managing multiple entries.

Test:

- Long and short behavior.
- Partial fills and partial exits.
- Rejected or canceled entries.
- Gaps through stop prices.
- Multiple signals on the same bar.
- Strategy disable, connection loss, and shutdown.

## Position and account state

Use `Ctx.Position` for the strategy's current instrument position:

```csharp
if (Ctx.Position.IsFlat)
{
    // Entry may be considered.
}

double quantity = Ctx.Position.Quantity;
double averagePrice = Ctx.Position.AveragePrice;
MarketPosition side = Ctx.Position.MarketPosition;
```

`Ctx.Account` exposes read-only account balances, orders, and positions. Strategy position and account position are not interchangeable when manual orders, multiple strategies, or multiple accounts are involved.

## Order and execution callbacks

Submission is only the start of the order lifecycle.

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

public override void OnExecutionUpdate(Order order)
{
    // React to executions and resulting position changes.
}
```

Store returned order references when you need to cancel or change a specific working order. Do not infer a fill only because an entry method returned.

## Direct `SubmitOrder(...)`

Use the lower-level API only when managed helpers cannot express the required order lifecycle.

```csharp
Order order = SubmitOrder(
    selectedBarsInProgress: 0,
    orderAction: OrderAction.Buy,
    orderType: OrderType.Limit,
    quantity: Quantity,
    limitPrice: entryPrice,
    stopPrice: 0,
    oco: "",
    signalName: "Advanced Entry");
```

Direct submission makes your strategy responsible for more state: working-order references, OCO identifiers, cancel/replace races, partial fills, duplicate prevention, and protective-order synchronization.

Supported order actions include `Buy`, `BuyToCover`, `Sell`, and `SellShort`. Supported types include `Market`, `Limit`, `MIT`, `StopLimit`, and `StopMarket`.

## Account Routing And Real-Time Transition

The selected account still determines the intended route. In `RealTime`, supported `ServerPaper` and `Broker` accounts route through their provider connection, while `LocalPaper` uses the local simulator. Playback does not change the selection, but the current release-candidate build rejects submissions, changes, and cancellations for `ServerPaper` and `Broker` accounts while Playback is active. Playback strategies therefore require `LocalPaper`.

Before enabling a strategy, explicitly verify:

- Account name and `AccountType`.
- Connection and instrument.
- Quantity, order type, and price.
- Whether the provider accepts the requested order semantics.

The active release-candidate source exposes the Preview `HistoricalPositionSyncMode` setting:

- `Disabled` is the default.
- `AdoptOwnedExactMatch` adopts only one exact position match owned by the strategy in its expected account/instrument scope.
- With `AdoptOwnedExactMatch` selected, a missing, ambiguous, differently sized, opposite-side, or unowned position is not repaired. The mismatch blocks new entries.
- Synchronization does not submit a market order to recreate historical exposure and does not flatten or exit an account position.

The legacy `SyncHistoricalPositionOnRealtimeStart` Boolean is obsolete and maps only to exact-match adoption for compatibility. Do not design new code around it. This release-candidate surface remains Preview until the signed build is frozen and verified.

:::danger Review before live enablement
Playback does not select `LocalPaper`; verify it explicitly even though the current candidate rejects external-account mutations during Playback. Before a live transition, compare the strategy's expected position with the authoritative account state. Exact-match adoption is not a reconciliation or flattening service.
:::

## Reload And Runtime Failure Behavior

An enabled strategy keeps its existing object and assembly across a custom rebuild. Disable it before building. If it remained enabled during the build, toggling **Enabled** off and on afterward still reuses that object. Remove and add the strategy again, or reload its owning chart or strategy host, to run the newly compiled version.

Live indicator failures are automatically disabled after repeated consecutive errors. A live strategy calculation error skips that update. After ten consecutive live calculation errors, HyperionX keeps the strategy enabled but suspends new entries; exits and protective handling remain available.

Inspect `AreEntriesSuspended`, `LastRuntimeError`, `ConsecutiveRuntimeErrors`, and `RuntimeStatusText`, or use the warning shown in the Strategies window. Correct the cause and reconcile the authoritative account, position, and working orders before choosing **Resume entries** or calling `ResumeEntriesAfterFault()`. Historical and validation failures can abort their run instead of using this live policy.

## Performance and research state

Strategies expose `SystemPerformance` and summary properties used by backtesting, validation, and optimization, including net profit, commissions, profit factor, Sharpe, drawdown, trades, and win rate.

Do not read final performance fields as though they were stable during an active run. Fitness modules receive a completed strategy run through their own contract.

## Production checklist

- Guard every series and child-indicator read.
- Keep order quantity and risk inputs bounded with `[Range]`.
- Use managed helpers unless the strategy genuinely needs lower-level control.
- Give every entry and exit a meaningful signal name.
- Track asynchronous order states and partial fills.
- Prevent repeated actions from a condition that stays true.
- Model commission and slippage.
- Backtest, then use playback or simulation with an explicitly selected `LocalPaper` account.
- Confirm the selected account type; Playback does not select `LocalPaper`, and the candidate external-mutation guard is only a backstop.
- Review `HistoricalPositionSyncMode` before a live transition; exact-match adoption never creates or closes an account position.
- Compare strategy position with account state deliberately.
- Disable running strategies before rebuilding, then recreate each affected strategy instance.
- Monitor runtime exceptions; correct and reconcile a strategy before resuming entries after automatic suspension.
- Test disable, reload, disconnect, and shutdown behavior.
- Never store credentials in strategy source or properties.

Continue with [Strategy and Order Reference](https://www.hyperionx.support/reference-strategies-orders), [Backtesting](https://www.hyperionx.support/backtesting), and [Validation](https://www.hyperionx.support/validation).
