Build Your First Strategy
This is the shortest supported path from a blank Code Lab file to a strategy that can submit managed orders. The example is intentionally complete so a developer or AI coding assistant can adapt it without borrowing APIs from another trading platform.
| Difficulty | Time | Applies to |
|---|---|---|
| Beginner | About 20 minutes | HyperionX 1.1.10 and the documented release-candidate custom runtime |
A strategy can place real orders when enabled on an external account. Compile first, then test on a chart with an explicitly selected LocalPaper account. Playback changes market data; it does not select LocalPaper for you.
What You Will Build
The strategy:
- calculates fast and slow moving averages;
- enters long or short when they cross;
- registers a stop loss and profit target before each entry;
- closes an open position on the opposite cross;
- exposes editable quantity and risk parameters;
- prints order-state and execution updates for verification.
This is an API example, not a profitable-trading claim.
1. Create The Strategy File
- In HyperionX, open Tools > Code Lab.
- Select Strategy as the script type.
- Select Blank strategy as the template.
- Select New.
- Replace the generated source with the complete example below.
Code Lab stores strategy source under:
%USERPROFILE%\Documents\HyperionX\Bin\Custom\Strategies
Keep the class name unique. A duplicate public class anywhere in the custom project can block the entire custom assembly.
2. Use A Complete Managed Strategy
using System.ComponentModel.DataAnnotations;
using HyperionX.Core.Attributes;
using HyperionX.Core.Enums;
using HyperionX.Core.Market;
namespace HyperionX.Custom.Strategies;
public class FirstManagedStrategy : Strategy
{
private const string LongEntry = "Long Entry";
private const string ShortEntry = "Short Entry";
[HyperionXProperty]
[Display(Name = "Fast period", GroupName = "Signals", Order = 1)]
[Range(1, 200)]
public int FastPeriod { get; set; }
[HyperionXProperty]
[Display(Name = "Slow period", GroupName = "Signals", Order = 2)]
[Range(2, 500)]
public int SlowPeriod { get; set; }
[HyperionXProperty]
[Display(Name = "Quantity", GroupName = "Risk", Order = 10)]
[Range(1, 1000)]
public double Quantity { get; set; }
[HyperionXProperty]
[Display(Name = "Stop ticks", GroupName = "Risk", Order = 11)]
[Range(1, 10000)]
public double StopTicks { get; set; }
[HyperionXProperty]
[Display(Name = "Target ticks", GroupName = "Risk", Order = 12)]
[Range(1, 10000)]
public double TargetTicks { get; set; }
public override void OnStateChanged()
{
if (State == State.SetDefaults)
{
Name = "First Managed Strategy";
Version = "1.0";
Calculate = CalculateMode.OnBarClose;
IsManagedOrderMode = true;
FastPeriod = 9;
SlowPeriod = 21;
Quantity = 1;
StopTicks = 20;
TargetTicks = 40;
}
}
public override void OnBarUpdate()
{
if (BarsInProgress != 0 || CurrentBar < SlowPeriod + 1)
return;
double fast = AverageClose(FastPeriod);
double slow = AverageClose(SlowPeriod);
double priorFast = AverageClose(FastPeriod, 1);
double priorSlow = AverageClose(SlowPeriod, 1);
bool crossedUp = priorFast <= priorSlow && fast > slow;
bool crossedDown = priorFast >= priorSlow && fast < slow;
if (Ctx.Position.IsFlat && crossedUp)
{
SetStopLossTicks(LongEntry, StopTicks);
SetProfitTargetTicks(LongEntry, TargetTicks);
EnterLong(Quantity, LongEntry);
}
else if (Ctx.Position.IsFlat && crossedDown)
{
SetStopLossTicks(ShortEntry, StopTicks);
SetProfitTargetTicks(ShortEntry, TargetTicks);
EnterShort(Quantity, ShortEntry);
}
else if (Ctx.Position.MarketPosition == MarketPosition.Long && crossedDown)
{
ExitLong(0, "Long Cross Exit", LongEntry);
}
else if (Ctx.Position.MarketPosition == MarketPosition.Short && crossedUp)
{
ExitShort(0, "Short Cross Exit", ShortEntry);
}
}
public override void OnOrderUpdate(Order order)
{
if (order == null)
return;
Print(
$"ORDER {order.Name}: {order.OrderState}, " +
$"quantity={order.Quantity}, filled={order.FilledQuantity}");
}
public override void OnExecutionUpdate(Order order)
{
if (order == null)
return;
Print(
$"FILL {order.Name}: filled={order.FilledQuantity}, " +
$"price={order.FillPrice}");
}
private double AverageClose(int period, int barsAgo = 0)
{
double sum = 0;
for (int index = barsAgo; index < barsAgo + period; index++)
sum += Close[index];
return sum / period;
}
}
3. Understand The Order Calls
The order sequence is deliberate:
SetStopLossTicks(LongEntry, StopTicks);
SetProfitTargetTicks(LongEntry, TargetTicks);
EnterLong(Quantity, LongEntry);
| Call | Meaning |
|---|---|
SetStopLossTicks("Long Entry", 20) | Register protection 20 ticks from the fill for the entry named Long Entry. |
SetProfitTargetTicks("Long Entry", 40) | Register a target 40 ticks from the fill for that same entry. |
EnterLong(1, "Long Entry") | Submit a managed market entry for quantity 1. |
"Long Entry" is not a comment. It is the relationship key between the entry and its protection. The value must match exactly, including spaces and capitalization.
Do not confuse these two uses of “stop”:
| Intent | Supported managed call |
|---|---|
| Enter only after price rises to a breakout level | EnterLongStopMarket(quantity, stopPrice, signalName) |
| Enter only after price falls to a downside breakout level | EnterShortStopMarket(quantity, stopPrice, signalName) |
| Protect an entry after it fills | SetStopLoss(...) or SetStopLossTicks(...) before the entry |
4. Compile The Complete Custom Project
- Save the strategy.
- Disable any affected strategy that is already running.
- Select Build or Compile Project.
- Fix every compile error, including errors in unrelated custom files.
- Confirm the build completes successfully.
Code Lab compiles all custom source into one HyperionX.Custom.dll. One broken indicator or duplicate class can prevent this strategy from appearing.
5. Load It On A Test Chart
- Open a chart with historical data.
- Use the chart's Strategies toolbar button or chart context menu.
- Select First Managed Strategy.
- Select and verify a
LocalPaperaccount. - Review the instrument, timeframe, quantity, stop ticks, and target ticks.
- Enable the strategy.
- Keep Orders, Positions, Strategies, and Output visible.
Do not use the main New > Strategy command for a local Code Lab strategy. That command opens the server-provisioned catalog.
After rebuilding, remove and add the strategy again, or reload its owning chart or strategy host. Toggling the old object off and on can keep the old assembly.
6. Verify Behavior
Before changing the signal logic, prove the order lifecycle:
- The strategy appears in the chart selector.
- It calculates without runtime errors.
- At most one entry is submitted for a cross.
- The entry signal name is correct.
- A stop and target appear after the entry fills.
- The stop and target use the intended prices and quantity.
- Filling one protective order cancels its OCO peer.
OnOrderUpdatereports submitted, working, filled, cancelled, or rejected states.OnExecutionUpdatereports actual fills.- Disabling the strategy produces the expected provider-side order and position state.
LocalPaper is a simplified full-fill simulator. Repeat provider-specific tests in the intended paper environment before considering live routing.