Estrategia de Fuerza de Tendencia VWAP ADX
La estrategia VWAP ADX Trend Strength está construida en torno al VWAP con la fuerza de tendencia del ADX.
Las pruebas indican un rendimiento anual promedio de aproximadamente 46%. Funciona mejor en el mercado de acciones.
Las señales se activan cuando sus indicadores confirman cambios de tendencia 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 = 25mCandleType = 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
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trend-following strategy that trades only when ADX confirms strong directional pressure around VWAP.
/// </summary>
public class VwapWithAdxTrendStrengthStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private AverageDirectionalIndex _adx;
private VolumeWeightedMovingAverage _vwap;
private int _cooldown;
/// <summary>
/// ADX period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Minimum ADX required to trade.
/// </summary>
public decimal AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Bars to wait after each order.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public VwapWithAdxTrendStrengthStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetRange(2, 100)
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 23m)
.SetRange(1m, 100m)
.SetDisplay("ADX Threshold", "Threshold for strong trend identification", "Signals");
_cooldownBars = Param(nameof(CooldownBars), 72)
.SetRange(1, 500)
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetRange(0.5m, 10m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_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()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_adx = null;
_vwap = null;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (Security == null)
throw new InvalidOperationException("Security is not specified.");
_adx = new AverageDirectionalIndex { Length = AdxPeriod };
_vwap = new VolumeWeightedMovingAverage();
_cooldown = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_adx, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _adx);
DrawOwnTrades(area);
}
StartProtection(new Unit(0, UnitTypes.Absolute), new Unit(StopLossPercent, UnitTypes.Percent), false);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
var typedAdx = (AverageDirectionalIndexValue)adxValue;
if (typedAdx.MovingAverage is not decimal adx ||
typedAdx.Dx.Plus is not decimal diPlus ||
typedAdx.Dx.Minus is not decimal diMinus)
return;
var vwap = _vwap.Process(candle).ToDecimal();
if (!_adx.IsFormed || !_vwap.IsFormed)
return;
if (ProcessState != ProcessStates.Started)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var price = candle.ClosePrice;
var isStrongTrend = adx >= AdxThreshold;
var bullishTrend = diPlus > diMinus;
var bearishTrend = diMinus > diPlus;
var aboveVwap = price > vwap;
var belowVwap = price < vwap;
if (Position == 0)
{
if (isStrongTrend && bullishTrend && aboveVwap)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (isStrongTrend && bearishTrend && belowVwap)
{
SellMarket();
_cooldown = CooldownBars;
}
return;
}
if (Position > 0 && (!aboveVwap || adx < AdxThreshold * 0.8m || bearishTrend))
{
SellMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
else if (Position < 0 && (!belowVwap || adx < AdxThreshold * 0.8m || bullishTrend))
{
BuyMarket(Math.Abs(Position));
_cooldown = 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, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageDirectionalIndex, VolumeWeightedMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class vwap_adx_trend_strength_strategy(Strategy):
"""
Trend-following strategy that trades only when ADX confirms strong directional pressure around VWAP.
"""
def __init__(self):
super(vwap_adx_trend_strength_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetRange(2, 100) \
.SetDisplay("ADX Period", "Period for ADX calculation", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 23.0) \
.SetRange(1.0, 100.0) \
.SetDisplay("ADX Threshold", "Threshold for strong trend identification", "Signals")
self._cooldown_bars = self.Param("CooldownBars", 72) \
.SetRange(1, 500) \
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetRange(0.5, 10.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vwap_adx_trend_strength_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(vwap_adx_trend_strength_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = int(self._adx_period.Value)
self._vwap = VolumeWeightedMovingAverage()
self._cooldown = 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.DrawIndicator(area, adx)
self.DrawOwnTrades(area)
self.StartProtection(
Unit(0, UnitTypes.Absolute),
Unit(self._stop_loss_percent.Value, UnitTypes.Percent),
False
)
def _process_candle(self, candle, adx_value):
if candle.State != CandleStates.Finished:
return
adx_typed = adx_value
adx_ma = adx_typed.MovingAverage
di_plus = adx_typed.Dx.Plus
di_minus = adx_typed.Dx.Minus
if adx_ma is None or di_plus is None or di_minus is None:
return
adx_val = float(adx_ma)
di_plus_val = float(di_plus)
di_minus_val = float(di_minus)
vwap_result = self._vwap.Process(CandleIndicatorValue(self._vwap, candle))
vwap_val = float(vwap_result)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if not self._vwap.IsFormed:
return
if self._cooldown > 0:
self._cooldown -= 1
return
price = float(candle.ClosePrice)
threshold = float(self._adx_threshold.Value)
is_strong_trend = adx_val >= threshold
bullish_trend = di_plus_val > di_minus_val
bearish_trend = di_minus_val > di_plus_val
above_vwap = price > vwap_val
below_vwap = price < vwap_val
cd = int(self._cooldown_bars.Value)
if self.Position == 0:
if is_strong_trend and bullish_trend and above_vwap:
self.BuyMarket()
self._cooldown = cd
elif is_strong_trend and bearish_trend and below_vwap:
self.SellMarket()
self._cooldown = cd
return
if self.Position > 0 and (not above_vwap or adx_val < threshold * 0.8 or bearish_trend):
self.SellMarket(Math.Abs(self.Position))
self._cooldown = cd
elif self.Position < 0 and (not below_vwap or adx_val < threshold * 0.8 or bullish_trend):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown = cd
def CreateClone(self):
return vwap_adx_trend_strength_strategy()