Vwap Adx 戦略
VWAPとADXインジケーターに基づく戦略。価格がVWAPより上でADX > 25のときロングエントリー。価格がVWAPより下でADX > 25のときショートエントリー。ADX < 20のときに決済。
テストでは年平均リターン約157%を示しています。暗号資産市場で最もパフォーマンスが高くなります。
VWAPがセッションのベンチマークとして機能し、ADXが確信度を測定します。価格がVWAPから乖離しADXが強さを示したときにエントリーが現れます。
イントラデイトレンドトレーダーに適しています。保護ストップはATRの倍数を使用します。
詳細
- エントリー条件:
- ロング:
Close > VWAP && ADX > 25 - ショート:
Close < VWAP && ADX > 25
- ロング:
- ロング/ショート: 両方
- エグジット条件: ADXが閾値を下回る
- ストップ:
StopLossPercentを使用したパーセントベース - デフォルト値:
StopLossPercent= 2mAdxPeriod= 14CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- フィルター:
- カテゴリ: 平均回帰
- 方向: 両方
- インジケーター: VWAP, ADX
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 中期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on VWAP and ADX indicators.
/// Enters long when price is above VWAP and ADX > 25.
/// Enters short when price is below VWAP and ADX > 25.
/// Exits when ADX < 20.
/// </summary>
public class VwapAdxStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private AverageDirectionalIndex _adx;
private VolumeWeightedMovingAverage _vwap;
private decimal _prevAdxValue;
private int _cooldown;
/// <summary>
/// Stop loss percentage value.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// ADX indicator period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="VwapAdxStrategy"/>.
/// </summary>
public VwapAdxStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop loss (%)", "Stop loss percentage from entry price", "Risk Management");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "Period for Average Directional Movement Index", "Indicators")
.SetOptimize(10, 20, 1);
_cooldownBars = Param(nameof(CooldownBars), 25)
.SetRange(1, 200)
.SetDisplay("Cooldown Bars", "Bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe of data for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security, DataType)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevAdxValue = default;
_cooldown = 0;
_adx = null;
_vwap = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create ADX indicator
_adx = new() { Length = AdxPeriod };
_vwap = new() { Length = AdxPeriod };
var dummyEma = new ExponentialMovingAverage { Length = 10 };
// Create subscription and subscribe to VWAP
var subscription = SubscribeCandles(CandleType);
// Process candles with ADX + dummy EMA (BindEx needs 2+ indicators)
subscription
.BindEx(_adx, dummyEma, ProcessCandle)
.Start();
// Setup chart visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _adx);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue dummyValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
decimal vwap;
try
{
var vwapValue = _vwap.Process(candle);
if (vwapValue == null || !_vwap.IsFormed)
return;
vwap = vwapValue.ToDecimal();
}
catch (IndexOutOfRangeException)
{
return;
}
// Get current ADX value
var typedAdx = (AverageDirectionalIndexValue)adxValue;
if (typedAdx.MovingAverage is not decimal currentAdxValue)
return;
// Trading logic
if (_cooldown > 0)
{
_cooldown--;
}
var adxImpulseUp = _prevAdxValue <= 25 && currentAdxValue > 25;
if (_cooldown == 0 && adxImpulseUp)
{
if (candle.ClosePrice > vwap * 1.001m && Position <= 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (candle.ClosePrice < vwap * 0.999m && Position >= 0)
{
SellMarket();
_cooldown = CooldownBars;
}
}
else if (currentAdxValue < 18 && Position != 0)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
_cooldown = CooldownBars;
}
// Store current ADX value for next candle
_prevAdxValue = currentAdxValue;
}
}
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, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import AverageDirectionalIndex, VolumeWeightedMovingAverage, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class vwap_adx_strategy(Strategy):
"""
Strategy based on VWAP and ADX indicators.
Enters long when ADX crosses above 25 and price above VWAP.
Enters short when ADX crosses above 25 and price below VWAP.
Exits when ADX < 18.
"""
def __init__(self):
super(vwap_adx_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop loss (%)", "Stop loss percentage from entry price", "Risk Management")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Period for Average Directional Movement Index", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 25) \
.SetRange(1, 200) \
.SetDisplay("Cooldown Bars", "Bars between entries", "General")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Timeframe of data for strategy", "General")
self._prev_adx_value = 0.0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(vwap_adx_strategy, self).OnStarted2(time)
self._prev_adx_value = 0.0
self._cooldown = 0
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
vwap = VolumeWeightedMovingAverage()
vwap.Length = self._adx_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(adx, vwap, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, adx)
self.DrawOwnTrades(area)
def OnReseted(self):
super(vwap_adx_strategy, self).OnReseted()
self._prev_adx_value = 0.0
self._cooldown = 0
def ProcessCandle(self, candle, adx_value, vwap_value):
if candle.State != CandleStates.Finished:
return
# Get VWAP value
vwap = float(vwap_value)
# Get current ADX value
adx_ma = adx_value.MovingAverage
if adx_ma is None:
return
current_adx_value = float(adx_ma)
if self._cooldown > 0:
self._cooldown -= 1
adx_impulse_up = self._prev_adx_value <= 25 and current_adx_value > 25
cooldown_val = int(self._cooldown_bars.Value)
price = float(candle.ClosePrice)
if self._cooldown == 0 and adx_impulse_up:
if price > vwap * 1.001 and self.Position <= 0:
self.BuyMarket()
self._cooldown = cooldown_val
elif price < vwap * 0.999 and self.Position >= 0:
self.SellMarket()
self._cooldown = cooldown_val
elif current_adx_value < 18 and self.Position != 0:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._cooldown = cooldown_val
self._prev_adx_value = current_adx_value
def CreateClone(self):
return vwap_adx_strategy()