MNQ EMA Strategy
Strategy based on multiple exponential moving averages for NASDAQ micro futures. It enters long when price is above fast and slow EMAs with bullish candle and exits using dynamic EMA levels, trailing profits and stop loss.
using System;
using System.Linq;
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>
/// MNQ strategy based on multiple EMA levels and dynamic exits.
/// </summary>
public class MNQEMAStrategy : Strategy
{
private readonly StrategyParam<int> _ema5Length;
private readonly StrategyParam<int> _ema13Length;
private readonly StrategyParam<int> _ema30Length;
private readonly StrategyParam<int> _ema200Length;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevEma5;
private decimal _prevEma13;
private bool _hasPrev;
private int _barsFromSignal;
public int Ema5Length { get => _ema5Length.Value; set => _ema5Length.Value = value; }
public int Ema13Length { get => _ema13Length.Value; set => _ema13Length.Value = value; }
public int Ema30Length { get => _ema30Length.Value; set => _ema30Length.Value = value; }
public int Ema200Length { get => _ema200Length.Value; set => _ema200Length.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MNQEMAStrategy()
{
_ema5Length = Param(nameof(Ema5Length), 5).SetGreaterThanZero();
_ema13Length = Param(nameof(Ema13Length), 13).SetGreaterThanZero();
_ema30Length = Param(nameof(Ema30Length), 30).SetGreaterThanZero();
_ema200Length = Param(nameof(Ema200Length), 50).SetGreaterThanZero();
_signalCooldownBars = Param(nameof(SignalCooldownBars), 12).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma5 = 0m;
_prevEma13 = 0m;
_hasPrev = false;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_prevEma5 = 0m;
_prevEma13 = 0m;
_hasPrev = false;
_barsFromSignal = SignalCooldownBars;
var ema5 = new EMA { Length = Ema5Length };
var ema13 = new EMA { Length = Ema13Length };
var ema30 = new EMA { Length = Ema30Length };
var ema200 = new EMA { Length = Ema200Length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema5, ema13, ema30, ema200, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema5, decimal ema13, decimal ema30, decimal ema200)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
_barsFromSignal++;
if (!_hasPrev)
{
_prevEma5 = ema5;
_prevEma13 = ema13;
_hasPrev = true;
return;
}
var crossUp = _prevEma5 <= _prevEma13 && ema5 > ema13;
var crossDown = _prevEma5 >= _prevEma13 && ema5 < ema13;
var longTrend = close > ema200 && ema30 > ema200;
var shortTrend = close < ema200 && ema30 < ema200;
if (_barsFromSignal >= SignalCooldownBars && crossUp && longTrend && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_barsFromSignal = 0;
}
else if (_barsFromSignal >= SignalCooldownBars && crossDown && shortTrend && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_barsFromSignal = 0;
}
else if (Position > 0 && crossDown)
{
SellMarket();
}
else if (Position < 0 && crossUp)
{
BuyMarket();
}
_prevEma5 = ema5;
_prevEma13 = ema13;
}
}
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 mnq_ema_strategy(Strategy):
"""
MNQ strategy based on multiple EMA levels.
EMA5/13 crossover with EMA30/200 trend filter.
"""
def __init__(self):
super(mnq_ema_strategy, self).__init__()
self._ema5_len = self.Param("Ema5Length", 5).SetDisplay("EMA 5", "EMA 5 length", "Indicators")
self._ema13_len = self.Param("Ema13Length", 13).SetDisplay("EMA 13", "EMA 13 length", "Indicators")
self._ema30_len = self.Param("Ema30Length", 30).SetDisplay("EMA 30", "EMA 30 length", "Indicators")
self._ema200_len = self.Param("Ema200Length", 50).SetDisplay("EMA 200", "EMA 200 length", "Indicators")
self._cooldown_bars = self.Param("SignalCooldownBars", 12).SetDisplay("Cooldown", "Min bars between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Timeframe", "General")
self._prev_e5 = 0.0
self._prev_e13 = 0.0
self._has_prev = False
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mnq_ema_strategy, self).OnReseted()
self._prev_e5 = 0.0
self._prev_e13 = 0.0
self._has_prev = False
self._bars_from_signal = 0
def OnStarted2(self, time):
super(mnq_ema_strategy, self).OnStarted2(time)
self._prev_e5 = 0.0
self._prev_e13 = 0.0
self._has_prev = False
self._bars_from_signal = self._cooldown_bars.Value
e5 = ExponentialMovingAverage()
e5.Length = self._ema5_len.Value
e13 = ExponentialMovingAverage()
e13.Length = self._ema13_len.Value
e30 = ExponentialMovingAverage()
e30.Length = self._ema30_len.Value
e200 = ExponentialMovingAverage()
e200.Length = self._ema200_len.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(e5, e13, e30, e200, self._process_candle).Start()
def _process_candle(self, candle, e5v, e13v, e30v, e200v):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
e5 = float(e5v)
e13 = float(e13v)
e30 = float(e30v)
e200 = float(e200v)
close = float(candle.ClosePrice)
self._bars_from_signal += 1
if not self._has_prev:
self._prev_e5 = e5
self._prev_e13 = e13
self._has_prev = True
return
cross_up = self._prev_e5 <= self._prev_e13 and e5 > e13
cross_down = self._prev_e5 >= self._prev_e13 and e5 < e13
long_trend = close > e200 and e30 > e200
short_trend = close < e200 and e30 < e200
if self._bars_from_signal >= self._cooldown_bars.Value and cross_up and long_trend and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif self._bars_from_signal >= self._cooldown_bars.Value and cross_down and short_trend and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
elif self.Position > 0 and cross_down:
self.SellMarket()
elif self.Position < 0 and cross_up:
self.BuyMarket()
self._prev_e5 = e5
self._prev_e13 = e13
def CreateClone(self):
return mnq_ema_strategy()