Esta estrategia replica el experto WPRSIsignal de MetaTrader. Combina el Williams Percent Range (WPR) y el Índice de Fuerza Relativa (RSI) para generar señales de compra y venta.
Lógica
Se genera una señal de compra cuando el WPR cruza por encima de -20 desde abajo y el RSI está por encima de 50. La señal se confirma solo si el WPR permanece por encima de -20 durante las próximas FilterUp barras.
Se genera una señal de venta cuando el WPR cruza por debajo de -80 desde arriba y el RSI está por debajo de 50. La señal se confirma solo si el WPR permanece por debajo de -80 durante las próximas FilterDown barras.
Cuando se confirma una señal de compra, la estrategia abre una posición larga si no hay ninguna activa. Cuando se confirma una señal de venta, abre una posición corta si no hay ninguna activa.
Parámetros
Period – longitud de cálculo para WPR y RSI.
FilterUp – número de barras que deben mantener el WPR por encima de -20 para confirmar una señal de compra.
FilterDown – número de barras que deben mantener el WPR por debajo de -80 para confirmar una señal de venta.
CandleType – marco temporal de las velas usadas para los cálculos.
Uso
Adjunte la estrategia a cualquier activo. La estrategia usa SubscribeCandles y Bind para recibir datos de velas y valores de indicadores. Las posiciones se gestionan con órdenes de mercado: BuyMarket para entradas largas y SellMarket para entradas cortas. La estrategia no implementa stop-loss ni take-profit; las posiciones se cierran mediante señales opuestas.
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()