ADX Volumen-Ausbruch-Strategie
Die ADX Volume Breakout-Strategie basiert auf ADX mit Volumen-Ausbruch.
Tests zeigen eine durchschnittliche Jahresrendite von etwa 55%. Sie funktioniert am besten im Aktienmarkt.
Signale werden ausgelöst, wenn die Indikatoren Ausbruchsmöglichkeiten auf Intraday-Daten (5m) bestätigen. Dies macht die Methode für aktive Trader geeignet.
Stops basieren auf ATR-Vielfachen und Faktoren wie AdxPeriod, AdxThreshold. Passen Sie diese Standardwerte an, um Risiko und Ertrag auszubalancieren.
Details
- Einstiegskriterien: siehe Implementierung für Indikatorbedingungen.
- Long/Short: Beide Richtungen.
- Ausstiegskriterien: entgegengesetztes Signal oder Stop-Logik.
- Stops: Ja, unter Verwendung indikatorbasierter Berechnungen.
- Standardwerte:
AdxPeriod = 14AdxThreshold = 25mVolumeAvgPeriod = 20VolumeThresholdFactor = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Mehrere
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday (5m)
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// ADX trend strength with a volume breakout confirmation.
/// Enters in the direction of the dominant directional index when ADX is above <see cref="AdxThreshold"/>
/// and volume exceeds its average by <see cref="VolumeThresholdFactor"/>. The opposite signal reverses the position.
/// </summary>
public class AdxWithVolumeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _volumeAvgPeriod;
private readonly StrategyParam<decimal> _volumeThresholdFactor;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeSma;
/// <summary>
/// ADX period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Minimum ADX value for a strong trend.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Period of the volume average.
/// </summary>
public int VolumeAvgPeriod
{
get => _volumeAvgPeriod.Value;
set => _volumeAvgPeriod.Value = value;
}
/// <summary>
/// Volume must exceed its average multiplied by this factor.
/// </summary>
public decimal VolumeThresholdFactor
{
get => _volumeThresholdFactor.Value;
set => _volumeThresholdFactor.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="AdxWithVolumeBreakoutStrategy"/>.
/// </summary>
public AdxWithVolumeBreakoutStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 25m)
.SetGreaterThanZero()
.SetDisplay("ADX Threshold", "Threshold for strong trend identification", "Indicators");
_volumeAvgPeriod = Param(nameof(VolumeAvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Volume Avg Period", "Period for volume moving average", "Indicators");
_volumeThresholdFactor = Param(nameof(VolumeThresholdFactor), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Volume Factor", "Volume must exceed its average by this factor", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_volumeSma = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
_volumeSma = new SimpleMovingAverage { Length = VolumeAvgPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var adxArea = CreateChartArea();
if (adxArea != null)
DrawIndicator(adxArea, adx);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
// The average is taken over previous bars so the current bar's volume is compared with history.
var volumeAverage = _volumeSma.GetCurrentValue();
var volumeFormed = _volumeSma.IsFormed;
_volumeSma.Process(candle.TotalVolume, candle.ServerTime, true);
if (!volumeFormed || !adxValue.IsFormed)
return;
var adxTyped = (AverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adx)
return;
if (adxTyped.Dx.Plus is not decimal plusDi || adxTyped.Dx.Minus is not decimal minusDi)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (adx <= AdxThreshold || candle.TotalVolume <= volumeAverage * VolumeThresholdFactor)
return;
if (plusDi > minusDi && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (minusDi > plusDi && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
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 AverageDirectionalIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class adx_with_volume_breakout_strategy(Strategy):
"""
ADX trend strength with a volume breakout confirmation.
Enters in the direction of the dominant directional index when ADX is above AdxThreshold
and volume exceeds its average by VolumeThresholdFactor. The opposite signal reverses the position.
"""
def __init__(self):
super(adx_with_volume_breakout_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 25.0) \
.SetGreaterThanZero() \
.SetDisplay("ADX Threshold", "Threshold for strong trend identification", "Indicators")
self._volume_avg_period = self.Param("VolumeAvgPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Volume Avg Period", "Period for volume moving average", "Indicators")
self._volume_threshold_factor = self.Param("VolumeThresholdFactor", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Volume Factor", "Volume must exceed its average by this factor", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_sma = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(adx_with_volume_breakout_strategy, self).OnReseted()
self._volume_sma = None
def OnStarted2(self, time):
super(adx_with_volume_breakout_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
self._volume_sma = SimpleMovingAverage()
self._volume_sma.Length = self._volume_avg_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(adx, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
adx_area = self.CreateChartArea()
if adx_area is not None:
self.DrawIndicator(adx_area, adx)
def _process_candle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
# The average is taken over previous bars so the current bar's volume is compared with history.
volume_average = float(get_current_value(self._volume_sma))
volume_formed = self._volume_sma.IsFormed
process_float(self._volume_sma, candle.TotalVolume, candle.ServerTime, True)
if not volume_formed or not adx_value.IsFormed:
return
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
dx = adx_value.Dx
if dx.Plus is None or dx.Minus is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
adx = float(adx_ma)
plus_di = float(dx.Plus)
minus_di = float(dx.Minus)
volume = float(candle.TotalVolume)
if adx <= float(self._adx_threshold.Value) or \
volume <= volume_average * float(self._volume_threshold_factor.Value):
return
if plus_di > minus_di and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif minus_di > plus_di and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return adx_with_volume_breakout_strategy()