首页
/
策略示例
在 GitHub 上查看
Williams Percent Directional Index 策略
概述
Williams Percent Directional Index 策略 在 StockSharp 高级 API 中重现了 MetaTrader 5 专家顾问“Mt5 Williams % Directional Index EA”。策略以 Williams %R 动量曲线与平均方向指数(ADX)交叉为入场触发点,并使用资金流量指数(MFI)与随机指标确认离场。程序只处理已经收盘的K线,并通过指标绑定获取最新的计算结果。
交易逻辑
趋势确认
多头:比较之前两根已收盘K线的 Williams %R 数值,如果出现上升斜率(较早的数值大于较新的数值),视为看涨动能。
空头:同样比较两根K线,如果斜率向下(较早的数值小于较新的数值),视为看跌动能。
ADX 的方向性差值(+DI - -DI)必须在上一根K线上穿或下穿零轴。负值到正值的过渡支持做多,正值到负值的过渡支持做空。
开仓规则
当 Williams %R 与 ADX 同时给出多头信号,并且当前没有多单时,立即以市价买入。
当两个指标同时给出空头信号,并且当前没有空单时,立即以市价卖出。
如果两个方向同时满足(极少出现),策略会跳过该信号以避免互相矛盾的指令。
平仓规则
多单在以下任一条件满足时离场:两根K线前的 MFI 数值高于超买阈值,或随机指标 %K 出现局部底部形态(K[-2] > K[-1] < K[0])。
空单在以下任一条件满足时离场:两根K线前的 MFI 数值低于对称的超卖水平(100 - 阈值),或随机指标 %K 出现局部顶部形态(K[-2] < K[-1] > K[0])。
风险控制
本转换保留了原策略的信号逻辑,但未实现 MQL 版本中的止损、拖尾或交易会话管理。可根据需要在 StockSharp 中添加 StartProtection 或其他风险模块。
参数
名称
说明
默认值
Candle Type
所有指标使用的K线周期。
15 分钟
Williams %R Period
Williams %R 的回溯周期。
42
Directional Period
ADX(含 +DI/-DI)使用的周期长度。
20
MFI Period
资金流量指数的周期长度。
19
MFI Level
触发平仓的超买阈值,对应超卖阈值为 100 - 该值。
79
Stochastic %K
随机指标 %K 的周期。
22
Stochastic %D
随机指标 %D 的周期。
16
Stochastic Smoothing
随机指标的平滑(减速)周期。
21
全部参数均通过 StrategyParam 暴露,可在 UI 中直接调整或用于优化。
使用说明
启动前需绑定交易品种并设置合适的下单手数(Volume)。
仅处理 CandleStates.Finished 的K线,确保全部指标数值已经最终确定。
若系统支持图表,策略会同时绘制价格、Williams %R、ADX、MFI、随机指标以及成交记录。
若需完全复制 MT5 EA 的风控特性,请自行在 StockSharp 中增加保护措施或自定义管理逻辑。
与原始 MQL 版本的差异
通过高层 BindEx 接口直接获取指标数值,不再使用缓冲区复制,但核心的零轴穿越与多K线形态判断完全遵循原策略。
会话过滤、下单重试和拖尾止损等管理代码未迁移,重点放在信号转换部分,方便后续根据需要添加。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Williams %R Directional Index strategy: Williams %R crossover.
/// Buys when Williams %R crosses above -80, sells when crosses below -20.
/// </summary>
public class WilliamsPercentDirectionalIndexStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevWr;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public WilliamsPercentDirectionalIndexStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 14)
.SetGreaterThanZero()
.SetDisplay("Period", "Williams %R period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevWr = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevWr = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var wr = new RelativeStrengthIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(wr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal wrValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevWr < 35 && wrValue >= 35 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevWr > 65 && wrValue <= 65 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevWr = wrValue;
_hasPrev = true;
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class williams_percent_directional_index_strategy(Strategy):
def __init__(self):
super(williams_percent_directional_index_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 14)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_wr = 0.0
self._candles_since_trade = 4
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(williams_percent_directional_index_strategy, self).OnReseted()
self._prev_wr = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
def OnStarted2(self, time):
super(williams_percent_directional_index_strategy, self).OnStarted2(time)
self._prev_wr = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, wr_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
wr_val = float(wr_value)
if self._has_prev:
if self._prev_wr < 35 and wr_val >= 35 and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif self._prev_wr > 65 and wr_val <= 65 and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_wr = wr_val
self._has_prev = True
def CreateClone(self):
return williams_percent_directional_index_strategy()