ADX Donchian 戦略
この戦略はADX Donchianインジケーターを使用してシグナルを生成します。 ADX > AdxThreshold かつ Price >= upperBorder(強いトレンドと上方ブレイクアウト)の場合にロングエントリー。ADX > AdxThreshold かつ Price <= lowerBorder(強いトレンドと下方ブレイクアウト)の場合にショートエントリー。 混合市場で機会を求めるトレーダーに適しています。
テストでは年間平均リターン約67%を示しています。株式市場で最もパフォーマンスが高いです。
詳細
- エントリー条件:
- ロング: ADX > AdxThreshold && Price >= upperBorder (strong trend with breakout up)
- ショート: ADX > AdxThreshold && Price <= lowerBorder (strong trend with breakout down)
- ロング/ショート: 両方。
- エグジット条件:
- ロング: ADXが(threshold - 5)を下回ったときポジションを終了
- ショート: ADXが(threshold - 5)を下回ったときポジションを終了
- ストップ: はい。
- デフォルト値:
AdxPeriod= 14DonchianPeriod= 5StopLossPercent= 2CandleType= TimeSpan.FromMinutes(5)AdxThreshold= 10Multiplier= 0.1 upperBorderは現在の足を含む直近DonchianPeriod本の最高値をMultiplierパーセント下げた値、lowerBorderは最安値をMultiplierパーセント上げた値です。ストップはエントリー価格の固定StopLossPercentで、ローソク足の間も監視されます。 保有ポジションと逆方向のエントリーシグナルはドテンになります。
- フィルター:
- カテゴリ: 混合
- 方向: 両方
- インジケーター: ADX Donchian
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ADX Donchian strategy.
/// The borders sit Multiplier percent inside the DonchianPeriod channel, which includes the current candle. With ADX above AdxThreshold
/// a close at or above the upper border goes long and one at or below the lower border goes short, reversing an opposite position.
/// The position closes once ADX falls below AdxThreshold minus 5, and a percent stop limits the loss.
/// </summary>
public class AdxDonchianStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _donchianPeriod;
private readonly StrategyParam<int> _adxThreshold;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of ADX.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Candles the channel spans.
/// </summary>
public int DonchianPeriod
{
get => _donchianPeriod.Value;
set => _donchianPeriod.Value = value;
}
/// <summary>
/// ADX value for strong trend detection.
/// </summary>
public int AdxThreshold
{
get => _adxThreshold.Value;
set => _adxThreshold.Value = value;
}
/// <summary>
/// Percent the borders sit inside the channel.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </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>
/// Constructor.
/// </summary>
public AdxDonchianStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Period of ADX", "Indicators");
_donchianPeriod = Param(nameof(DonchianPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Donchian Period", "Candles the channel spans", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 10)
.SetGreaterThanZero()
.SetDisplay("ADX Threshold", "ADX value for strong trend detection", "Indicators");
_multiplier = Param(nameof(Multiplier), 0.1m)
.SetNotNegative()
.SetDisplay("Multiplier", "Percent the borders sit inside the channel", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "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()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var adx = new AverageDirectionalIndex { Length = AdxPeriod };
var donchian = new DonchianChannels { Length = DonchianPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(adx, donchian, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, donchian);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, adx);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue adxValue, IIndicatorValue donchianValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!adxValue.IsFormed || !donchianValue.IsFormed)
return;
if (adxValue is not AverageDirectionalIndexValue { MovingAverage: decimal strength })
return;
if (donchianValue is not IDonchianChannelsValue { UpperBand: decimal upper, LowerBand: decimal lower })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var upperBorder = upper * (1 - Multiplier / 100);
var lowerBorder = lower * (1 + Multiplier / 100);
var strong = strength > AdxThreshold;
var close = candle.ClosePrice;
if (strong && close >= upperBorder && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (strong && close <= lowerBorder && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && strength < AdxThreshold - 5)
SellMarket(Position);
else if (Position < 0 && strength < AdxThreshold - 5)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import AverageDirectionalIndex, DonchianChannels
from StockSharp.Algo.Strategies import Strategy
class adx_donchian_strategy(Strategy):
"""
ADX Donchian strategy.
The borders sit Multiplier percent inside the DonchianPeriod channel, which includes the current candle. With ADX above AdxThreshold
a close at or above the upper border goes long and one at or below the lower border goes short, reversing an opposite position.
The position closes once ADX falls below AdxThreshold minus 5, and a percent stop limits the loss.
"""
def __init__(self):
super(adx_donchian_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14).SetGreaterThanZero().SetDisplay("ADX Period", "Period of ADX", "Indicators")
self._donchian_period = self.Param("DonchianPeriod", 5).SetGreaterThanZero().SetDisplay("Donchian Period", "Candles the channel spans", "Indicators")
self._adx_threshold = self.Param("AdxThreshold", 10).SetGreaterThanZero().SetDisplay("ADX Threshold", "ADX value for strong trend detection", "Indicators")
self._multiplier = self.Param("Multiplier", 0.1).SetNotNegative().SetDisplay("Multiplier", "Percent the borders sit inside the channel", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(adx_donchian_strategy, self).OnStarted2(time)
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
donchian = DonchianChannels()
donchian.Length = self._donchian_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(adx, donchian, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, donchian)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, adx)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, adx_value, donchian_value):
if candle.State != CandleStates.Finished:
return
if not adx_value.IsFormed or not donchian_value.IsFormed:
return
if adx_value.MovingAverage is None or donchian_value.UpperBand is None or donchian_value.LowerBand is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
strength = adx_value.MovingAverage
threshold = Decimal(self._adx_threshold.Value)
shift = Decimal(self._multiplier.Value) / Decimal(100)
upper_border = donchian_value.UpperBand * (Decimal(1) - shift)
lower_border = donchian_value.LowerBand * (Decimal(1) + shift)
strong = strength > threshold
weak = strength < threshold - Decimal(5)
close = candle.ClosePrice
if strong and close >= upper_border and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif strong and close <= lower_border and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and weak:
self.SellMarket(self.Position)
elif self.Position < 0 and weak:
self.BuyMarket(-self.Position)
def CreateClone(self):
return adx_donchian_strategy()