WPR Level Cross Strategy
This strategy trades based on the Williams %R oscillator crossing predefined overbought and oversold levels.
When the indicator crosses below the Low Level, it signals a potential reversal from an oversold condition. When it crosses above the High Level, it indicates a possible reversal from an overbought condition. Depending on the selected Trend Mode, the strategy can trade in the direction of the indicator or invert the signals for counter-trend trading.
Parameters
WprPeriod– lookback period for Williams %R.HighLevel– overbought threshold.LowLevel– oversold threshold.Trend–Directtrades with indicator signals,Againstinverts them.EnableBuyEntry/EnableSellEntry– allow entering long/short positions.EnableBuyExit/EnableSellExit– allow closing short/long positions.StopLoss– stop-loss value in price units.TakeProfit– take-profit value in price units.CandleType– timeframe of candles used for calculations.
How It Works
- The strategy subscribes to candles and calculates the Williams %R indicator.
- On each finished candle it checks if the indicator crossed the specified levels.
- Depending on
Trendand enabled actions it opens or closes positions using market orders. - Optional stop-loss and take-profit protection is enabled through
StartProtection.
Notes
- Comments in code are provided in English.
- Only the C# version is implemented; Python version is intentionally omitted.
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 indicator crossing specified levels.
/// </summary>
public class WprLevelCrossStrategy : Strategy
{
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<TrendModes> _trend;
private readonly StrategyParam<bool> _enableBuyEntry;
private readonly StrategyParam<bool> _enableSellEntry;
private readonly StrategyParam<bool> _enableBuyExit;
private readonly StrategyParam<bool> _enableSellExit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevWr;
/// <summary>
/// Lookback period for Williams %R.
/// </summary>
public int WprPeriod
{
get => _wprPeriod.Value;
set => _wprPeriod.Value = value;
}
/// <summary>
/// Upper threshold to detect overbought levels.
/// </summary>
public decimal HighLevel
{
get => _highLevel.Value;
set => _highLevel.Value = value;
}
/// <summary>
/// Lower threshold to detect oversold levels.
/// </summary>
public decimal LowLevel
{
get => _lowLevel.Value;
set => _lowLevel.Value = value;
}
/// <summary>
/// Trend mode: Direct trades with indicator, Against inverts signals.
/// </summary>
public TrendModes Trend
{
get => _trend.Value;
set => _trend.Value = value;
}
/// <summary>
/// Enable opening of long positions.
/// </summary>
public bool EnableBuyEntry
{
get => _enableBuyEntry.Value;
set => _enableBuyEntry.Value = value;
}
/// <summary>
/// Enable opening of short positions.
/// </summary>
public bool EnableSellEntry
{
get => _enableSellEntry.Value;
set => _enableSellEntry.Value = value;
}
/// <summary>
/// Enable closing of short positions.
/// </summary>
public bool EnableBuyExit
{
get => _enableBuyExit.Value;
set => _enableBuyExit.Value = value;
}
/// <summary>
/// Enable closing of long positions.
/// </summary>
public bool EnableSellExit
{
get => _enableSellExit.Value;
set => _enableSellExit.Value = value;
}
/// <summary>
/// Stop loss value in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit value in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Candle type to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="WprLevelCrossStrategy"/>.
/// </summary>
public WprLevelCrossStrategy()
{
_wprPeriod = Param(nameof(WprPeriod), 14)
.SetDisplay("WPR Period", "Lookback period for Williams %R", "Indicators")
.SetOptimize(10, 20, 2);
_highLevel = Param(nameof(HighLevel), -20m)
.SetDisplay("High Level", "Overbought threshold", "Indicators");
_lowLevel = Param(nameof(LowLevel), -80m)
.SetDisplay("Low Level", "Oversold threshold", "Indicators");
_trend = Param(nameof(Trend), TrendModes.Direct)
.SetDisplay("Trend Mode", "Direct - trade with indicator; Against - inverse signals", "General");
_enableBuyEntry = Param(nameof(EnableBuyEntry), true)
.SetDisplay("Enable Buy Entry", "Allow opening long positions", "Trading");
_enableSellEntry = Param(nameof(EnableSellEntry), true)
.SetDisplay("Enable Sell Entry", "Allow opening short positions", "Trading");
_enableBuyExit = Param(nameof(EnableBuyExit), true)
.SetDisplay("Enable Buy Exit", "Allow closing short positions", "Trading");
_enableSellExit = Param(nameof(EnableSellExit), true)
.SetDisplay("Enable Sell Exit", "Allow closing long positions", "Trading");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevWr = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(wpr, ProcessCandle).Start();
StartProtection(
new Unit(TakeProfit, UnitTypes.Absolute),
new Unit(StopLoss, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, wpr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal wr)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevWr == 0m)
{
_prevWr = wr;
return;
}
var crossedBelowLow = _prevWr > LowLevel && wr <= LowLevel;
var crossedAboveHigh = _prevWr < HighLevel && wr >= HighLevel;
if (Trend == TrendModes.Direct)
{
if (crossedBelowLow && EnableBuyEntry && Position <= 0)
BuyMarket();
if (crossedAboveHigh && EnableSellEntry && Position >= 0)
SellMarket();
}
else
{
if (crossedBelowLow && EnableSellEntry && Position >= 0)
SellMarket();
if (crossedAboveHigh && EnableBuyEntry && Position <= 0)
BuyMarket();
}
_prevWr = wr;
}
/// <summary>
/// Trend modes for interpreting Williams %R signals.
/// </summary>
public enum TrendModes
{
/// <summary>
/// Trade in the direction of indicator signals.
/// </summary>
Direct,
/// <summary>
/// Invert indicator signals for counter-trend trading.
/// </summary>
Against
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class wpr_level_cross_strategy(Strategy):
DIRECT = 0
AGAINST = 1
def __init__(self):
super(wpr_level_cross_strategy, self).__init__()
self._wpr_period = self.Param("WprPeriod", 14)
self._high_level = self.Param("HighLevel", -20.0)
self._low_level = self.Param("LowLevel", -80.0)
self._trend = self.Param("Trend", 0)
self._enable_buy_entry = self.Param("EnableBuyEntry", True)
self._enable_sell_entry = self.Param("EnableSellEntry", True)
self._enable_buy_exit = self.Param("EnableBuyExit", True)
self._enable_sell_exit = self.Param("EnableSellExit", True)
self._stop_loss = self.Param("StopLoss", 1000.0)
self._take_profit = self.Param("TakeProfit", 2000.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_wr = 0.0
@property
def WprPeriod(self):
return self._wpr_period.Value
@WprPeriod.setter
def WprPeriod(self, value):
self._wpr_period.Value = value
@property
def HighLevel(self):
return self._high_level.Value
@HighLevel.setter
def HighLevel(self, value):
self._high_level.Value = value
@property
def LowLevel(self):
return self._low_level.Value
@LowLevel.setter
def LowLevel(self, value):
self._low_level.Value = value
@property
def Trend(self):
return self._trend.Value
@Trend.setter
def Trend(self, value):
self._trend.Value = value
@property
def EnableBuyEntry(self):
return self._enable_buy_entry.Value
@EnableBuyEntry.setter
def EnableBuyEntry(self, value):
self._enable_buy_entry.Value = value
@property
def EnableSellEntry(self):
return self._enable_sell_entry.Value
@EnableSellEntry.setter
def EnableSellEntry(self, value):
self._enable_sell_entry.Value = value
@property
def EnableBuyExit(self):
return self._enable_buy_exit.Value
@EnableBuyExit.setter
def EnableBuyExit(self, value):
self._enable_buy_exit.Value = value
@property
def EnableSellExit(self):
return self._enable_sell_exit.Value
@EnableSellExit.setter
def EnableSellExit(self, value):
self._enable_sell_exit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(wpr_level_cross_strategy, self).OnStarted2(time)
self._prev_wr = 0.0
wpr = WilliamsR()
wpr.Length = self.WprPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(wpr, self.ProcessCandle).Start()
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
def ProcessCandle(self, candle, wr_value):
if candle.State != CandleStates.Finished:
return
wr = float(wr_value)
if self._prev_wr == 0.0:
self._prev_wr = wr
return
crossed_below_low = self._prev_wr > float(self.LowLevel) and wr <= float(self.LowLevel)
crossed_above_high = self._prev_wr < float(self.HighLevel) and wr >= float(self.HighLevel)
if self.Trend == self.DIRECT:
if crossed_below_low and self.EnableBuyEntry and self.Position <= 0:
self.BuyMarket()
if crossed_above_high and self.EnableSellEntry and self.Position >= 0:
self.SellMarket()
else:
if crossed_below_low and self.EnableSellEntry and self.Position >= 0:
self.SellMarket()
if crossed_above_high and self.EnableBuyEntry and self.Position <= 0:
self.BuyMarket()
self._prev_wr = wr
def OnReseted(self):
super(wpr_level_cross_strategy, self).OnReseted()
self._prev_wr = 0.0
def CreateClone(self):
return wpr_level_cross_strategy()