Williams %Rダイバージェンス戦略
Williams %Rオシレーターは買われ過ぎと売られ過ぎの状態を測定します。価格が新しい安値を付けるが%Rがより高い安値を形成する場合、または価格が新しい高値を付けるが%Rが下向きに転じる場合、モメンタムが反転する可能性があります。この戦略はインジケーターの極値でそのようなダイバージェンスを探します。
テストでは年平均リターンが約109%であることが示されています。暗号資産市場で最も良いパフォーマンスを発揮します。
毎バー、システムは最新の終値と%Rの値を記録して前回の読みと比較します。-80以下の売られ過ぎレベルと組み合わせた強気のダイバージェンスがロングエントリーを起動し、-20以上の弱気のダイバージェンスがショートを生み出します。ストップは価格のパーセントを使って設定されます。
オシレーターが反対の極値に戻った時点でポジションを決済し、ダイバージェンスシグナルからの反発を捉えます。
詳細
- エントリー条件: 価格/Williams %Rのダイバージェンスで、ロングには%Rが-80以下、ショートには-20以上。
- ロング/ショート: 両方。
- エグジット条件: Williams %Rが反対の極値に達するか、ストップロス。
- ストップ: はい、パーセントベース。
- デフォルト値:
WilliamsRPeriod= 14DivergencePeriod= 5CandleType= 5 minuteStopLossPercent= 2
- フィルター:
- カテゴリ: ダイバージェンス
- 方向: 両方
- インジケーター: Williams %R
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: はい
- リスクレベル: 中
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>
/// Compares each finished bar's close and Williams %R with the reading DivergencePeriod bars back
/// and trades the divergence in the %R extreme zone.
/// Exits at the opposite %R extreme or native actual-fill percent protection.
/// </summary>
public class WilliamsPercentRDivergenceStrategy : Strategy
{
private readonly StrategyParam<int> _williamsRPeriod;
private readonly StrategyParam<int> _divergencePeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly Queue<(decimal Close, decimal WilliamsR)> _history = new();
private WilliamsR _williamsR;
private Order _pendingOrder;
public int WilliamsRPeriod { get => _williamsRPeriod.Value; set => _williamsRPeriod.Value = value; }
public int DivergencePeriod { get => _divergencePeriod.Value; set => _divergencePeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public WilliamsPercentRDivergenceStrategy()
{
_williamsRPeriod = Param(nameof(WilliamsRPeriod), 14).SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Highest-high/lowest-low lookback", "Indicators");
_divergencePeriod = Param(nameof(DivergencePeriod), 5).SetGreaterThanZero()
.SetDisplay("Divergence Period", "Bars back to the compared close and %R reading", "Pattern");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Williams %R and divergence timeframe", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 2m).SetNotNegative()
.SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it", "Protection");
OrderRegistering += order => _pendingOrder = order;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
protected override void OnReseted()
{
base.OnReseted();
ClearState();
}
private void ClearState()
{
_history.Clear();
_williamsR = null;
_pendingOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ClearState();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
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();
}
_williamsR = new WilliamsR { Length = WilliamsRPeriod };
var candles = SubscribeCandles(CandleType);
candles.Bind(_williamsR, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, candles);
DrawIndicator(area, _williamsR);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// Native actual-fill protection evaluates executable quotes between signal candles.
}
private void ProcessCandle(ICandleMessage candle, decimal value)
{
if (candle.State != CandleStates.Finished || !_williamsR.IsFormed)
return;
var bullish = false;
var bearish = false;
if (_history.Count == DivergencePeriod)
{
var prior = _history.Peek();
bullish = candle.ClosePrice < prior.Close && value > prior.WilliamsR && value < -80m;
bearish = candle.ClosePrice > prior.Close && value < prior.WilliamsR && value > -20m;
}
_history.Enqueue((candle.ClosePrice, value));
if (_history.Count > DivergencePeriod)
_history.Dequeue();
if (!IsFormedAndOnlineAndAllowTrading() ||
_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed))
return;
if (Position > 0m && value >= -20m)
SellMarket(Position);
else if (Position < 0m && value <= -80m)
BuyMarket(Math.Abs(Position));
else if (Position == 0m)
{
if (bullish)
BuyMarket(Volume);
else if (bearish)
SellMarket(Volume);
}
}
}
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, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, OrderStates, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class williams_percent_r_divergence_strategy(Strategy):
"""Compare each finished bar's close and Williams %R with the reading DivergencePeriod bars back
and trade the divergence in the %R extreme zone."""
def __init__(self):
super(williams_percent_r_divergence_strategy, self).__init__()
self._wr_period = self.Param("WilliamsRPeriod", 14).SetGreaterThanZero().SetDisplay("Williams %R Period", "Highest-high/lowest-low lookback", "Indicators")
self._divergence_period = self.Param("DivergencePeriod", 5).SetGreaterThanZero().SetDisplay("Divergence Period", "Bars back to the compared close and %R reading", "Pattern")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Williams %R and divergence timeframe", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it", "Protection")
self._clear_state()
self.OrderRegistering += self._track_pending
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.Level1)]
def _track_pending(self, order):
self._pending_order = order
def _clear_state(self):
self._history = []
self._wr = None
self._pending_order = None
def OnReseted(self):
super(williams_percent_r_divergence_strategy, self).OnReseted()
self._clear_state()
def OnStarted2(self, time):
super(williams_percent_r_divergence_strategy, self).OnStarted2(time)
self._clear_state()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
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()
self._wr = WilliamsR()
self._wr.Length = self._wr_period.Value
candles = self.SubscribeCandles(self.candle_type)
candles.Bind(self._wr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, candles)
self.DrawIndicator(area, self._wr)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# Native actual-fill protection evaluates executable quotes between signal candles.
pass
def _process_candle(self, candle, value):
if candle.State != CandleStates.Finished or not self._wr.IsFormed:
return
bullish = False
bearish = False
period = self._divergence_period.Value
close = candle.ClosePrice
if len(self._history) == period:
prior_close, prior_wr = self._history[0]
bullish = close < prior_close and value > prior_wr and value < Decimal(-80)
bearish = close > prior_close and value < prior_wr and value > Decimal(-20)
self._history.append((close, value))
if len(self._history) > period:
self._history.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
return
if self.Position > 0 and value >= Decimal(-20):
self.SellMarket(self.Position)
elif self.Position < 0 and value <= Decimal(-80):
self.BuyMarket(Math.Abs(self.Position))
elif self.Position == 0:
if bullish:
self.BuyMarket(self.Volume)
elif bearish:
self.SellMarket(self.Volume)
def CreateClone(self):
return williams_percent_r_divergence_strategy()