The Candle Trailing Stop strategy is a StockSharp port of the MetaTrader expert advisor with the same name. The original robot
combined multi-timeframe trend filters, momentum confirmation and an aggressive trailing stop engine that followed the lows and
highs of recent candles. The C# version keeps the same workflow but relies on high-level StockSharp components and exposes all
critical settings as strategy parameters.
Core logic
Data subscriptions
The trading timeframe drives entries and trailing stop updates.
A higher timeframe provides confirmation using linear weighted moving averages (LWMA) and a momentum indicator.
A third subscription calculates a MACD line on a slow timeframe (monthly by default) to filter trades.
Trend alignment
Trades are allowed only when the fast, middle and slow LWMA sequences are aligned on both the trading and higher
timeframes (bullish sequence for longs, bearish for shorts).
Momentum gate
The momentum indicator must be close to the neutral value of 100 for at least one of the last three higher-timeframe bars.
MACD confirmation
Longs require the MACD line to be above the signal line; shorts require the opposite relationship.
Entry trigger
A breakout through the fast LWMA on the current timeframe (candle closing above/below the average after touching it on the
previous bar) initiates new trades while respecting a configurable position limit.
Risk and exit management
Initial stop-loss and take-profit distances are defined in pips and automatically converted to price steps.
Stops can migrate to break-even, trail behind the extreme of recent candles, or fall back to a classic fixed-distance trail.
Optional equity-based features mirror the original EA: monetary take profit, percentage take profit, equity trailing and
drawdown protection.
Parameters
Group
Name
Description
Default
Trading
Volume
Order size in lots/contracts.
1
MaxTrades
Maximum aggregated exposure expressed as Volume * MaxTrades.
10
Indicators
FastCurrentLength
Fast LWMA on the trading timeframe.
9
MiddleCurrentLength
Middle LWMA on the trading timeframe.
20
SlowCurrentLength
Slow LWMA on the trading timeframe.
52
FastHigherLength
Fast LWMA on the higher timeframe.
9
MiddleHigherLength
Middle LWMA on the higher timeframe.
20
SlowHigherLength
Slow LWMA on the higher timeframe.
52
MomentumPeriod
Higher-timeframe momentum period.
14
MomentumBuyThreshold
Maximum deviation from 100 allowed for long trades.
0.3
MomentumSellThreshold
Maximum deviation from 100 allowed for short trades.
0.3
MacdFastLength
Fast EMA length for MACD confirmation.
12
MacdSlowLength
Slow EMA length for MACD confirmation.
26
MacdSignalLength
Signal EMA length for MACD confirmation.
9
Risk
StopLossPips
Stop-loss distance in pips.
20
TakeProfitPips
Take-profit distance in pips.
50
UseMoveToBreakEven
Enables the break-even logic.
true
BreakEvenTriggerPips
Profit in pips required before moving the stop.
30
BreakEvenOffsetPips
Offset added when shifting the stop to break even.
30
UseCandleTrail
Choose between candle-based trailing (true) or classic trailing (false).
true
CandleTrailLength
Number of closed candles used to compute trailing extremes.
3
PadAmountPips
Extra buffer added below/above the trailing extreme.
10
TrailTriggerPips
Profit required before the classic trail activates.
40
TrailAmountPips
Distance maintained by the classic trail.
40
Equity rules
UseMoneyTakeProfit
Close all positions when floating profit exceeds the monetary target.
false
MoneyTakeProfit
Monetary profit target.
40
UsePercentTakeProfit
Close all positions when floating profit exceeds the percentage target.
false
PercentTakeProfit
Percentage of initial equity used as profit target.
10
EnableMoneyTrailing
Activates trailing of floating profit after a threshold.
true
MoneyTrailTarget
Profit level that turns on the monetary trailing logic.
40
MoneyTrailStop
Maximum allowed pullback once the target was reached.
10
UseEquityStop
Enables equity drawdown protection.
true
EquityRiskPercent
Maximum drawdown from the equity peak before forcing a flat position.
1
Data
CurrentCandleType
Trading timeframe.
5m
HigherCandleType
Higher timeframe used for filters.
30m
MacdCandleType
Timeframe for MACD confirmation (monthly by default).
30d
Notes and assumptions
Pips are converted to price steps using the instrument tick size. On symbols where one pip differs from one tick you may need
to adjust the default pip distances.
Monetary features rely on unrealized profit approximated as (close - averagePrice) * position. Swap and commission adjustments
are not simulated.
The strategy uses market orders for entries and exits. Initial take-profit orders are registered once a trade is opened, while
stop-loss management is handled internally and exits through market orders when the calculated level is crossed.
All in-code comments are written in English as requested by the project guidelines.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class CandleTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CandleTrailingStopStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class candle_trailing_stop_strategy(Strategy):
def __init__(self):
super(candle_trailing_stop_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(candle_trailing_stop_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(candle_trailing_stop_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return candle_trailing_stop_strategy()