O "ADX EA" original do MetaTrader combina rompimentos do Average Directional Index com cruzamentos de +DI/−DI, confirmação de
momentum em período superior e um filtro MACD mensal. O port em C# replica esse fluxo de trabalho multi-filtro sobre a API de
alto nível do StockSharp. A estratégia se inscreve em três fluxos de candles:
Período principal (padrão 5 minutos) — aciona ADX, médias móveis lineares ponderadas, verificações de estrutura de preço
e filtros de volume.
Período de momentum (padrão 15 minutos) — produz os desvios de momentum em torno da linha de base 100 que condicionam as
entradas.
Período de MACD (padrão 30 dias) — espelha o MACD mensal que controla as saídas de posição.
Lógica de Trading
Módulo de rompimento – Quando habilitado, as operações compradas requerem:
ADX ou +DI acima de EntryLevel e a diferença entre +DI e −DI maior que MinDirectionalDifference.
A LWMA rápida acima da LWMA lenta, estrutura de candle altista (Low[2] < High[1]) e momentum crescente
(Momentum[1] > Momentum[2]).
Pelo menos uma das últimas três leituras de momentum no período superior a se desviar de 100 em mais de
MomentumBuyThreshold.
Volume crescente no período principal (Volume[1] > Volume[2] ou Volume[1] > Volume[3]).
MACD no período mensal altista (MacdMain[1] > MacdSignal[1]).
ADX acima de ExitLevel para confirmar a fortaleza geral do trend.
Os rompimentos vendidos aplicam a lógica simétrica com dominância de −DI, estrutura baixista (Low[1] < High[2]), momentum
abaixo de 100 em MomentumSellThreshold e uma comparação MACD baixista.
Módulo de cruzamento – Quando ativo, procura +DI cruzando acima de −DI (comprados) ou −DI cruzando acima de +DI
(vendidos). Filtros opcionais refletem o EA original:
RequireAdxSlope exige que ADX seja maior que a leitura anterior.
ConfirmCrossOnBreakout adiciona as mesmas verificações de limiar de rompimento na barra de cruzamento.
MinAdxMainLine impõe uma fortaleza mínima de ADX durante o cruzamento.
O alinhamento de LWMA, inclinação de momentum, expansão de volume e polaridade de MACD ainda devem concordar com a
direção pretendida.
Piramidação – Cada nova ordem adiciona volume de acordo com LotExponent. A estratégia trata TradeVolume como o
tamanho de lote base e o incrementa por LotExponent^n, onde n é o número de etapas já abertas. MaxTrades limita a
quantidade de volume líquido que pode ser acumulado.
Gestão de Risco
Ordens de proteção – TakeProfitSteps e StopLossSteps são passados ao StartProtection e expressos em passos de
preço do instrumento.
Trailing stop – TrailingStopSteps mantém uma barreira de trailing manual além do melhor preço de fechamento.
Ponto de equilíbrio – Quando UseBreakEven está habilitado, o stop é ajustado após o preço avançar BreakEvenTrigger
passos e pode deslocar o stop BreakEvenOffset passos.
Saída por MACD – Quando EnableMacdExit é verdadeiro, a relação MACD mensal fecha os comprados quando MACD cai abaixo
de seu sinal (e vice-versa para vendidos), correspondendo às rotinas Close_BUY/Close_SELL do EA.
Stop de capital – UseEquityStop rastreia a curva de lucro flutuante e liquida posições assim que o drawdown atinge
TotalEquityRisk por cento.
As funcionalidades que dependiam de alvos em moeda da conta ("Take Profit in Money", "Trailing Profit in Money", etc.) não estão
portadas porque as estratégias do StockSharp tipicamente gerenciam a lógica de proteção através de distâncias de stop e o
serviço de proteção integrado. Todos os outros pontos de decisão do EA são preservados com equivalentes de indicadores.
Parâmetros
Parâmetro
Padrão
Descrição
TradeVolume
0.01
Tamanho de lote base para a primeira entrada.
CandleType
Período de 5m
Série de candles principal para lógica ADX/LWMA.
MomentumCandleType
Período de 15m
Período superior para o filtro de desvio de momentum.
MacdCandleType
Período de 30 dias
Período que alimenta o filtro de saída MACD.
FastMaPeriod
6
Comprimento da média móvel linear ponderada rápida.
SlowMaPeriod
85
Comprimento da média móvel linear ponderada lenta.
AdxPeriod
14
Período do Average Directional Index.
MomentumPeriod
14
Período do indicador de momentum no período superior.
MacdFastPeriod
12
Período de EMA rápido dentro do filtro de saída MACD.
MacdSlowPeriod
26
Período de EMA lento dentro do filtro de saída MACD.
MacdSignalPeriod
9
Período de SMA de sinal dentro do filtro de saída MACD.
EnableBreakoutStrategy
true
Alternância para o ramo de rompimento ADX.
EnableCrossStrategy
true
Alternância para o ramo de cruzamento DI.
UseTrendFilter
true
Impõe dominância de +DI para comprados e −DI para vendidos durante rompimentos.
RequireAdxSlope
true
Requer que ADX suba ao avaliar cruzamentos DI.
ConfirmCrossOnBreakout
true
Adiciona limiares de rompimento ao módulo de cruzamento.
EnableMacdExit
true
Habilita a rotina de saída baseada em MACD.
EntryLevel
10
Nível mínimo de ADX/+DI/−DI usado pelos rompimentos.
ExitLevel
10
Fortaleza mínima de ADX que permite novas entradas.
MinDirectionalDifference
10
Diferença requerida entre +DI e −DI.
MinAdxMainLine
10
Nível mínimo de ADX durante cruzamentos DI.
MomentumBuyThreshold
0.3
Desvio requerido de 100 para confirmação de momentum altista.
MomentumSellThreshold
0.3
Desvio requerido de 100 para confirmação de momentum baixista.
MaxTrades
10
Número máximo de etapas de piramidação.
LotExponent
1.44
Multiplicador de volume para cada etapa adicional.
TakeProfitSteps
50
Distância, em passos de preço, para a ordem de take-profit.
StopLossSteps
20
Distância, em passos de preço, para a ordem de stop-loss.
TrailingStopSteps
40
Distância do trailing stop manual em passos de preço.
UseBreakEven
true
Ativa a lógica de realocação do ponto de equilíbrio.
BreakEvenTrigger
30
Passos de movimento favorável necessários antes de armar o ponto de equilíbrio.
BreakEvenOffset
30
Passos adicionais ao preço de entrada ao mover o stop.
UseEquityStop
true
Habilita a saída de emergência baseada em drawdown.
TotalEquityRisk
1
Percentual de drawdown permitido antes de fechar todas as posições.
Dicas de Uso
Alinhe MomentumCandleType e MacdCandleType com seu período principal para imitar o mapeamento de período original (p. ex.,
gráfico de 5 minutos → momentum de 15 minutos → MACD mensal).
Ajuste EntryLevel, MinDirectionalDifference e MinAdxMainLine juntos; diminuir os três afrouxa consideravelmente o filtro
de rompimento.
LotExponent maior que 1.0 recria o escalonamento estilo martingale do EA. Configure para 1.0 para manter os tamanhos de
posição constantes.
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()