Williams R Momentum
Williams R Momentum 策略基于 Williams %R with Momentum filter。
当 Williams confirms momentum shifts 在日内(5m)数据上得到确认时触发信号,适合积极交易者。
止损依赖于 ATR 倍数以及 WilliamsRPeriod, MomentumPeriod 等参数,可根据需要调整以平衡风险与收益。
详情
- 入场条件:参见指标条件实现.
- 多空方向:双向.
- 退出条件:反向信号或止损逻辑.
- 止损:是,基于指标计算.
- 默认值:
WilliamsRPeriod = 14MomentumPeriod = 14WilliamsROversold = -80mWilliamsROverbought = -20mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- 过滤器:
- 分类: 趋势跟随
- 方向: 双向
- 指标: Williams, R
- 止损: 是
- 复杂度: 中等
- 时间框架: 日内 (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 Williams %R with Momentum filter.
/// </summary>
public class WilliamsPercentRWithMomentumStrategy : Strategy
{
private readonly StrategyParam<int> _williamsRPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<decimal> _williamsROversold;
private readonly StrategyParam<decimal> _williamsROverbought;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _williamsR;
private Momentum _momentum;
private SimpleMovingAverage _momentumSma;
/// <summary>
/// Williams %R period parameter.
/// </summary>
public int WilliamsRPeriod
{
get => _williamsRPeriod.Value;
set => _williamsRPeriod.Value = value;
}
/// <summary>
/// Momentum period parameter.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Williams %R oversold level parameter.
/// </summary>
public decimal WilliamsROversold
{
get => _williamsROversold.Value;
set => _williamsROversold.Value = value;
}
/// <summary>
/// Williams %R overbought level parameter.
/// </summary>
public decimal WilliamsROverbought
{
get => _williamsROverbought.Value;
set => _williamsROverbought.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public WilliamsPercentRWithMomentumStrategy()
{
_williamsRPeriod = Param(nameof(WilliamsRPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Period for Williams %R calculation", "Indicators")
.SetOptimize(5, 30, 5);
_momentumPeriod = Param(nameof(MomentumPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Period for Momentum calculation", "Indicators")
.SetOptimize(5, 30, 5);
_williamsROversold = Param(nameof(WilliamsROversold), -80m)
.SetDisplay("Williams %R Oversold", "Williams %R oversold level", "Indicators")
.SetOptimize(-90, -70, 5);
_williamsROverbought = Param(nameof(WilliamsROverbought), -20m)
.SetDisplay("Williams %R Overbought", "Williams %R overbought level", "Indicators")
.SetOptimize(-30, -10, 5);
_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();
_williamsR?.Reset();
_momentum?.Reset();
_momentumSma?.Reset();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_williamsR = new WilliamsR { Length = WilliamsRPeriod };
_momentum = new Momentum { Length = MomentumPeriod };
_momentumSma = new SMA { Length = MomentumPeriod };
// Subscribe to candles and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_williamsR, _momentum, (candle, williamsRValue, momentumValue) =>
{
// Calculate momentum average
var momentumAvg = _momentumSma.Process(new DecimalIndicatorValue(_momentumSma, momentumValue, candle.ServerTime)).ToDecimal();
// Process the strategy logic
ProcessStrategy(candle, williamsRValue, momentumValue, momentumAvg);
})
.Start();
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _williamsR);
DrawIndicator(area, _momentum);
DrawOwnTrades(area);
}
// Setup position protection
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessStrategy(ICandleMessage candle, decimal williamsRValue, decimal momentumValue, decimal momentumAvg)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready for trading
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Check momentum - rising or falling
var isMomentumRising = momentumValue > momentumAvg;
// Trading logic
if (williamsRValue < WilliamsROversold && isMomentumRising && Position <= 0)
{
// Williams %R oversold with rising momentum - Go long
CancelActiveOrders();
// Calculate position size
var volume = Volume + Math.Abs(Position);
// Enter long position
BuyMarket(volume);
}
else if (williamsRValue > WilliamsROverbought && !isMomentumRising && Position >= 0)
{
// Williams %R overbought with falling momentum - Go short
CancelActiveOrders();
// Calculate position size
var volume = Volume + Math.Abs(Position);
// Enter short position
SellMarket(volume);
}
// Exit logic - when Williams %R crosses the middle (-50) level
if ((Position > 0 && williamsRValue > -50) || (Position < 0 && williamsRValue < -50))
{
// Close position
ClosePosition();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import WilliamsR, Momentum, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class williams_percent_r_with_momentum_strategy(Strategy):
"""
Strategy based on Williams %R with Momentum filter.
"""
def __init__(self):
super(williams_percent_r_with_momentum_strategy, self).__init__()
self._williams_r_period = self.Param("WilliamsRPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Williams %R Period", "Period for Williams %R calculation", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Momentum Period", "Period for Momentum calculation", "Indicators")
self._williams_r_oversold = self.Param("WilliamsROversold", -80.0) \
.SetDisplay("Williams %R Oversold", "Williams %R oversold level", "Indicators")
self._williams_r_overbought = self.Param("WilliamsROverbought", -20.0) \
.SetDisplay("Williams %R Overbought", "Williams %R overbought level", "Indicators")
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 OnReseted(self):
super(williams_percent_r_with_momentum_strategy, self).OnReseted()
def OnStarted2(self, time):
super(williams_percent_r_with_momentum_strategy, self).OnStarted2(time)
williams_r = WilliamsR()
williams_r.Length = int(self._williams_r_period.Value)
momentum = Momentum()
momentum.Length = int(self._momentum_period.Value)
self._momentum_sma = SimpleMovingAverage()
self._momentum_sma.Length = int(self._momentum_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(williams_r, momentum, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, williams_r)
self.DrawIndicator(area, momentum)
self.DrawOwnTrades(area)
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(1, UnitTypes.Percent)
)
def _process_candle(self, candle, williams_r_value, momentum_value):
momentum_avg = float(process_float(self._momentum_sma, Decimal(float(momentum_value)), candle.ServerTime, True))
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
wr = float(williams_r_value)
mom = float(momentum_value)
is_momentum_rising = mom > momentum_avg
oversold = float(self._williams_r_oversold.Value)
overbought = float(self._williams_r_overbought.Value)
if wr < oversold and is_momentum_rising and self.Position <= 0:
volume = self.Volume + Math.Abs(self.Position)
self.BuyMarket(volume)
elif wr > overbought and not is_momentum_rising and self.Position >= 0:
volume = self.Volume + Math.Abs(self.Position)
self.SellMarket(volume)
if self.Position > 0 and wr > -50:
self.SellMarket(Math.Abs(self.Position))
elif self.Position < 0 and wr < -50:
self.BuyMarket(Math.Abs(self.Position))
def CreateClone(self):
return williams_percent_r_with_momentum_strategy()