Port of the MetaTrader expert AddOn_TrailingStop. The strategy does not open positions on its own and only adjusts trailing stops for an existing net position.
How it works
Subscribes to Level1 data to monitor the latest best bid and ask quotes.
Calculates the pip size from the security decimals so the inputs behave like in MetaTrader (4/5 digits = 0.0001 pip, 2/3 digits = 0.01 pip).
When a long position is open and the bid price advances by TrailingStartPips pips, the strategy moves the internal trailing stop to Bid - TrailingStartPips pips.
The long stop is only advanced when the new level is at least TrailingStepPips pips higher than the previous stop.
When a short position is open and the ask price drops by TrailingStartPips pips, the strategy moves the internal trailing stop to Ask + TrailingStartPips pips.
The short stop is only advanced when the new level is at least TrailingStepPips pips lower than the previous stop.
If the current quote crosses the trailing stop, the strategy closes the entire position at market and resets its state.
Parameters
EnableTrailing (default true) – enables or disables trailing stop management.
TrailingStartPips (default 15) – profit in pips required before trailing activates.
TrailingStepPips (default 5) – extra profit in pips required before the stop can move again.
MagicNumber (default 0) – identifier kept for parity with the MQL expert. It is informational because StockSharp operates on the current strategy position.
Notes
Requires a configured Security, Portfolio, and Level1 data feed.
Designed to complement other strategies that handle entries.
Uses StrategyParam<T> so every input can be optimized or exposed in the UI.
Sends BuyMarket/SellMarket orders when the trailing stop is hit because StockSharp automatically manages protective orders after position exits.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// AddOn Trailing Stop strategy: EMA crossover with trailing logic.
/// Buys when fast EMA crosses above slow EMA, sells on cross below.
/// </summary>
public class AddOnTrailingStopStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public AddOnTrailingStopStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
BuyMarket();
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
SellMarket();
}
else
{
if (fastValue > slowValue && Position <= 0)
BuyMarket();
else if (fastValue < slowValue && Position >= 0)
SellMarket();
}
_prevFast = fastValue;
_prevSlow = slowValue;
_hasPrev = true;
}
}