Ver no GitHub

Estratégia Rsi Williams R

Implementação da estratégia - RSI + Williams %R. Compra quando o RSI está abaixo de 30 e o Williams %R está abaixo de -80 (condição de dupla sobrevenda). Vende quando o RSI está acima de 70 e o Williams %R está acima de -20 (condição de dupla sobrecompra).

Os testes indicam um retorno anual médio de aproximadamente 76%. Funciona melhor no mercado forex.

O RSI descreve o momentum geral, enquanto o Williams %R fornece um sinal mais rápido de reversão. As operações são ativadas quando os dois osciladores concordam.

Bom para traders ativos que buscam oscilações curtas. Stops baseados em ATR são empregados.

Detalhes

  • Critérios de entrada:
    • Comprado: RSI < RsiOversold && WilliamsR < WilliamsROversold
    • Vendido: RSI > RsiOverbought && WilliamsR > WilliamsROverbought
  • Comprado/Vendido: Ambos
  • Critérios de saída:
    • RSI retorna à zona neutra
  • Stops: Baseados em porcentagem usando StopLoss
  • Valores padrão:
    • RsiPeriod = 14
    • RsiOversold = 30m
    • RsiOverbought = 70m
    • WilliamsRPeriod = 14
    • WilliamsROversold = -80m
    • WilliamsROverbought = -20m
    • StopLoss = new Unit(2, UnitTypes.Percent)
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoria: Reversão à média
    • Direção: Ambos
    • Indicadores: RSI, Williams %R, R
    • Stops: Sim
    • Complexidade: Intermediário
    • Período: Médio prazo
    • Sazonalidade: Não
    • Redes neurais: Não
    • Divergência: Não
    • Nível de risco: Médio
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;

using StockSharp.Algo;
using StockSharp.Algo.Candles;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Implementation of strategy - RSI + Williams %R.
/// Buy when RSI is below 30 and Williams %R is below -80 (double oversold condition).
/// Sell when RSI is above 70 and Williams %R is above -20 (double overbought condition).
/// </summary>
public class RsiWilliamsRStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOversold;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<int> _williamsRPeriod;
	private readonly StrategyParam<decimal> _williamsROversold;
	private readonly StrategyParam<decimal> _williamsROverbought;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<Unit> _stopLoss;
	private readonly StrategyParam<DataType> _candleType;

	private int _cooldown;
	private decimal _prevRsi;
	private decimal _prevWilliams;

	/// <summary>
	/// RSI period.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// RSI oversold level.
	/// </summary>
	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	/// <summary>
	/// RSI overbought level.
	/// </summary>
	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	/// <summary>
	/// Williams %R period.
	/// </summary>
	public int WilliamsRPeriod
	{
		get => _williamsRPeriod.Value;
		set => _williamsRPeriod.Value = value;
	}

	/// <summary>
	/// Williams %R oversold level (usually below -80).
	/// </summary>
	public decimal WilliamsROversold
	{
		get => _williamsROversold.Value;
		set => _williamsROversold.Value = value;
	}

	/// <summary>
	/// Williams %R overbought level (usually above -20).
	/// </summary>
	public decimal WilliamsROverbought
	{
		get => _williamsROverbought.Value;
		set => _williamsROverbought.Value = value;
	}

	/// <summary>
	/// Bars to wait between trades.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Stop-loss value.
	/// </summary>
	public Unit StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Candle type used for strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize <see cref="RsiWilliamsRStrategy"/>.
	/// </summary>
	public RsiWilliamsRStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Period for Relative Strength Index", "RSI Parameters");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetRange(1, 100)
			.SetDisplay("RSI Oversold", "RSI level to consider market oversold", "RSI Parameters");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetRange(1, 100)
			.SetDisplay("RSI Overbought", "RSI level to consider market overbought", "RSI Parameters");

		_williamsRPeriod = Param(nameof(WilliamsRPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Williams %R Period", "Period for Williams %R", "Williams %R Parameters");

		_williamsROversold = Param(nameof(WilliamsROversold), -80m)
			.SetRange(-100, 0)
			.SetDisplay("Williams %R Oversold", "Williams %R level to consider market oversold", "Williams %R Parameters");

		_williamsROverbought = Param(nameof(WilliamsROverbought), -20m)
			.SetRange(-100, 0)
			.SetDisplay("Williams %R Overbought", "Williams %R level to consider market overbought", "Williams %R Parameters");

		_cooldownBars = Param(nameof(CooldownBars), 180)
			.SetRange(5, 500)
			.SetDisplay("Cooldown Bars", "Bars between trades", "General");

		_stopLoss = Param(nameof(StopLoss), new Unit(2, UnitTypes.Percent))
			.SetDisplay("Stop Loss", "Stop loss percent or value", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for strategy", "General");
	}

	/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
	return [(Security, CandleType)];
}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_cooldown = 0;
		_prevRsi = 0;
		_prevWilliams = 0;
	}

/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
	base.OnStarted2(time);

		// Create indicators
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var williamsR = new WilliamsR { Length = WilliamsRPeriod };

		// Setup candle subscription
		var subscription = SubscribeCandles(CandleType);

		// Bind indicators to candles
		subscription
			.Bind(rsi, williamsR, ProcessCandle)
			.Start();

		// Setup chart visualization if available
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);

			// Create separate area for oscillators
			var oscillatorArea = CreateChartArea();
			if (oscillatorArea != null)
			{
				DrawIndicator(oscillatorArea, rsi);
				DrawIndicator(oscillatorArea, williamsR);
			}

			DrawOwnTrades(area);
		}

	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal williamsRValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_prevRsi == 0 && _prevWilliams == 0)
		{
			_prevRsi = rsiValue;
			_prevWilliams = williamsRValue;
			return;
		}

		LogInfo($"Candle: {candle.OpenTime}, Close: {candle.ClosePrice}, " +
			$"RSI: {rsiValue} , Williams %R: {williamsRValue}");

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevRsi = rsiValue;
			_prevWilliams = williamsRValue;
			return;
		}

		var oversoldCross = _prevRsi >= RsiOversold && rsiValue < RsiOversold
			&& _prevWilliams >= WilliamsROversold && williamsRValue < WilliamsROversold;
		var overboughtCross = _prevRsi <= RsiOverbought && rsiValue > RsiOverbought
			&& _prevWilliams <= WilliamsROverbought && williamsRValue > WilliamsROverbought;

		// Trading rules
		if (oversoldCross && Position == 0)
		{
			BuyMarket();
			_cooldown = CooldownBars;

			LogInfo($"Buy signal: Double oversold condition - RSI: {rsiValue} < {RsiOversold} and Williams %R: {williamsRValue} < {WilliamsROversold}.");
		}
		else if (overboughtCross && Position == 0)
		{
			SellMarket();
			_cooldown = CooldownBars;

			LogInfo($"Sell signal: Double overbought condition - RSI: {rsiValue} > {RsiOverbought} and Williams %R: {williamsRValue} > {WilliamsROverbought}.");
		}
		// Exit conditions
		else if (rsiValue > 50 && Position > 0)
		{
			// Exit long position when RSI returns to neutral zone
			SellMarket();
			_cooldown = CooldownBars;
			LogInfo($"Exit long: RSI returned to neutral zone ({rsiValue} > 50). Position: {Position}");
		}
		else if (rsiValue < 50 && Position < 0)
		{
			// Exit short position when RSI returns to neutral zone
			BuyMarket();
			_cooldown = CooldownBars;
			LogInfo($"Exit short: RSI returned to neutral zone ({rsiValue} < 50). Position: {Position}");
		}

		_prevRsi = rsiValue;
		_prevWilliams = williamsRValue;
	}
}