Der originale MetaTrader "ADX EA" kombiniert Average Directional Index-Ausbrüche mit +DI/−DI-Kreuzungen, Momentum-Bestätigung
auf einem höheren Zeitrahmen und einem monatlichen MACD-Filter. Der C#-Port repliziert diesen Multi-Filter-Workflow auf der
StockSharp High-Level-API. Die Strategie abonniert drei Kerzendatenströme:
Primärer Zeitrahmen (Standard 5 Minuten) — treibt ADX, lineare gewichtete gleitende Durchschnitte, Preisstruktur-
prüfungen und Volumenfilter an.
Momentum-Zeitrahmen (Standard 15 Minuten) — erzeugt die Momentum-Abweichungen um die 100-Basislinie, die Einträge
kontrollieren.
MACD-Zeitrahmen (Standard 30 Tage) — spiegelt den monatlichen MACD wider, der Positionsausstiege steuert.
Handelslogik
Ausbruchsmodul – Wenn aktiviert, erfordern Long-Trades:
ADX oder +DI über EntryLevel und die Lücke zwischen +DI und −DI größer als MinDirectionalDifference.
Die schnelle LWMA über der langsamen LWMA, bullische Kerzenstruktur (Low[2] < High[1]) und wachsendes Momentum
(Momentum[1] > Momentum[2]).
Mindestens eine der letzten drei Momentum-Ablesungen auf dem höheren Zeitrahmen weicht von 100 um mehr als
MomentumBuyThreshold ab.
Steigendes Volumen auf dem primären Zeitrahmen (Volume[1] > Volume[2] oder Volume[1] > Volume[3]).
MACD auf dem monatlichen Zeitrahmen bullisch (MacdMain[1] > MacdSignal[1]).
ADX über ExitLevel zur Bestätigung der allgemeinen Trendstärke.
Short-Ausbrüche wenden die symmetrische Logik mit −DI-Dominanz, bärischer Struktur (Low[1] < High[2]), Momentum unter 100
um MomentumSellThreshold und einem bärischen MACD-Vergleich an.
Kreuzungsmodul – Wenn aktiv, sucht nach +DI-Kreuzung über −DI (Longs) oder −DI-Kreuzung über +DI (Shorts). Optionale
Filter spiegeln den Original-EA wider:
RequireAdxSlope erfordert, dass ADX höher als die vorherige Ablesung ist.
ConfirmCrossOnBreakout fügt dieselben Ausbruchsschwellenprüfungen auf der Kreuzungsbar hinzu.
MinAdxMainLine erzwingt eine Mindest-ADX-Stärke während der Kreuzung.
LWMA-Ausrichtung, Momentum-Steigung, Volumenexpansion und MACD-Polarität müssen noch mit der beabsichtigten Richtung
übereinstimmen.
Pyramidisierung – Jede neue Order fügt Volumen gemäß LotExponent hinzu. Die Strategie behandelt TradeVolume als
Basis-Lotgröße und erhöht sie um LotExponent^n, wobei n die Anzahl der bereits geöffneten Stufen ist. MaxTrades
begrenzt die Menge des Netto-Volumens, das angesammelt werden kann.
Risikomanagement
Schutzaufträge – TakeProfitSteps und StopLossSteps werden an StartProtection übergeben und in Preisschritten des
Instruments ausgedrückt.
Trailing Stop – TrailingStopSteps pflegt eine manuelle Trailing-Barriere jenseits des besten Schlusskurses.
Break-Even – Wenn UseBreakEven aktiviert ist, wird der Stop nach Preisfortschritt von BreakEvenTrigger Schritten
angezogen und kann den Stop um BreakEvenOffset Schritte versetzen.
MACD-Ausstieg – Wenn EnableMacdExit wahr ist, schließt die monatliche MACD-Beziehung Longs, wenn MACD unter sein
Signal fällt (und umgekehrt für Shorts), passend zu den Close_BUY/Close_SELL-Routinen des EA.
Eigenkapital-Stop – UseEquityStop verfolgt die Kurve des schwebenden Gewinns und liquidiert Positionen, sobald der
Drawdown TotalEquityRisk Prozent erreicht.
Funktionen, die auf Kontowährungszielen basierten ("Take Profit in Money", "Trailing Profit in Money" usw.) sind nicht portiert,
da StockSharp-Strategien typischerweise die Schutzlogik durch Stop-Abstände und den eingebauten Schutzdienst verwalten. Alle
anderen Entscheidungspunkte des EA werden mit Indikator-Äquivalenten beibehalten.
Parameter
Parameter
Standard
Beschreibung
TradeVolume
0.01
Basis-Lotgröße für den ersten Eintrag.
CandleType
5m-Zeitrahmen
Primäre Kerzenreihe für ADX/LWMA-Logik.
MomentumCandleType
15m-Zeitrahmen
Höherer Zeitrahmen für den Momentum-Abweichungsfilter.
Momentum-Indikatorperiode auf dem höheren Zeitrahmen.
MacdFastPeriod
12
Schnelle EMA-Periode im MACD-Ausstiegsfilter.
MacdSlowPeriod
26
Langsame EMA-Periode im MACD-Ausstiegsfilter.
MacdSignalPeriod
9
Signal-SMA-Periode im MACD-Ausstiegsfilter.
EnableBreakoutStrategy
true
Umschalter für den ADX-Ausbruchszweig.
EnableCrossStrategy
true
Umschalter für den DI-Kreuzungszweig.
UseTrendFilter
true
Erzwingt +DI-Dominanz für Longs und −DI-Dominanz für Shorts bei Ausbrüchen.
RequireAdxSlope
true
Erfordert ADX-Anstieg bei der Bewertung von DI-Kreuzungen.
ConfirmCrossOnBreakout
true
Fügt Ausbruchsschwellen zum Kreuzungsmodul hinzu.
EnableMacdExit
true
Aktiviert die MACD-basierte Ausstiegsroutine.
EntryLevel
10
Minimales ADX/+DI/−DI-Niveau für Ausbrüche.
ExitLevel
10
Minimale ADX-Stärke, die neue Einträge erlaubt.
MinDirectionalDifference
10
Erforderliche Lücke zwischen +DI und −DI.
MinAdxMainLine
10
Minimales ADX-Niveau bei DI-Kreuzungen.
MomentumBuyThreshold
0.3
Erforderliche Abweichung von 100 für bullische Momentum-Bestätigung.
MomentumSellThreshold
0.3
Erforderliche Abweichung von 100 für bärische Momentum-Bestätigung.
MaxTrades
10
Maximale Anzahl von Pyramidisierungsstufen.
LotExponent
1.44
Volumen-Multiplikator für jede zusätzliche Stufe.
TakeProfitSteps
50
Abstand in Preisschritten für den Take-Profit-Auftrag.
StopLossSteps
20
Abstand in Preisschritten für den Stop-Loss-Auftrag.
TrailingStopSteps
40
Manueller Trailing-Stop-Abstand in Preisschritten.
UseBreakEven
true
Aktiviert die Break-Even-Umsetzungslogik.
BreakEvenTrigger
30
Schritte günstiger Bewegung, die vor dem Aktivieren von Break-Even erforderlich sind.
BreakEvenOffset
30
Zusätzliche Schritte, die beim Verschieben des Stops zum Eintrittspreis hinzugefügt werden.
UseEquityStop
true
Aktiviert den drawdown-basierten Notausstieg.
TotalEquityRisk
1
Erlaubter prozentualer Drawdown vor dem Schließen aller Positionen.
Verwendungshinweise
Richten Sie MomentumCandleType und MacdCandleType auf Ihren primären Zeitrahmen aus, um das ursprüngliche Zeitrahmen-
Mapping nachzuahmen (z. B. 5-Minuten-Chart → 15-Minuten-Momentum → monatlicher MACD).
Stimmen Sie EntryLevel, MinDirectionalDifference und MinAdxMainLine zusammen ab; alle drei zu senken lockert den
Ausbruchsfilter erheblich.
LotExponent größer als 1.0 repliziert die martingalähnliche Skalierung des EA. Setzen Sie ihn auf 1.0, um die
Positionsgrößen konstant zu halten.
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()