Ver en GitHub

Estrategia de Señal WPRSI

Descripción general

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;
	}
}