Ver no GitHub

Estratégia de Grade RSI Reco

Visão geral

Esta estratégia reproduz o comportamento do consultor especialista original "Reco" do MetaTrader usando a API de alto nível do StockSharp. O algoritmo abre uma posição inicial com base no Índice de Força Relativa (RSI) e, em seguida, coloca posições contrárias formando uma grade. A distância entre as ordens da grade e seu volume crescem geometricamente. Todas as posições abertas são fechadas juntas quando o lucro ou prejuízo cumulativo atinge limiares predefinidos.

Lógica de trading

  • Sinal inicial – o RSI ultrapassa as zonas de sobrecompra ou sobrevenda configuradas. Uma posição vendida é aberta quando o RSI está acima do nível de venda e uma posição comprada quando está abaixo do nível de compra.
  • Expansão da grade – após a primeira ordem, a estratégia observa o movimento do preço em relação à última negociação. Quando o preço se move uma distância calculada, uma ordem de mercado oposta é enviada. A distância aumenta com o Distance Multiplier em cada novo passo e pode ser limitada por Max Distance e Min Distance.
  • Escalonamento de volume – o tamanho de cada nova ordem é igual ao Lot inicial multiplicado por Lot Multiplier elevado ao número de ordens já abertas. Limites de volume máximo e mínimo também são suportados.
  • Regras de saída – se Use Close Profit estiver habilitado, todas as posições são fechadas quando o lucro agregado é maior que Profit First Order multiplicado por Profit Multiplier para cada ordem adicional. Se Use Close Lose estiver habilitado, a mesma lógica é aplicada às perdas usando Lose First Order e Lose Multiplier.

Parâmetros

Nome Descrição
RsiPeriod Período do indicador RSI.
RsiSellZone Nível do RSI que aciona um sinal de venda.
RsiBuyZone Nível do RSI que aciona um sinal de compra.
StartDistance Distância inicial da última ordem expressa em pontos.
DistanceMultiplier Multiplicador aplicado à distância para cada ordem adicional.
MaxDistance Limite superior para o crescimento da distância (0 desabilita).
MinDistance Limite inferior para o crescimento da distância (0 desabilita).
MaxOrders Número máximo de ordens abertas simultaneamente (0 significa sem limite).
Lot Volume base da ordem.
LotMultiplier Multiplicador para escalonamento de volume.
MaxLot Volume máximo permitido por ordem (0 desabilita).
MinLot Volume mínimo permitido por ordem (0 desabilita).
UseCloseProfit Habilitar fechamento de todas as posições por meta de lucro.
ProfitFirstOrder Meta de lucro para a primeira ordem.
ProfitMultiplier Multiplicador de lucro para ordens subsequentes.
UseCloseLose Habilitar fechamento de todas as posições por limiar de perda.
LoseFirstOrder Limiar de perda para a primeira ordem.
LoseMultiplier Multiplicador de perda para ordens subsequentes.
PointMultiplier Multiplicador aplicado ao passo de preço do instrumento para calcular um ponto.
CandleType Tipo de velas usadas para cálculos do indicador.

Notas

  • A estratégia trabalha com ordens de mercado e assume execução imediata.
  • As posições são neteadas: abrir uma ordem oposta pode reduzir ou reverter a posição atual.
  • A estratégia usa tabulações para indentação e comentários em inglês conforme as convenções do projeto.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RSI based grid strategy.
/// Opens the first trade when RSI reaches overbought/oversold zones
/// and then adds counter trades as price moves by configurable steps.
/// All positions are closed together on defined profit target.
/// </summary>
public class RecoRsiGridStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiSellZone;
	private readonly StrategyParam<decimal> _rsiBuyZone;
	private readonly StrategyParam<decimal> _gridStep;
	private readonly StrategyParam<int> _maxOrders;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _lastOrderPrice;
	private bool _lastOrderIsBuy;
	private int _ordersTotal;
	private decimal _entryPrice;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal RsiSellZone { get => _rsiSellZone.Value; set => _rsiSellZone.Value = value; }
	public decimal RsiBuyZone { get => _rsiBuyZone.Value; set => _rsiBuyZone.Value = value; }
	public decimal GridStep { get => _gridStep.Value; set => _gridStep.Value = value; }
	public int MaxOrders { get => _maxOrders.Value; set => _maxOrders.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public RecoRsiGridStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI indicator period", "Signal");

		_rsiSellZone = Param(nameof(RsiSellZone), 70m)
			.SetDisplay("RSI Sell Zone", "RSI level to sell", "Signal");

		_rsiBuyZone = Param(nameof(RsiBuyZone), 30m)
			.SetDisplay("RSI Buy Zone", "RSI level to buy", "Signal");

		_gridStep = Param(nameof(GridStep), 200m)
			.SetDisplay("Grid Step", "Distance between grid orders", "Grid");

		_maxOrders = Param(nameof(MaxOrders), 5)
			.SetDisplay("Max Orders", "Maximum number of grid orders", "Grid");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_lastOrderPrice = 0;
		_lastOrderIsBuy = false;
		_ordersTotal = 0;
		_entryPrice = 0;
	}

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

		_lastOrderPrice = 0;
		_lastOrderIsBuy = false;
		_ordersTotal = 0;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, rsi);
			DrawOwnTrades(area);
		}
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var price = candle.ClosePrice;

		// Check if we should close all on profit
		if (_ordersTotal > 0 && _entryPrice > 0)
		{
			var unrealized = Position > 0
				? price - _entryPrice
				: _entryPrice - price;

			if (unrealized > GridStep * 0.5m)
			{
				// Close all
				if (Position > 0)
					SellMarket();
				else if (Position < 0)
					BuyMarket();

				_ordersTotal = 0;
				_lastOrderPrice = 0;
				_entryPrice = 0;
				return;
			}
		}

		var signal = GetSignal(price, rsiValue);

		if (signal > 0 && Position <= 0)
		{
			BuyMarket();
			_lastOrderIsBuy = true;
			_lastOrderPrice = price;
			_entryPrice = price;
			_ordersTotal++;
		}
		else if (signal < 0 && Position >= 0)
		{
			SellMarket();
			_lastOrderIsBuy = false;
			_lastOrderPrice = price;
			_entryPrice = price;
			_ordersTotal++;
		}
	}

	private int GetSignal(decimal price, decimal rsiValue)
	{
		if (_ordersTotal == 0)
		{
			if (rsiValue >= RsiSellZone)
				return -1;
			if (rsiValue <= RsiBuyZone)
				return 1;
			return 0;
		}

		if (MaxOrders > 0 && _ordersTotal >= MaxOrders)
		{
			// Reset grid when max orders reached
			_ordersTotal = 0;
			_lastOrderPrice = 0;
			return 0;
		}

		// Add counter-trend orders at grid steps
		if (_lastOrderIsBuy && price <= _lastOrderPrice - GridStep)
			return 1;
		else if (!_lastOrderIsBuy && price >= _lastOrderPrice + GridStep)
			return -1;

		return 0;
	}
}