ADX 出来高ブレイクアウト戦略
ADX Volume Breakout 戦略は、ADX と出来高ブレイクアウトを中心に構築されています。
テストでは年間平均リターン約 55% を示しています。株式市場で最もよく機能します。
インジケーターがイントラデイ (5m) データ上のブレイクアウト機会を確認したときにシグナルが発動します。これにより、この手法はアクティブトレーダーに適しています。
ストップは ATR の倍数と AdxPeriod、AdxThreshold などの要素に依存します。リスクとリワードのバランスをとるためにこれらのデフォルト値を調整してください。
詳細
- エントリー条件: インジケーター条件については実装を参照してください。
- ロング/ショート: 両方。
- エグジット条件: 反対のシグナルまたはストップロジック。
- ストップ: はい、インジケーターベースの計算を使用。
- デフォルト値:
AdxPeriod = 14AdxThreshold = 25mVolumeAvgPeriod = 20VolumeThresholdFactor = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: 複数
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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()