Simple MA ADX EA
Strategy combining an EMA with the Average Directional Index to confirm trend strength.
It buys when the EMA is rising, the previous close is above the EMA, ADX exceeds a threshold and +DI is greater than -DI. It sells when the opposite conditions appear. Stop-loss and take-profit levels manage risk.
Details
- Entry Criteria: EMA direction, price vs EMA, ADX, +DI/-DI.
- Long/Short: Both directions.
- Exit Criteria: Opposite signal or protection orders.
- Stops: Yes.
- Default Values:
AdxPeriod= 8MaPeriod= 8AdxThreshold= 22mStopLoss= 30mTakeProfit= 100mVolume= 0.1mCandleType= TimeSpan.FromMinutes(1)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: EMA, ADX
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday (1m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining EMA direction with a slower trend filter.
/// </summary>
public class SimpleMaAdxEaStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema;
private ExponentialMovingAverage _trendMa;
private decimal _emaPrev1;
private decimal _emaPrev2;
private decimal _trendPrev;
private decimal _prevClose;
private bool _isInitialized;
private int _barsSinceTrade;
/// <summary>
/// Trend filter period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// EMA period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Minimum percentage distance between the fast and slow averages.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SimpleMaAdxEaStrategy"/> class.
/// </summary>
public SimpleMaAdxEaStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 21)
.SetDisplay("Trend Period", "Period for trend confirmation", "Indicators");
_maPeriod = Param(nameof(MaPeriod), 8)
.SetDisplay("MA Period", "EMA calculation period", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 0.05m)
.SetDisplay("Trend Threshold", "Minimum average distance in percent", "Indicators");
_stopLoss = Param(nameof(StopLoss), 400m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk Management");
_takeProfit = Param(nameof(TakeProfit), 1200m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk Management");
_cooldownBars = Param(nameof(CooldownBars), 200)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_trendMa = null;
_emaPrev1 = 0m;
_emaPrev2 = 0m;
_trendPrev = 0m;
_prevClose = 0m;
_isInitialized = false;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = MaPeriod };
_trendMa = new ExponentialMovingAverage { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
stopLoss: new Unit(StopLoss, UnitTypes.Absolute),
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
useMarketOrders: true);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var ema = _ema.Process(new DecimalIndicatorValue(_ema, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
var trendMa = _trendMa.Process(new DecimalIndicatorValue(_trendMa, candle.ClosePrice, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_ema.IsFormed || !_trendMa.IsFormed || trendMa == 0m)
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
if (!_isInitialized)
{
_emaPrev2 = ema;
_emaPrev1 = ema;
_trendPrev = trendMa;
_prevClose = candle.ClosePrice;
_isInitialized = true;
return;
}
var distancePercent = Math.Abs(ema - trendMa) / trendMa * 100m;
var buyCond1 = ema > _emaPrev1 && _emaPrev1 >= _emaPrev2;
var buyCond2 = _prevClose > _emaPrev1 && candle.ClosePrice > trendMa;
var buyCond3 = trendMa >= _trendPrev && distancePercent >= AdxThreshold;
var sellCond1 = ema < _emaPrev1 && _emaPrev1 <= _emaPrev2;
var sellCond2 = _prevClose < _emaPrev1 && candle.ClosePrice < trendMa;
var sellCond3 = trendMa <= _trendPrev && distancePercent >= AdxThreshold;
if (_barsSinceTrade >= CooldownBars && Position == 0)
{
if (buyCond1 && buyCond2 && buyCond3)
{
BuyMarket();
_barsSinceTrade = 0;
}
else if (sellCond1 && sellCond2 && sellCond3)
{
SellMarket();
_barsSinceTrade = 0;
}
}
_emaPrev2 = _emaPrev1;
_emaPrev1 = ema;
_trendPrev = trendMa;
_prevClose = candle.ClosePrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class simple_ma_adx_ea_strategy(Strategy):
def __init__(self):
super(simple_ma_adx_ea_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 21) \
.SetDisplay("Trend Period", "Period for trend confirmation", "Indicators")
self._ma_period = self.Param("MaPeriod", 8) \
.SetDisplay("MA Period", "EMA calculation period", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 0.05) \
.SetDisplay("Trend Threshold", "Minimum average distance in percent", "Indicators")
self._stop_loss = self.Param("StopLoss", 400.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk Management")
self._take_profit = self.Param("TakeProfit", 1200.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk Management")
self._cooldown_bars = self.Param("CooldownBars", 200) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._ema = None
self._trend_ma = None
self._ema_prev1 = 0.0
self._ema_prev2 = 0.0
self._trend_prev = 0.0
self._prev_close = 0.0
self._is_initialized = False
self._bars_since_trade = 0
@property
def AdxPeriod(self):
return self._adx_period.Value
@AdxPeriod.setter
def AdxPeriod(self, value):
self._adx_period.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
@property
def AdxThreshold(self):
return self._adx_threshold.Value
@AdxThreshold.setter
def AdxThreshold(self, value):
self._adx_threshold.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.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(simple_ma_adx_ea_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.MaPeriod
self._trend_ma = ExponentialMovingAverage()
self._trend_ma.Length = self.AdxPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute),
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute),
useMarketOrders=True)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
ema_val = float(process_float(self._ema, close, candle.OpenTime, True))
trend_ma_val = float(process_float(self._trend_ma, close, candle.OpenTime, True))
if not self._ema.IsFormed or not self._trend_ma.IsFormed or trend_ma_val == 0.0:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
if not self._is_initialized:
self._ema_prev2 = ema_val
self._ema_prev1 = ema_val
self._trend_prev = trend_ma_val
self._prev_close = float(close)
self._is_initialized = True
return
distance_percent = abs(ema_val - trend_ma_val) / trend_ma_val * 100.0
buy_cond1 = ema_val > self._ema_prev1 and self._ema_prev1 >= self._ema_prev2
buy_cond2 = self._prev_close > self._ema_prev1 and float(close) > trend_ma_val
buy_cond3 = trend_ma_val >= self._trend_prev and distance_percent >= float(self.AdxThreshold)
sell_cond1 = ema_val < self._ema_prev1 and self._ema_prev1 <= self._ema_prev2
sell_cond2 = self._prev_close < self._ema_prev1 and float(close) < trend_ma_val
sell_cond3 = trend_ma_val <= self._trend_prev and distance_percent >= float(self.AdxThreshold)
if self._bars_since_trade >= self.CooldownBars and self.Position == 0:
if buy_cond1 and buy_cond2 and buy_cond3:
self.BuyMarket()
self._bars_since_trade = 0
elif sell_cond1 and sell_cond2 and sell_cond3:
self.SellMarket()
self._bars_since_trade = 0
self._ema_prev2 = self._ema_prev1
self._ema_prev1 = ema_val
self._trend_prev = trend_ma_val
self._prev_close = float(close)
def OnReseted(self):
super(simple_ma_adx_ea_strategy, self).OnReseted()
self._ema = None
self._trend_ma = None
self._ema_prev1 = 0.0
self._ema_prev2 = 0.0
self._trend_prev = 0.0
self._prev_close = 0.0
self._is_initialized = False
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return simple_ma_adx_ea_strategy()