This strategy trades the crossover of two Exponential Moving Averages (EMA). A faster EMA and a slower EMA are calculated from the chosen candle series. When the fast EMA crosses above the slow EMA the strategy can close any existing short position and optionally open a long position. When the fast EMA crosses below the slow EMA it can close a long position and optionally open a short position.
To manage risk, the strategy allows placing take profit and stop loss orders after a new position is opened. Both distances are specified in ticks. These protective orders are cancelled and recreated on each new entry.
The strategy provides separate switches for enabling or disabling long and short entries as well as for independently closing long and short positions on the opposite signal. All calculations use only finished candles.
Parameters
Fast Period – length of the fast EMA.
Slow Period – length of the slow EMA.
Candle Type – timeframe of candles used for calculations.
Allow Buy Open – open long when the fast EMA crosses above the slow EMA.
Allow Sell Open – open short when the fast EMA crosses below the slow EMA.
Allow Buy Close – close long when the fast EMA crosses below the slow EMA.
Allow Sell Close – close short when the fast EMA crosses above the slow EMA.
Take Profit Ticks – take profit distance in ticks from the entry price.
Stop Loss Ticks – stop loss distance in ticks from the entry price.
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>
/// Trades EMA crossovers with optional separate entry and exit permissions for long and short positions.
/// </summary>
public class EmaCrossoverSignalStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private bool _isInitialized;
private bool _wasFastAboveSlow;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public EmaCrossoverSignalStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Length of the fast EMA", "EMA");
_slowPeriod = Param(nameof(SlowPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Length of the slow EMA", "EMA");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_isInitialized = default;
_wasFastAboveSlow = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, Process)
.Start();
}
private void Process(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isInitialized)
{
_wasFastAboveSlow = fastValue > slowValue;
_isInitialized = true;
return;
}
var isFastAboveSlow = fastValue > slowValue;
if (_wasFastAboveSlow != isFastAboveSlow)
{
if (isFastAboveSlow)
{
// Upward crossover - buy signal
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else
{
// Downward crossover - sell signal
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_wasFastAboveSlow = isFastAboveSlow;
}
}
}
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 ema_crossover_signal_strategy(Strategy):
def __init__(self):
super(ema_crossover_signal_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast Period", "Length of the fast EMA", "EMA")
self._slow_period = self.Param("SlowPeriod", 13) \
.SetDisplay("Slow Period", "Length of the slow EMA", "EMA")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._is_initialized = False
self._was_fast_above_slow = False
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(ema_crossover_signal_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(fast_ema, slow_ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
if not self._is_initialized:
self._was_fast_above_slow = fast > slow
self._is_initialized = True
return
is_fast_above_slow = fast > slow
if self._was_fast_above_slow != is_fast_above_slow:
if is_fast_above_slow:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
else:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._was_fast_above_slow = is_fast_above_slow
def OnReseted(self):
super(ema_crossover_signal_strategy, self).OnReseted()
self._is_initialized = False
self._was_fast_above_slow = False
def CreateClone(self):
return ema_crossover_signal_strategy()