Triple EMA Crossover Strategy
Strategy based on three simple moving averages. A long trade is opened when the short SMA crosses above the middle SMA while all three are aligned upward. A short trade is opened on the opposite crossover and alignment. Price crossing back over the short SMA exits the position.
Details
- Entry Criteria: Crossovers of SMA1 and SMA2 with trend filter.
- Long/Short: Both directions.
- Exit Criteria: Price crossing SMA1 or protective stops.
- Stops: Yes.
- Default Values:
Sma1Period= 9Sma2Period= 21Sma3Period= 55StopLossTicks= 200TakeProfitTicks= 200CandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: SMA
- Stops: Fixed
- Complexity: Basic
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Triple SMA crossover with stop loss and take profit.
/// </summary>
public class TripleEmaCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _sma1Period;
private readonly StrategyParam<int> _sma2Period;
private readonly StrategyParam<int> _sma3Period;
private readonly StrategyParam<int> _stopLossTicks;
private readonly StrategyParam<int> _takeProfitTicks;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevSma1;
private decimal _prevSma2;
private decimal _prevSma3;
private int _cooldown;
public TripleEmaCrossoverStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_sma1Period = Param(nameof(Sma1Period), 5)
.SetDisplay("SMA1 Period", "Period for short SMA", "Indicators");
_sma2Period = Param(nameof(Sma2Period), 13)
.SetDisplay("SMA2 Period", "Period for middle SMA", "Indicators");
_sma3Period = Param(nameof(Sma3Period), 21)
.SetDisplay("SMA3 Period", "Period for long SMA", "Indicators");
_stopLossTicks = Param(nameof(StopLossTicks), 200)
.SetDisplay("Stop Loss (ticks)", "Stop loss in ticks", "Risk");
_takeProfitTicks = Param(nameof(TakeProfitTicks), 200)
.SetDisplay("Take Profit (ticks)", "Take profit in ticks", "Risk");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Sma1Period
{
get => _sma1Period.Value;
set => _sma1Period.Value = value;
}
public int Sma2Period
{
get => _sma2Period.Value;
set => _sma2Period.Value = value;
}
public int Sma3Period
{
get => _sma3Period.Value;
set => _sma3Period.Value = value;
}
public int StopLossTicks
{
get => _stopLossTicks.Value;
set => _stopLossTicks.Value = value;
}
public int TakeProfitTicks
{
get => _takeProfitTicks.Value;
set => _takeProfitTicks.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSma1 = 0;
_prevSma2 = 0;
_prevSma3 = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma1 = new SimpleMovingAverage { Length = Sma1Period };
var sma2 = new SimpleMovingAverage { Length = Sma2Period };
var sma3 = new SimpleMovingAverage { Length = Sma3Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma1, sma2, sma3, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma1);
DrawIndicator(area, sma2);
DrawIndicator(area, sma3);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal sma1, decimal sma2, decimal sma3)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevSma1 == 0 || _prevSma2 == 0 || _prevSma3 == 0)
{
_prevSma1 = sma1;
_prevSma2 = sma2;
_prevSma3 = sma3;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevSma1 = sma1;
_prevSma2 = sma2;
_prevSma3 = sma3;
return;
}
// SMA1 cross SMA2 — entry signal
var crossUp = _prevSma1 <= _prevSma2 && sma1 > sma2;
var crossDown = _prevSma1 >= _prevSma2 && sma1 < sma2;
// Exit on opposite cross
if (Position > 0 && crossDown)
{
SellMarket();
_cooldown = 20;
}
else if (Position < 0 && crossUp)
{
BuyMarket();
_cooldown = 20;
}
// Entry on SMA1/SMA2 cross
if (Position == 0)
{
if (crossUp)
{
BuyMarket();
_cooldown = 20;
}
else if (crossDown)
{
SellMarket();
_cooldown = 20;
}
}
_prevSma1 = sma1;
_prevSma2 = sma2;
_prevSma3 = sma3;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class triple_ema_crossover_strategy(Strategy):
"""Triple SMA crossover: trade on SMA1/SMA2 cross with cooldown."""
def __init__(self):
super(triple_ema_crossover_strategy, self).__init__()
self._sma1_period = self.Param("Sma1Period", 5).SetDisplay("SMA1 Period", "Short SMA", "Indicators")
self._sma2_period = self.Param("Sma2Period", 13).SetDisplay("SMA2 Period", "Middle SMA", "Indicators")
self._sma3_period = self.Param("Sma3Period", 21).SetDisplay("SMA3 Period", "Long SMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(triple_ema_crossover_strategy, self).OnReseted()
self._prev_sma1 = 0
self._prev_sma2 = 0
self._prev_sma3 = 0
self._cooldown = 0
def OnStarted2(self, time):
super(triple_ema_crossover_strategy, self).OnStarted2(time)
self._prev_sma1 = 0
self._prev_sma2 = 0
self._prev_sma3 = 0
self._cooldown = 0
sma1 = SimpleMovingAverage()
sma1.Length = self._sma1_period.Value
sma2 = SimpleMovingAverage()
sma2.Length = self._sma2_period.Value
sma3 = SimpleMovingAverage()
sma3.Length = self._sma3_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sma1, sma2, sma3, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, sma1)
self.DrawIndicator(area, sma2)
self.DrawIndicator(area, sma3)
self.DrawOwnTrades(area)
def OnProcess(self, candle, s1, s2, s3):
if candle.State != CandleStates.Finished:
return
s1 = float(s1)
s2 = float(s2)
s3 = float(s3)
if self._prev_sma1 == 0 or self._prev_sma2 == 0 or self._prev_sma3 == 0:
self._prev_sma1 = s1
self._prev_sma2 = s2
self._prev_sma3 = s3
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_sma1 = s1
self._prev_sma2 = s2
self._prev_sma3 = s3
return
cross_up = self._prev_sma1 <= self._prev_sma2 and s1 > s2
cross_down = self._prev_sma1 >= self._prev_sma2 and s1 < s2
# Exit on opposite cross
if self.Position > 0 and cross_down:
self.SellMarket()
self._cooldown = 20
elif self.Position < 0 and cross_up:
self.BuyMarket()
self._cooldown = 20
# Entry
if self.Position == 0:
if cross_up:
self.BuyMarket()
self._cooldown = 20
elif cross_down:
self.SellMarket()
self._cooldown = 20
self._prev_sma1 = s1
self._prev_sma2 = s2
self._prev_sma3 = s3
def CreateClone(self):
return triple_ema_crossover_strategy()