ADXトレンド
平均方向指数(ADX)トレンドに基づく戦略。ADXトレンド戦略は、ADXインジケーターを使用して市場の強さを測定します。ADXが閾値を上回り、価格が移動平均の正しい側にある場合、システムはその方向に取引します。ADXが弱まるか、逆の条件が現れると、ポジションが閉じられます。
テスト結果では、年間平均リターンが約46%であることが示されています。株式市場で最も効果を発揮します。
しっかりしたADX値を待つことで、このアプローチはモメンタムが確固として確立されている時のみ取引します。ストップは通常ATRの倍数を使用し、リスクがボラティリティに応じて調整されます。
詳細
- エントリー条件: MA、ADX、ATRに基づくシグナル。
- ロング/ショート: 両方の方向。
- エグジット条件: 逆シグナルまたはストップ。
- ストップ: はい。
- デフォルト値:
AdxPeriod= 14MaPeriod= 50AtrMultiplier= 2mAdxExitThreshold= 20CandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: MA, ADX, ATR
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Average Directional Index (ADX) trend.
/// While ADX is above 25 it holds a long position when the close is above the moving average and a short one when it is below.
/// The position closes when ADX falls below the exit threshold or the close crosses the ATR stop set at entry,
/// and reverses when the opposite setup appears.
/// </summary>
public class AdxTrendStrategy : Strategy
{
/// <summary>
/// ADX level above which the trend counts as established.
/// </summary>
public const decimal AdxEntryThreshold = 25m;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _adxExitThreshold;
private readonly StrategyParam<DataType> _candleType;
private decimal _stopPrice;
/// <summary>
/// ADX period, also used for the ATR of the stop.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Moving Average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Stop distance in ATR multiples, fixed at entry.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// ADX level below which the position is closed.
/// </summary>
public int AdxExitThreshold
{
get => _adxExitThreshold.Value;
set => _adxExitThreshold.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the ADX Trend strategy.
/// </summary>
public AdxTrendStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period for the ADX and the ATR of the stop", "Indicators")
.SetOptimize(10, 30, 2);
_maPeriod = Param(nameof(MaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for calculating Moving Average", "Indicators")
.SetOptimize(20, 100, 10);
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Stop distance in entry ATR multiples", "Risk parameters")
.SetOptimize(1, 3, 0.5m);
_adxExitThreshold = Param(nameof(AdxExitThreshold), 20)
.SetRange(0, 100)
.SetDisplay("ADX Exit Threshold", "ADX level below which to exit position", "Exit parameters")
.SetOptimize(15, 25, 1);
_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();
_stopPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var atr = new AverageTrueRange { Length = AdxPeriod };
var ma = new SMA { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, atr, ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, adx);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue atrValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished || !adxValue.IsFormed || !atrValue.IsFormed || !maValue.IsFormed)
return;
if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal adx })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var ma = maValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var trending = adx > AdxEntryThreshold;
var longSetup = trending && close > ma;
var shortSetup = trending && close < ma;
if (Position > 0)
{
if (shortSetup)
{
SellMarket(Volume + Position);
_stopPrice = close + AtrMultiplier * atr;
}
else if (adx < AdxExitThreshold || close <= _stopPrice)
{
SellMarket(Position);
_stopPrice = 0m;
}
}
else if (Position < 0)
{
if (longSetup)
{
BuyMarket(Volume - Position);
_stopPrice = close - AtrMultiplier * atr;
}
else if (adx < AdxExitThreshold || close >= _stopPrice)
{
BuyMarket(-Position);
_stopPrice = 0m;
}
}
else if (longSetup)
{
BuyMarket(Volume);
_stopPrice = close - AtrMultiplier * atr;
}
else if (shortSetup)
{
SellMarket(Volume);
_stopPrice = close + AtrMultiplier * atr;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageDirectionalIndex, AverageTrueRange, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
# ADX level above which the trend counts as established.
ADX_ENTRY_THRESHOLD = Decimal(25)
class adx_trend_strategy(Strategy):
"""
Strategy based on Average Directional Index (ADX) trend.
While ADX is above 25 it holds a long position when the close is above the moving average and a short one when it is below.
The position closes when ADX falls below the exit threshold or the close crosses the ATR stop set at entry,
and reverses when the opposite setup appears.
"""
def __init__(self):
super(adx_trend_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ADX Period", "Period for the ADX and the ATR of the stop", "Indicators") \
.SetOptimize(10, 30, 2)
self._ma_period = self.Param("MaPeriod", 50) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Period for calculating Moving Average", "Indicators") \
.SetOptimize(20, 100, 10)
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("ATR Multiplier", "Stop distance in entry ATR multiples", "Risk parameters") \
.SetOptimize(1.0, 3.0, 0.5)
self._adx_exit_threshold = self.Param("AdxExitThreshold", 20) \
.SetRange(0, 100) \
.SetDisplay("ADX Exit Threshold", "ADX level below which to exit position", "Exit parameters") \
.SetOptimize(15, 25, 1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._stop_price = Decimal(0)
def OnReseted(self):
super(adx_trend_strategy, self).OnReseted()
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(adx_trend_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
atr = AverageTrueRange()
atr.Length = self._adx_period.Value
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.BindEx(adx, atr, ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, adx)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, adx_value, atr_value, ma_value):
if candle.State != CandleStates.Finished or not adx_value.IsFormed or not atr_value.IsFormed or not ma_value.IsFormed:
return
if adx_value.MovingAverage is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
adx = adx_value.MovingAverage
close = candle.ClosePrice
ma = ma_value.GetValue[Decimal](None)
atr = atr_value.GetValue[Decimal](None)
offset = Decimal(self._atr_multiplier.Value) * atr
exit_level = Decimal(self._adx_exit_threshold.Value)
trending = adx > ADX_ENTRY_THRESHOLD
long_setup = trending and close > ma
short_setup = trending and close < ma
position = self.Position
if position > 0:
if short_setup:
self.SellMarket(self.Volume + position)
self._stop_price = close + offset
elif adx < exit_level or close <= self._stop_price:
self.SellMarket(position)
self._stop_price = Decimal(0)
elif position < 0:
if long_setup:
self.BuyMarket(self.Volume - position)
self._stop_price = close - offset
elif adx < exit_level or close >= self._stop_price:
self.BuyMarket(-position)
self._stop_price = Decimal(0)
elif long_setup:
self.BuyMarket(self.Volume)
self._stop_price = close - offset
elif short_setup:
self.SellMarket(self.Volume)
self._stop_price = close + offset
def CreateClone(self):
return adx_trend_strategy()