Estrategia de Ruptura de Volumen ADX
La estrategia ADX Volume Breakout está construida en torno al ADX con ruptura de volumen.
Las pruebas indican un rendimiento anual promedio de aproximadamente 55%. Funciona mejor en el mercado de acciones.
Las señales se activan cuando sus indicadores confirman oportunidades de ruptura en datos intradía (5m). Esto hace que el método sea adecuado para traders activos.
Los stops dependen de múltiplos de ATR y factores como AdxPeriod, AdxThreshold. Ajuste estos valores predeterminados para equilibrar el riesgo y la recompensa.
Detalles
- Criterios de entrada: ver implementación para condiciones de indicadores.
- Largo/Corto: Ambos.
- Criterios de salida: señal opuesta o lógica de stops.
- Stops: Sí, usando cálculos basados en indicadores.
- Valores predeterminados:
AdxPeriod = 14AdxThreshold = 25mVolumeAvgPeriod = 20VolumeThresholdFactor = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: Múltiples
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía (5m)
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Strategy based on ADX with a volume breakout confirmation.
/// </summary>
public class AdxWithVolumeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _volumeAvgPeriod;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeSma;
private int _cooldownRemaining;
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
public int VolumeAvgPeriod
{
get => _volumeAvgPeriod.Value;
set => _volumeAvgPeriod.Value = value;
}
public int SignalCooldownBars
{
get => _signalCooldownBars.Value;
set => _signalCooldownBars.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public AdxWithVolumeBreakoutStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 10)
.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), 10)
.SetGreaterThanZero()
.SetDisplay("Volume Avg Period", "Period for volume moving average", "Indicators");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 15)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between signals", "Trading");
_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;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
_volumeSma = new SimpleMovingAverage { Length = VolumeAvgPeriod };
_cooldownRemaining = 0;
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(adx, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!adxValue.IsFinal)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
// Process volume average
var volumeAvgValue = _volumeSma.Process(new DecimalIndicatorValue(_volumeSma, candle.TotalVolume, candle.ServerTime));
var adxTyped = (AverageDirectionalIndexValue)adxValue;
if (adxTyped.MovingAverage is not decimal adx)
return;
var dx = adxTyped.Dx;
if (dx.Plus is not decimal plusDi || dx.Minus is not decimal minusDi)
return;
var volumeAverage = volumeAvgValue.IsFormed ? volumeAvgValue.ToDecimal() : 0m;
var isStrongTrend = adx > AdxThreshold;
var isVolumeBreakout = volumeAverage <= 0m || candle.TotalVolume >= volumeAverage;
var isBullish = plusDi > minusDi;
var isBearish = minusDi > plusDi;
if (_cooldownRemaining > 0)
return;
if (!isStrongTrend || !isVolumeBreakout)
return;
if (Position == 0)
{
if (isBullish)
{
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
else if (isBearish)
{
SellMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
}
}
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 AverageDirectionalIndex, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class adx_with_volume_breakout_strategy(Strategy):
"""
Strategy based on ADX with a volume breakout confirmation.
"""
def __init__(self):
super(adx_with_volume_breakout_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 10) \
.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", 10) \
.SetGreaterThanZero() \
.SetDisplay("Volume Avg Period", "Period for volume moving average", "Indicators")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 15) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown", "Bars to wait between signals", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adx_with_volume_breakout_strategy, self).OnReseted()
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(adx_with_volume_breakout_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = int(self._adx_period.Value)
self._volume_sma = SimpleMovingAverage()
self._volume_sma.Length = int(self._volume_avg_period.Value)
self._cooldown_remaining = 0
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(1, UnitTypes.Percent)
)
def _process_candle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
if not adx_value.IsFinal:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
volume_avg_result = process_float(self._volume_sma, candle.TotalVolume, candle.ServerTime, True)
adx_typed = adx_value
adx_ma = adx_typed.MovingAverage
if adx_ma is None:
return
dx = adx_typed.Dx
if dx is None:
return
plus_di = dx.Plus
minus_di = dx.Minus
if plus_di is None or minus_di is None:
return
adx_val = float(adx_ma)
plus_di_val = float(plus_di)
minus_di_val = float(minus_di)
volume_average = float(volume_avg_result) if volume_avg_result.IsFormed else 0.0
threshold = float(self._adx_threshold.Value)
is_strong_trend = adx_val > threshold
is_volume_breakout = volume_average <= 0.0 or float(candle.TotalVolume) >= volume_average
is_bullish = plus_di_val > minus_di_val
is_bearish = minus_di_val > plus_di_val
if self._cooldown_remaining > 0:
return
if not is_strong_trend or not is_volume_breakout:
return
cd = int(self._signal_cooldown_bars.Value)
if self.Position == 0:
if is_bullish:
self.BuyMarket()
self._cooldown_remaining = cd
elif is_bearish:
self.SellMarket()
self._cooldown_remaining = cd
def CreateClone(self):
return adx_with_volume_breakout_strategy()