The original MetaTrader "ADX EA" combines Average Directional Index breakouts with +DI/−DI crossovers, higher timeframe momentum confirmation, and a monthly MACD filter. The C# port replicates that multi-filter workflow on top of the StockSharp high-level API. The strategy subscribes to three candle streams:
Primary timeframe (default 5 minutes) — drives ADX, linear weighted moving averages, price structure checks, and volume filters.
Momentum timeframe (default 15 minutes) — produces the momentum deviations around the 100 baseline that gate entries.
MACD timeframe (default 30 days) — mirrors the monthly MACD that controls position exits.
Trading logic
Breakout module – When enabled, long trades require:
ADX or +DI above EntryLevel and the gap between +DI and −DI greater than MinDirectionalDifference.
The fast LWMA above the slow LWMA, bullish candle structure (Low[2] < High[1]), and growing momentum (Momentum[1] > Momentum[2]).
At least one of the last three momentum readings on the higher timeframe to deviate from 100 by more than MomentumBuyThreshold.
Rising volume on the primary timeframe (Volume[1] > Volume[2] or Volume[1] > Volume[3]).
MACD on the monthly timeframe bullish (MacdMain[1] > MacdSignal[1]).
ADX above ExitLevel to confirm overall trend strength.
Short breakouts apply the symmetrical logic with −DI dominance, bearish structure (Low[1] < High[2]), momentum below 100 by MomentumSellThreshold, and a bearish MACD comparison.
Crossover module – When active, looks for +DI crossing above −DI (longs) or −DI crossing above +DI (shorts). Optional filters mirror the original EA:
RequireAdxSlope demands ADX to be higher than the previous reading.
ConfirmCrossOnBreakout adds the same breakout threshold checks on the crossing bar.
MinAdxMainLine enforces a minimum ADX strength during the cross.
LWMA alignment, momentum slope, volume expansion, and MACD polarity must still agree with the intended direction.
Pyramiding – Every new order adds volume according to LotExponent. The strategy treats TradeVolume as the base lot size and increases it by LotExponent^n, where n is the number of already opened steps. MaxTrades limits the amount of net volume that can be accumulated.
Risk management
Protective orders – TakeProfitSteps and StopLossSteps are fed to StartProtection and expressed in security price steps.
Trailing stop – TrailingStopSteps maintains a manual trailing barrier beyond the best close price.
Break-even – When UseBreakEven is enabled, the stop is tightened after price advances by BreakEvenTrigger steps and can offset the stop by BreakEvenOffset steps.
MACD exit – When EnableMacdExit is true, the monthly MACD relationship closes longs when MACD falls below its signal (and vice versa for shorts), matching the Close_BUY/Close_SELL routines from the EA.
Equity stop – UseEquityStop tracks the floating profit curve and liquidates positions once the drawdown reaches TotalEquityRisk percent.
Features that relied on account currency targets ("Take Profit in Money", "Trailing Profit in Money", etc.) are not ported because StockSharp strategies typically manage protective logic through stop distances and the built-in protection service. All other decision points from the EA are preserved with indicator equivalents.
Parameters
Parameter
Default
Description
TradeVolume
0.01
Base lot size for the first entry.
CandleType
5m timeframe
Primary candle series for ADX/LWMA logic.
MomentumCandleType
15m timeframe
Higher timeframe for the momentum deviation filter.
MacdCandleType
30-day timeframe
Timeframe that feeds the MACD exit filter.
FastMaPeriod
6
Fast linear weighted moving average length.
SlowMaPeriod
85
Slow linear weighted moving average length.
AdxPeriod
14
Average Directional Index period.
MomentumPeriod
14
Momentum indicator period on the higher timeframe.
MacdFastPeriod
12
Fast EMA period inside the MACD exit filter.
MacdSlowPeriod
26
Slow EMA period inside the MACD exit filter.
MacdSignalPeriod
9
Signal SMA period inside the MACD exit filter.
EnableBreakoutStrategy
true
Toggle for the ADX breakout branch.
EnableCrossStrategy
true
Toggle for the DI crossover branch.
UseTrendFilter
true
Enforces +DI dominance for longs and −DI dominance for shorts during breakouts.
RequireAdxSlope
true
Requires ADX to rise when evaluating DI crosses.
ConfirmCrossOnBreakout
true
Adds breakout thresholds to the crossover module.
EnableMacdExit
true
Enables the MACD-based exit routine.
EntryLevel
10
Minimum ADX/+DI/−DI level used by breakouts.
ExitLevel
10
Minimum ADX strength that allows new entries.
MinDirectionalDifference
10
Required gap between +DI and −DI.
MinAdxMainLine
10
Minimum ADX level during DI crosses.
MomentumBuyThreshold
0.3
Required deviation from 100 for bullish momentum confirmation.
MomentumSellThreshold
0.3
Required deviation from 100 for bearish momentum confirmation.
MaxTrades
10
Maximum number of pyramid steps.
LotExponent
1.44
Volume multiplier for each additional step.
TakeProfitSteps
50
Distance, in price steps, for the take-profit order.
StopLossSteps
20
Distance, in price steps, for the stop-loss order.
TrailingStopSteps
40
Manual trailing stop distance in price steps.
UseBreakEven
true
Activates the break-even relocation logic.
BreakEvenTrigger
30
Steps of favourable movement required before arming break-even.
BreakEvenOffset
30
Additional steps added to the entry price when moving the stop.
UseEquityStop
true
Enables the drawdown-based emergency exit.
TotalEquityRisk
1
Allowed percentage drawdown before flattening all positions.
Usage tips
Align the MomentumCandleType and MacdCandleType with your primary timeframe to mimic the original timeframe mapping (e.g., 5-minute chart → 15-minute momentum, → monthly MACD).
Tune EntryLevel, MinDirectionalDifference, and MinAdxMainLine together; lowering all three loosens the breakout filter considerably.
LotExponent greater than 1.0 recreates the martingale-style scaling from the EA. Set it to 1.0 to keep position sizes constant.
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>
/// ADX EA strategy using EMA crossover with ADX trend strength filter.
/// Buys when fast EMA crosses above slow EMA with strong trend.
/// Sells on reverse crossover.
/// </summary>
public class AdxEaStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public AdxEaStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// EMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 adx_ea_strategy(Strategy):
def __init__(self):
super(adx_ea_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 50) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 200) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(adx_ea_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(adx_ea_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
# Check SL/TP
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
# EMA crossover
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 60
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return adx_ea_strategy()