This strategy replicates the WPRSIsignal MetaTrader expert. It combines the Williams Percent Range (WPR) and Relative Strength Index (RSI) to generate buy and sell signals.
Logic
A buy signal is generated when WPR crosses above -20 from below and RSI is above 50. The signal is confirmed only if WPR remains above -20 for the next FilterUp bars.
A sell signal is generated when WPR crosses below -80 from above and RSI is below 50. The signal is confirmed only if WPR remains below -80 for the next FilterDown bars.
When a buy signal is confirmed, the strategy opens a long position if no long position is active. When a sell signal is confirmed, it opens a short position if no short position is active.
Parameters
Period – calculation length for WPR and RSI.
FilterUp – number of bars that must keep WPR above -20 to confirm a buy signal.
FilterDown – number of bars that must keep WPR below -80 to confirm a sell signal.
CandleType – timeframe of candles used for the calculations.
Usage
Attach the strategy to any security. The strategy uses SubscribeCandles and Bind to receive candle data and indicator values. Positions are managed using market orders: BuyMarket for long entries and SellMarket for short entries. The strategy does not implement stop-loss or take-profit; positions are closed by opposite signals.
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 and RSI combination.
/// </summary>
public class WprsiSignalStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _filterUp;
private readonly StrategyParam<int> _filterDown;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _wpr;
private RelativeStrengthIndex _rsi;
private decimal _prevWpr;
private bool _isPrevInit;
private bool _pendingBuy;
private bool _pendingSell;
private int _upCounter;
private int _downCounter;
/// <summary>
/// Calculation length for WPR and RSI.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Bars count to confirm buy signal.
/// </summary>
public int FilterUp
{
get => _filterUp.Value;
set => _filterUp.Value = value;
}
/// <summary>
/// Bars count to confirm sell signal.
/// </summary>
public int FilterDown
{
get => _filterDown.Value;
set => _filterDown.Value = value;
}
/// <summary>
/// Candle type for indicators calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="WprsiSignalStrategy"/>.
/// </summary>
public WprsiSignalStrategy()
{
_period = Param(nameof(Period), 27)
.SetGreaterThanZero()
.SetDisplay("Period", "Period for WPR and RSI", "Parameters");
_filterUp = Param(nameof(FilterUp), 10)
.SetNotNegative()
.SetDisplay("Filter Up", "Bars to confirm buy", "Parameters");
_filterDown = Param(nameof(FilterDown), 10)
.SetNotNegative()
.SetDisplay("Filter Down", "Bars to confirm sell", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevWpr = 0m;
_isPrevInit = false;
_pendingBuy = false;
_pendingSell = false;
_upCounter = 0;
_downCounter = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_wpr = new WilliamsR { Length = Period };
_rsi = new RelativeStrengthIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_wpr, _rsi, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _wpr);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isPrevInit)
{
_prevWpr = wprValue;
_isPrevInit = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevWpr = wprValue;
return;
}
if (_pendingBuy)
{
if (wprValue <= -20)
_pendingBuy = false;
else if (--_upCounter <= 0)
{
if (rsiValue > 50 && Position <= 0)
BuyMarket();
_pendingBuy = false;
}
}
else if (_prevWpr < -20 && wprValue > -20 && rsiValue > 50)
{
_pendingBuy = true;
_upCounter = FilterUp;
}
if (_pendingSell)
{
if (wprValue >= -80)
_pendingSell = false;
else if (--_downCounter <= 0)
{
if (rsiValue < 50 && Position >= 0)
SellMarket();
_pendingSell = false;
}
}
else if (_prevWpr > -80 && wprValue < -80 && rsiValue < 50)
{
_pendingSell = true;
_downCounter = FilterDown;
}
_prevWpr = wprValue;
}
}
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 WilliamsR, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class wprsi_signal_strategy(Strategy):
def __init__(self):
super(wprsi_signal_strategy, self).__init__()
self._period = self.Param("Period", 27) \
.SetDisplay("Period", "Period for WPR and RSI", "Parameters")
self._filter_up = self.Param("FilterUp", 10) \
.SetDisplay("Filter Up", "Bars to confirm buy", "Parameters")
self._filter_down = self.Param("FilterDown", 10) \
.SetDisplay("Filter Down", "Bars to confirm sell", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "Parameters")
self._prev_wpr = 0.0
self._is_prev_init = False
self._pending_buy = False
self._pending_sell = False
self._up_counter = 0
self._down_counter = 0
@property
def period(self):
return self._period.Value
@property
def filter_up(self):
return self._filter_up.Value
@property
def filter_down(self):
return self._filter_down.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(wprsi_signal_strategy, self).OnReseted()
self._prev_wpr = 0.0
self._is_prev_init = False
self._pending_buy = False
self._pending_sell = False
self._up_counter = 0
self._down_counter = 0
def OnStarted2(self, time):
super(wprsi_signal_strategy, self).OnStarted2(time)
wpr = WilliamsR()
wpr.Length = self.period
rsi = RelativeStrengthIndex()
rsi.Length = self.period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(wpr, rsi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, wpr)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, wpr_value, rsi_value):
if candle.State != CandleStates.Finished:
return
wpr_value = float(wpr_value)
rsi_value = float(rsi_value)
if not self._is_prev_init:
self._prev_wpr = wpr_value
self._is_prev_init = True
return
if self._pending_buy:
if wpr_value <= -20:
self._pending_buy = False
else:
self._up_counter -= 1
if self._up_counter <= 0:
if rsi_value > 50 and self.Position <= 0:
self.BuyMarket()
self._pending_buy = False
elif self._prev_wpr < -20 and wpr_value > -20 and rsi_value > 50:
self._pending_buy = True
self._up_counter = self.filter_up
if self._pending_sell:
if wpr_value >= -80:
self._pending_sell = False
else:
self._down_counter -= 1
if self._down_counter <= 0:
if rsi_value < 50 and self.Position >= 0:
self.SellMarket()
self._pending_sell = False
elif self._prev_wpr > -80 and wpr_value < -80 and rsi_value < 50:
self._pending_sell = True
self._down_counter = self.filter_down
self._prev_wpr = wpr_value
def CreateClone(self):
return wprsi_signal_strategy()