Improved EMA & CDC Trailing Stop Strategy
Combines EMA trend filter, MACD confirmation and an ATR-based CDC trailing stop.
Details
- Entry Criteria:
- Long: price > EMA60, EMA60 > EMA90, MACD line > signal line.
- Short: price < EMA60, EMA60 < EMA90, MACD line < signal line.
- Long/Short: Both sides.
- Exit Criteria:
- Trailing stop or ATR-based profit target.
- Stops: Yes.
- Default Values:
Ema60Period= 60Ema90Period= 90AtrPeriod= 24Multiplier= 4ProfitTargetMultiplier= 2CandleType= TimeSpan.FromMinutes(1).TimeFrame()
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: EMA, MACD, ATR
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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;
/// <summary>
/// Trend-following strategy using EMA cross and ATR-based trailing stop.
/// </summary>
public class ImprovedEmaCdcTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _ema60Period;
private readonly StrategyParam<int> _ema90Period;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<decimal> _profitTargetMultiplier;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// EMA 60 period.
/// </summary>
public int Ema60Period { get => _ema60Period.Value; set => _ema60Period.Value = value; }
/// <summary>
/// EMA 90 period.
/// </summary>
public int Ema90Period { get => _ema90Period.Value; set => _ema90Period.Value = value; }
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
/// <summary>
/// ATR multiplier for trailing stop.
/// </summary>
public decimal Multiplier { get => _multiplier.Value; set => _multiplier.Value = value; }
/// <summary>
/// ATR multiplier for profit target.
/// </summary>
public decimal ProfitTargetMultiplier { get => _profitTargetMultiplier.Value; set => _profitTargetMultiplier.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of <see cref="ImprovedEmaCdcTrailingStopStrategy"/>.
/// </summary>
public ImprovedEmaCdcTrailingStopStrategy()
{
_ema60Period = Param(nameof(Ema60Period), 60)
.SetGreaterThanZero()
.SetDisplay("EMA 60 Period", "Length of the fast EMA", "Parameters")
.SetOptimize(20, 100, 10);
_ema90Period = Param(nameof(Ema90Period), 90)
.SetGreaterThanZero()
.SetDisplay("EMA 90 Period", "Length of the slow EMA", "Parameters")
.SetOptimize(30, 120, 10);
_atrPeriod = Param(nameof(AtrPeriod), 24)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Period for ATR calculation", "Parameters")
.SetOptimize(14, 50, 2);
_multiplier = Param(nameof(Multiplier), 4m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for trailing stop", "Parameters")
.SetOptimize(1m, 5m, 1m);
_profitTargetMultiplier = Param(nameof(ProfitTargetMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Profit Target Multiplier", "ATR multiplier for take profit", "Parameters")
.SetOptimize(1m, 5m, 1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
var ema60 = new ExponentialMovingAverage { Length = Ema60Period };
var ema90 = new ExponentialMovingAverage { Length = Ema90Period };
var atr = new AverageTrueRange { Length = AtrPeriod };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = 12 }, LongMa = { Length = 26 } },
SignalMa = { Length = 9 }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ema60, ema90, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema60);
DrawIndicator(area, ema90);
DrawOwnTrades(area);
var macdArea = CreateChartArea();
DrawIndicator(macdArea, macd);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue ema60Value, IIndicatorValue ema90Value, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macdTyped)
return;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
if (ema60Value.IsEmpty || ema90Value.IsEmpty || atrValue.IsEmpty)
return;
var ema60 = ema60Value.ToDecimal();
var ema90 = ema90Value.ToDecimal();
var atr = atrValue.ToDecimal();
if (atr <= 0) return;
var longCondition = candle.ClosePrice > ema60 && ema60 > ema90 && macd > signal;
var shortCondition = candle.ClosePrice < ema60 && ema60 < ema90 && macd < signal;
if (longCondition && Position <= 0)
BuyMarket();
else if (shortCondition && Position >= 0)
SellMarket();
}
}
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, AverageTrueRange, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class improved_ema_cdc_trailing_stop_strategy(Strategy):
def __init__(self):
super(improved_ema_cdc_trailing_stop_strategy, self).__init__()
self._ema60_period = self.Param("Ema60Period", 60) \
.SetGreaterThanZero() \
.SetDisplay("EMA 60 Period", "Length of the fast EMA", "Parameters")
self._ema90_period = self.Param("Ema90Period", 90) \
.SetGreaterThanZero() \
.SetDisplay("EMA 90 Period", "Length of the slow EMA", "Parameters")
self._atr_period = self.Param("AtrPeriod", 24) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "Period for ATR calculation", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(improved_ema_cdc_trailing_stop_strategy, self).OnStarted2(time)
ema60 = ExponentialMovingAverage()
ema60.Length = self._ema60_period.Value
ema90 = ExponentialMovingAverage()
ema90.Length = self._ema90_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = 12
self._macd.Macd.LongMa.Length = 26
self._macd.SignalMa.Length = 9
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._macd, ema60, ema90, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema60)
self.DrawIndicator(area, ema90)
self.DrawOwnTrades(area)
def OnProcess(self, candle, macd_val, ema60_val, ema90_val, atr_val):
if candle.State != CandleStates.Finished:
return
if ema60_val.IsEmpty or ema90_val.IsEmpty or atr_val.IsEmpty:
return
macd_v = macd_val.Macd
signal_v = macd_val.Signal
if macd_v is None or signal_v is None:
return
macd_d = float(macd_v)
signal_d = float(signal_v)
ema60_v = float(ema60_val)
ema90_v = float(ema90_val)
atr_v = float(atr_val)
if atr_v <= 0:
return
close = float(candle.ClosePrice)
long_cond = close > ema60_v and ema60_v > ema90_v and macd_d > signal_d
short_cond = close < ema60_v and ema60_v < ema90_v and macd_d < signal_d
if long_cond and self.Position <= 0:
self.BuyMarket()
elif short_cond and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return improved_ema_cdc_trailing_stop_strategy()