Estratégia de Rompimento de Volume ADX
A estratégia ADX Volume Breakout é construída em torno do ADX com rompimento de volume.
Os testes indicam um retorno anual médio de aproximadamente 55%. Funciona melhor no mercado de ações.
Os sinais são acionados quando seus indicadores confirmam oportunidades de rompimento em dados intradiários (5m). Isso torna o método adequado para traders ativos.
Os stops dependem de múltiplos de ATR e fatores como AdxPeriod, AdxThreshold. Ajuste esses valores padrão para equilibrar risco e recompensa.
Detalhes
- Critérios de entrada: ver implementação para condições dos indicadores.
- Comprado/Vendido: Ambos.
- Critérios de saída: sinal oposto ou lógica de stops.
- Stops: Sim, usando cálculos baseados em indicadores.
- Valores padrão:
AdxPeriod = 14AdxThreshold = 25mVolumeAvgPeriod = 20VolumeThresholdFactor = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoria: Seguidor de tendência
- Direção: Ambos
- Indicadores: Múltiplos
- Stops: Sim
- Complexidade: Intermediário
- Período: Intradiário (5m)
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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()