ADX-Volumen-Multiplikator-Strategie
Die ADX-Volumen-Multiplikator-Strategie kombiniert die Trendstärke des Average Directional Index mit einem Volumenanstiegsfilter. Sie tritt in Trades nur ein, wenn der ADX einen Schwellenwert überschreitet, die dominante Richtungslinie in die Trendrichtung zeigt und das aktuelle Volumen einen gleitenden Durchschnitt multipliziert mit einem benutzerdefinierten Faktor überschreitet.
Details
- Einstiegskriterien:
- ADX über Schwellenwert und DI+ > DI- mit Volumen größer als SMA(Volumen) * Multiplikator → Long.
- ADX über Schwellenwert und DI- > DI+ mit Volumen größer als SMA(Volumen) * Multiplikator → Short.
- Long/Short: Beide.
- Ausstiegskriterien:
- Ein umgekehrtes Signal löst eine Positionsumkehr aus.
- Stops: Keine.
- Standardwerte:
AdxPeriod= 21AdxThreshold= 26VolumeMultiplier= 1.8VolumePeriod= 20
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: ADX, Volume SMA
- Stops: Nein
- Komplexität: Niedrig
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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 with volume multiplier filter.
/// </summary>
public class AdxVolumeMultiplierStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<decimal> _volumeMultiplier;
private readonly StrategyParam<int> _volumePeriod;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldownRemaining;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public decimal VolumeMultiplier { get => _volumeMultiplier.Value; set => _volumeMultiplier.Value = value; }
public int VolumePeriod { get => _volumePeriod.Value; set => _volumePeriod.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdxVolumeMultiplierStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period for ADX", "ADX");
_adxThreshold = Param(nameof(AdxThreshold), 20m)
.SetDisplay("ADX Threshold", "Trend strength threshold", "ADX");
_volumeMultiplier = Param(nameof(VolumeMultiplier), 0.8m)
.SetDisplay("Volume Multiplier", "Multiplier for average volume", "Volume");
_volumePeriod = Param(nameof(VolumePeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Volume Period", "Period for volume SMA", "Volume");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var ema = new ExponentialMovingAverage { Length = VolumePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var adxTyped = (IAverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adx ||
adxTyped.Dx.Plus is not decimal diPlus ||
adxTyped.Dx.Minus is not decimal diMinus)
return;
var ema = emaValue.ToDecimal();
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Use EMA as trend confirmation instead of volume multiplier
var aboveEma = candle.ClosePrice > ema;
var belowEma = candle.ClosePrice < ema;
var longCondition = adx > AdxThreshold && diPlus > diMinus && aboveEma;
var shortCondition = adx > AdxThreshold && diMinus > diPlus && belowEma;
if (longCondition && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
else if (shortCondition && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
}
}
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
from StockSharp.Algo.Indicators import AverageDirectionalIndex, ExponentialMovingAverage, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class adx_volume_multiplier_strategy(Strategy):
def __init__(self):
super(adx_volume_multiplier_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ADX Period", "Period for ADX", "ADX")
self._adx_threshold = self.Param("AdxThreshold", 20.0) \
.SetDisplay("ADX Threshold", "Trend strength threshold", "ADX")
self._volume_period = self.Param("VolumePeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Volume Period", "Period for volume SMA", "Volume")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adx_volume_multiplier_strategy, self).OnReseted()
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adx_volume_multiplier_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = int(self._adx_period.Value)
ema = ExponentialMovingAverage()
ema.Length = int(self._volume_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, adx_value, ema_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
dx = adx_value.Dx
di_plus_val = dx.Plus
di_minus_val = dx.Minus
if di_plus_val is None or di_minus_val is None:
return
adx_v = float(adx_ma)
di_plus = float(di_plus_val)
di_minus = float(di_minus_val)
ema_v = float(IndicatorHelper.ToDecimal(ema_value))
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
threshold = float(self._adx_threshold.Value)
above_ema = close > ema_v
below_ema = close < ema_v
long_cond = adx_v > threshold and di_plus > di_minus and above_ema
short_cond = adx_v > threshold and di_minus > di_plus and below_ema
if long_cond and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif short_cond and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
def CreateClone(self):
return adx_volume_multiplier_strategy()