ADX-Stop-Order-Vorlage-Strategie
Diese Strategie demonstriert, wie ausstehende Stop-Orders mit dem Average Directional Index (ADX) und seinen Directional Movement-Komponenten platziert werden. Sie repliziert die Kernlogik einer klassischen MQL-Vorlage: Wenn der Markt einen starken Trend zeigt und die +DI- und -DI-Linien kreuzen, platziert das System eine Kauf-Stop- oder Verkauf-Stop-Order in einem festen Abstand. Schutzende Stop-Loss- und Take-Profit-Niveaus werden automatisch verwaltet.
Das Beispiel ist bewusst einfach gehalten und konzentriert sich auf die Auftragsabwicklung. Trader können es mit zusätzlichen Filtern oder Geldmanagementregeln erweitern, um fortgeschrittenere Systeme zu entwickeln.
Details
- Einstiegskriterien:
- ADX-Wert über dem Parameter
ADX Threshold. - Long:
+DIgrößer als-DIund vor zwei Kerzen war+DIunter-DI. - Short:
+DIkleiner als-DIund vor zwei Kerzen war+DIüber-DI. - Der aktuelle Spread muss unter dem Parameter
Max Spreadliegen.
- ADX-Wert über dem Parameter
- Orderplatzierung:
- Ausstehende Stop-Orders werden
PipsPreisschritte vom aktuellen Bid oder Ask entfernt platziert. - Es ist jeweils nur eine ausstehende Order aktiv; ältere Orders werden storniert, wenn ein neues Signal erscheint.
- Ausstehende Stop-Orders werden
- Ausstiegskriterien:
- Long-Positionen werden geschlossen, wenn
-DIüber+DIsteigt. - Short-Positionen werden geschlossen, wenn
+DIüber-DIsteigt.
- Long-Positionen werden geschlossen, wenn
- Stops:
- Stop-Loss und Take-Profit werden über
StartProtectionmit den ParameternStop LossundTake Profitangewendet.
- Stop-Loss und Take-Profit werden über
- Standardwerte:
ADX Period= 14ADX Threshold= 5Pips= 10 PreisschritteTake Profit= 1000 PreisschritteStop Loss= 500 PreisschritteMax Spread= 20 PreisschritteCandle Type= 15-Minuten-Kerzen
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: ADX, DMI
- Stops: Ja
- Komplexität: Moderat
- Zeitrahmen: Intraday
- Spread-Filter: Ja
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>
/// Strategy based on ADX and DMI crossovers.
/// </summary>
public class AdxStopOrderTemplateStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxSignal;
private readonly StrategyParam<int> _pips;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<decimal> _maxSpread;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _minDiSpread;
private readonly StrategyParam<int> _cooldownBars;
private decimal? _prevPlus;
private decimal? _prevMinus;
private int _cooldownRemaining;
/// <summary>
/// Initializes a new instance of the <see cref="AdxStopOrderTemplateStrategy"/> class.
/// </summary>
public AdxStopOrderTemplateStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Calculation period for ADX and DMI.", "Indicators");
_adxSignal = Param(nameof(AdxSignal), 20m)
.SetGreaterThanZero()
.SetDisplay("ADX Threshold", "Minimum ADX value to allow entries.", "Indicators");
_pips = Param(nameof(Pips), 10)
.SetGreaterThanZero()
.SetDisplay("Pending Offset", "Distance in price steps for stop orders.", "Orders");
_takeProfit = Param(nameof(TakeProfit), 1000)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit size in price steps.", "Risk");
_stopLoss = Param(nameof(StopLoss), 500)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss size in price steps.", "Risk");
_maxSpread = Param(nameof(MaxSpread), 20m)
.SetGreaterThanZero()
.SetDisplay("Max Spread", "Maximum allowed spread in price steps.", "Orders");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis.", "General");
_minDiSpread = Param(nameof(MinDiSpread), 5m)
.SetDisplay("DI Spread", "Minimum spread between DI+ and DI-.", "Filters");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change.", "Trading");
}
#region Parameters
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
public decimal AdxSignal
{
get => _adxSignal.Value;
set => _adxSignal.Value = value;
}
public int Pips
{
get => _pips.Value;
set => _pips.Value = value;
}
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
public decimal MaxSpread
{
get => _maxSpread.Value;
set => _maxSpread.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal MinDiSpread
{
get => _minDiSpread.Value;
set => _minDiSpread.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
#endregion
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevPlus = null;
_prevMinus = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var dmi = new DirectionalIndex { Length = AdxPeriod };
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(dmi, adx, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, adx);
DrawIndicator(area, dmi);
DrawOwnTrades(area);
}
var step = Security.PriceStep ?? 1m;
StartProtection(
new Unit(TakeProfit * step, UnitTypes.Absolute),
new Unit(StopLoss * step, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue dmiValue, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished || !dmiValue.IsFinal || !adxValue.IsFinal)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var dmiTyped = (DirectionalIndexValue)dmiValue;
if (dmiTyped.Plus is not decimal diPlus || dmiTyped.Minus is not decimal diMinus)
return;
var adxTyped = (AverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adx)
return;
if (_prevPlus is not decimal prevPlus || _prevMinus is not decimal prevMinus)
{
_prevPlus = diPlus;
_prevMinus = diMinus;
return;
}
var crossUp = prevPlus <= prevMinus && diPlus > diMinus;
var crossDown = prevPlus >= prevMinus && diPlus < diMinus;
var diSpread = Math.Abs(diPlus - diMinus);
if (_cooldownRemaining == 0 && adx >= AdxSignal && diSpread >= MinDiSpread)
{
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
else if (Position > 0 && crossDown)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
else if (Position < 0 && crossUp)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
_prevPlus = diPlus;
_prevMinus = diMinus;
}
}
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, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import DirectionalIndex, AverageDirectionalIndex
from StockSharp.Algo.Strategies import Strategy
class adx_stop_order_template_strategy(Strategy):
def __init__(self):
super(adx_stop_order_template_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Calculation period for ADX and DMI.", "Indicators")
self._adx_signal = self.Param("AdxSignal", 20.0) \
.SetDisplay("ADX Threshold", "Minimum ADX value to allow entries.", "Indicators")
self._pips = self.Param("Pips", 10) \
.SetDisplay("Pending Offset", "Distance in price steps for stop orders.", "Orders")
self._take_profit = self.Param("TakeProfit", 1000) \
.SetDisplay("Take Profit", "Take profit size in price steps.", "Risk")
self._stop_loss = self.Param("StopLoss", 500) \
.SetDisplay("Stop Loss", "Stop loss size in price steps.", "Risk")
self._max_spread = self.Param("MaxSpread", 20.0) \
.SetDisplay("Max Spread", "Maximum allowed spread in price steps.", "Orders")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for analysis.", "General")
self._min_di_spread = self.Param("MinDiSpread", 5.0) \
.SetDisplay("DI Spread", "Minimum spread between DI+ and DI-.", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change.", "Trading")
self._prev_plus = None
self._prev_minus = None
self._cooldown_remaining = 0
@property
def adx_period(self):
return self._adx_period.Value
@property
def adx_signal(self):
return self._adx_signal.Value
@property
def pips(self):
return self._pips.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def max_spread(self):
return self._max_spread.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def min_di_spread(self):
return self._min_di_spread.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(adx_stop_order_template_strategy, self).OnReseted()
self._prev_plus = None
self._prev_minus = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adx_stop_order_template_strategy, self).OnStarted2(time)
dmi = DirectionalIndex()
dmi.Length = self.adx_period
adx = AverageDirectionalIndex()
adx.Length = self.adx_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(dmi, adx, self.process_candle).Start()
step = self.Security.PriceStep if self.Security.PriceStep is not None else 1.0
step = float(step)
self.StartProtection(
Unit(self.take_profit * step, UnitTypes.Absolute),
Unit(self.stop_loss * step, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, adx)
self.DrawIndicator(area, dmi)
self.DrawOwnTrades(area)
def process_candle(self, candle, dmi_value, adx_value):
if candle.State != CandleStates.Finished or not dmi_value.IsFinal or not adx_value.IsFinal:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
di_plus = dmi_value.Plus
di_minus = dmi_value.Minus
if di_plus is None or di_minus is None:
return
di_plus = float(di_plus)
di_minus = float(di_minus)
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
adx_val = float(adx_ma)
if self._prev_plus is None or self._prev_minus is None:
self._prev_plus = di_plus
self._prev_minus = di_minus
return
cross_up = self._prev_plus <= self._prev_minus and di_plus > di_minus
cross_down = self._prev_plus >= self._prev_minus and di_plus < di_minus
di_spread = abs(di_plus - di_minus)
adx_threshold = float(self.adx_signal)
min_di = float(self.min_di_spread)
if self._cooldown_remaining == 0 and adx_val >= adx_threshold and di_spread >= min_di:
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
elif self.Position > 0 and cross_down:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
elif self.Position < 0 and cross_up:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_plus = di_plus
self._prev_minus = di_minus
def CreateClone(self):
return adx_stop_order_template_strategy()