Your first strategy

Strategies

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();
    }
}
Drop it in. Compile this into your plugin DLL and it shows up in the catalog automatically — no registration. See Discovery & metadata for how that works.

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 →