Your first strategy
Your first strategy
A complete, working strategy in one screen: declare parameters and indicators, then go long or flat on a moving-average crossover.
SIMPLE SMA crossover
The fastest way to understand the shape of a strategy is to read one that does something real. The example below declares two simple moving averages whose periods come from parameters, goes long while the fast average is above the slow one, and exits to flat when the fast average crosses back below.
Notice how the IsFlat and IsLong position guards make the logic
edge-triggered: EnterLong only fires when you are currently flat, and
ExitLong only when you are currently long. Without those guards the strategy would issue an
order on every bar the condition held, instead of only at the crossover. Everything else here — the
parameter declarations, the DeclareIndicator factories, the OnBar body —
is explained in the pages that follow.
SmaCrossoverStrategy.csusing TradeStrike.Pipeline.Indicators.Builtin;
using TradeStrike.Pipeline.Strategies;
using TradeStrike.Pipeline.Strategies.Catalog;
[StrategyMetadata(
DisplayName = "SMA Crossover",
Description = "Long while a fast SMA is above a slow SMA; flat when it crosses back below.")]
public sealed class SmaCrossoverStrategy : Strategy
{
private StrategyParameter<int> _fastPeriod = null!;
private StrategyParameter<int> _slowPeriod = null!;
private StrategyParameter<int> _quantity = null!;
private StrategyIndicator<Sma> _fast = null!;
private StrategyIndicator<Sma> _slow = null!;
protected override void OnInitialize()
{
// (name, default, min, max, step) — the optimizable form.
_fastPeriod = IntParameter("FastPeriod", 10, 2, 100, 1);
_slowPeriod = IntParameter("SlowPeriod", 30, 5, 400, 1);
_quantity = IntParameter("Quantity", 1, 1, 100, 1);
// Factories run AFTER overrides land, so the period picks up this run's value.
_fast = DeclareIndicator(() => new Sma { Period = _fastPeriod.Value });
_slow = DeclareIndicator(() => new Sma { Period = _slowPeriod.Value });
}
protected override void OnBar()
{
if (BarCount < 2) return; // skip warm-up
double fast = _fast.Instance.Value;
double slow = _slow.Instance.Value;
if (fast > slow && IsFlat) EnterLong(_quantity.Value);
else if (fast < slow && IsLong) ExitLong();
}
}
Anatomy of the example
Three things are happening, each covered in depth later in the chapter:
Parameters
The IntParameter(name, default, min, max, step)
overload declares an optimizable input. Hold the returned handle and read .Value.
More →
Indicators
DeclareIndicator(factory) feeds an indicator every
bar the strategy sees. Read it through .Instance from OnStart onward.
More →
Managed intent
EnterLong / ExitLong express the
position you want; the framework computes the order and handles reversals.
More →