ADX-Kreuzungs-Strategie
Die ADX-Kreuzungs-Strategie basiert auf dem Average Directional Index (ADX). Sie analysiert die Kreuzung des positiven Richtungsindikators (+DI) und des negativen Richtungsindikators (-DI), um potenzielle Trendwechsel zu identifizieren.
Wenn +DI über -DI kreuzt, betrachtet die Strategie dies als bullishes Signal und kann Long-Positionen eröffnen, während bestehende Short-Positionen optional geschlossen werden. Wenn +DI unter -DI kreuzt, wird dies als bärisches Signal behandelt, das Short-Einstiege und das optionale Schließen von Long-Positionen auslöst. Optionale Stop-Loss- und Take-Profit-Niveaus werden durch integriertes Risikomanagement unterstützt.
Indikator
Die Strategie verwendet den AverageDirectionalIndex-Indikator aus StockSharp. Nur die Richtungslinien werden benötigt; der ADX-Hauptwert wird nicht für Entscheidungen verwendet.
Parameter
ADX Period – Länge der ADX-Berechnung. Standard ist 50.
Candle Type – Zeitrahmen für das Kerzen-Abonnement. Standard ist 1 Stunde.
Allow Buy Open – Long-Positionen eröffnen aktivieren. Standard ist true.
Allow Sell Open – Short-Positionen eröffnen aktivieren. Standard ist true.
Allow Buy Close – Schließen von Long-Positionen bei Verkaufssignal erlauben. Standard ist true.
Allow Sell Close – Schließen von Short-Positionen bei Kaufsignal erlauben. Standard ist true.
Stop Loss – Stop-Loss-Abstand in absoluten Preiseinheiten. Standard ist 1000.
Take Profit – Take-Profit-Abstand in absoluten Preiseinheiten. Standard ist 2000.
Handelslogik
- Kerzen des ausgewählten Zeitrahmens abonnieren und den ADX-Indikator berechnen.
- Vorherige Werte von +DI und -DI verfolgen, um Kreuzungen zu erkennen.
- Bei bullischer Kreuzung (+DI kreuzt über -DI):
- Short-Position schließen, wenn
Allow Sell Close aktiviert ist.
- Long-Position eröffnen, wenn
Allow Buy Open aktiviert ist.
- Bei bärischer Kreuzung (+DI kreuzt unter -DI):
- Long-Position schließen, wenn
Allow Buy Close aktiviert ist.
- Short-Position eröffnen, wenn
Allow Sell Open aktiviert ist.
- Schutzende Stop-Loss- und Take-Profit-Niveaus werden mit
StartProtection angewendet.
Hinweise
- Es werden nur abgeschlossene Kerzen (
CandleStates.Finished) verarbeitet.
- Die Strategie nutzt das integrierte StockSharp-Risikomanagement für Stop-Niveaus.
- Positionen werden durch Senden eines entgegengesetzten Marktauftrags mit dem aktuellen Volumen geschlossen.
Diese Strategie dient Bildungszwecken und erfordert möglicherweise weitere Anpassungen vor dem Einsatz auf Live-Märkten.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on crossovers of the +DI and -DI lines of the ADX indicator.
/// It opens or closes positions when directional lines cross each other.
/// </summary>
public class AdxCrossingStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _allowBuyOpen;
private readonly StrategyParam<bool> _allowSellOpen;
private readonly StrategyParam<bool> _allowBuyClose;
private readonly StrategyParam<bool> _allowSellClose;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _trendThreshold;
private decimal _prevPlusDi;
private decimal _prevMinusDi;
private bool _isInitialized;
/// <summary>
/// ADX period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Permission to open long positions.
/// </summary>
public bool AllowBuyOpen
{
get => _allowBuyOpen.Value;
set => _allowBuyOpen.Value = value;
}
/// <summary>
/// Permission to open short positions.
/// </summary>
public bool AllowSellOpen
{
get => _allowSellOpen.Value;
set => _allowSellOpen.Value = value;
}
/// <summary>
/// Permission to close long positions.
/// </summary>
public bool AllowBuyClose
{
get => _allowBuyClose.Value;
set => _allowBuyClose.Value = value;
}
/// <summary>
/// Permission to close short positions.
/// </summary>
public bool AllowSellClose
{
get => _allowSellClose.Value;
set => _allowSellClose.Value = value;
}
/// <summary>
/// Stop-loss in absolute price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take-profit in absolute price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Minimal ADX strength required to trade.
/// </summary>
public decimal TrendThreshold
{
get => _trendThreshold.Value;
set => _trendThreshold.Value = value;
}
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public AdxCrossingStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 50)
.SetDisplay("ADX Period", "Period of ADX indicator", "Indicators")
.SetOptimize(10, 100, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
_allowBuyOpen = Param(nameof(AllowBuyOpen), true)
.SetDisplay("Allow Buy Open", "Enable opening long positions", "Permissions");
_allowSellOpen = Param(nameof(AllowSellOpen), true)
.SetDisplay("Allow Sell Open", "Enable opening short positions", "Permissions");
_allowBuyClose = Param(nameof(AllowBuyClose), true)
.SetDisplay("Allow Buy Close", "Enable closing long positions", "Permissions");
_allowSellClose = Param(nameof(AllowSellClose), true)
.SetDisplay("Allow Sell Close", "Enable closing short positions", "Permissions");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Absolute stop-loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Absolute take-profit in price units", "Risk");
_trendThreshold = Param(nameof(TrendThreshold), 15m)
.SetDisplay("Trend Threshold", "Minimal ADX strength required to trade", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevPlusDi = default;
_prevMinusDi = default;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, adx);
DrawOwnTrades(area);
}
StartProtection(
stopLoss: StopLoss > 0m ? new Unit(StopLoss, UnitTypes.Absolute) : null,
takeProfit: TakeProfit > 0m ? new Unit(TakeProfit, UnitTypes.Absolute) : null);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var adx = (AverageDirectionalIndexValue)adxValue;
if (adx.Dx.Plus is not decimal plusDi || adx.Dx.Minus is not decimal minusDi)
return;
if (!_isInitialized)
{
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
_isInitialized = true;
return;
}
if (adx.MovingAverage is not decimal adxStrength || adxStrength < TrendThreshold)
{
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
return;
}
var buySignal = plusDi > minusDi && _prevPlusDi <= _prevMinusDi;
var sellSignal = plusDi < minusDi && _prevPlusDi >= _prevMinusDi;
if (buySignal)
{
if (AllowBuyOpen && Position <= 0)
BuyMarket(Position < 0 ? Volume + Math.Abs(Position) : Volume);
else if (AllowSellClose && Position < 0)
BuyMarket(Math.Abs(Position));
}
if (sellSignal)
{
if (AllowSellOpen && Position >= 0)
SellMarket(Position > 0 ? Volume + Position : Volume);
else if (AllowBuyClose && Position > 0)
SellMarket(Math.Abs(Position));
}
_prevPlusDi = plusDi;
_prevMinusDi = minusDi;
}
}
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 AverageDirectionalIndex
from StockSharp.Algo.Strategies import Strategy
class adx_crossing_strategy(Strategy):
def __init__(self):
super(adx_crossing_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 50) \
.SetDisplay("ADX Period", "Period of ADX indicator", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles for calculations", "General")
self._allow_buy_open = self.Param("AllowBuyOpen", True) \
.SetDisplay("Allow Buy Open", "Enable opening long positions", "Permissions")
self._allow_sell_open = self.Param("AllowSellOpen", True) \
.SetDisplay("Allow Sell Open", "Enable opening short positions", "Permissions")
self._allow_buy_close = self.Param("AllowBuyClose", True) \
.SetDisplay("Allow Buy Close", "Enable closing long positions", "Permissions")
self._allow_sell_close = self.Param("AllowSellClose", True) \
.SetDisplay("Allow Sell Close", "Enable closing short positions", "Permissions")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Absolute stop-loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Absolute take-profit in price units", "Risk")
self._trend_threshold = self.Param("TrendThreshold", 15.0) \
.SetDisplay("Trend Threshold", "Minimal ADX strength required to trade", "Indicators")
self._prev_plus_di = 0.0
self._prev_minus_di = 0.0
self._is_initialized = False
@property
def AdxPeriod(self):
return self._adx_period.Value
@AdxPeriod.setter
def AdxPeriod(self, value):
self._adx_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AllowBuyOpen(self):
return self._allow_buy_open.Value
@AllowBuyOpen.setter
def AllowBuyOpen(self, value):
self._allow_buy_open.Value = value
@property
def AllowSellOpen(self):
return self._allow_sell_open.Value
@AllowSellOpen.setter
def AllowSellOpen(self, value):
self._allow_sell_open.Value = value
@property
def AllowBuyClose(self):
return self._allow_buy_close.Value
@AllowBuyClose.setter
def AllowBuyClose(self, value):
self._allow_buy_close.Value = value
@property
def AllowSellClose(self):
return self._allow_sell_close.Value
@AllowSellClose.setter
def AllowSellClose(self, value):
self._allow_sell_close.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 TrendThreshold(self):
return self._trend_threshold.Value
@TrendThreshold.setter
def TrendThreshold(self, value):
self._trend_threshold.Value = value
def OnStarted2(self, time):
super(adx_crossing_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self.AdxPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.BindEx(adx, self.ProcessCandle) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, adx)
self.DrawOwnTrades(area)
sl = Unit(self.StopLoss, UnitTypes.Absolute) if self.StopLoss > 0 else None
tp = Unit(self.TakeProfit, UnitTypes.Absolute) if self.TakeProfit > 0 else None
self.StartProtection(stopLoss=sl, takeProfit=tp)
def ProcessCandle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
plus_raw = adx_value.Dx.Plus
minus_raw = adx_value.Dx.Minus
if plus_raw is None or minus_raw is None:
return
plus_di = float(plus_raw)
minus_di = float(minus_raw)
if not self._is_initialized:
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
self._is_initialized = True
return
ma_raw = adx_value.MovingAverage
if ma_raw is None:
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
return
adx_strength = float(ma_raw)
if adx_strength < float(self.TrendThreshold):
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
return
buy_signal = plus_di > minus_di and self._prev_plus_di <= self._prev_minus_di
sell_signal = plus_di < minus_di and self._prev_plus_di >= self._prev_minus_di
if buy_signal:
if self.AllowBuyOpen and self.Position <= 0:
volume = self.Volume + abs(self.Position) if self.Position < 0 else self.Volume
self.BuyMarket(volume)
elif self.AllowSellClose and self.Position < 0:
self.BuyMarket(abs(self.Position))
if sell_signal:
if self.AllowSellOpen and self.Position >= 0:
volume = self.Volume + self.Position if self.Position > 0 else self.Volume
self.SellMarket(volume)
elif self.AllowBuyClose and self.Position > 0:
self.SellMarket(abs(self.Position))
self._prev_plus_di = plus_di
self._prev_minus_di = minus_di
def OnReseted(self):
super(adx_crossing_strategy, self).OnReseted()
self._prev_plus_di = 0.0
self._prev_minus_di = 0.0
self._is_initialized = False
def CreateClone(self):
return adx_crossing_strategy()