VWAP ADX Trend Strength
The VWAP ADX Trend Strength strategy is built around VWAP with ADX Trend Strength.
Testing indicates an average annual return of about 46%. It performs best in the stocks market.
Signals trigger when its indicators confirms trend changes on intraday (5m) data. This makes the method suitable for active traders.
Stops rely on ATR multiples and factors like AdxPeriod, AdxThreshold. Adjust these defaults to balance risk and reward.
Details
- Entry Criteria: see implementation for indicator conditions.
- Long/Short: Both directions.
- Exit Criteria: opposite signal or stop logic.
- Stops: Yes, using indicator-based calculations.
- Default Values:
AdxPeriod = 14AdxThreshold = 25mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: multiple indicators
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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()