Ver no GitHub

Estratégia de reversão RPoint 250

A Estratégia de reversão RPoint 250 é uma versão StockSharp do MetaTrader 4 consultor especialista e_RPoint_250. O robô original depende de um indicador personalizado chamado RPoint que destaca a oscilação máxima e mínima mais recente. Porque esse indicador é não disponível em StockSharp, a conversão reproduz o mesmo comportamento com os indicadores Highest e Lowest integrados. Sempre que um novo extremo substitui o anteriormente detectado, a estratégia imediatamente inverte a posição e restaura a mesma. lógica de stop-loss, take-profit e trailing definidas na versão MQL.

Fluxo de trabalho de negociação

  1. Assine a série de velas especificada por CandleType (padrão: velas de 5 minutos).
  2. Acompanhe o máximo e o mínimo contínuos nas últimas ReversePoint barras. Esses valores representam os níveis RPoint emulados.
  3. Se o preço imprimir uma nova máxima máxima, feche qualquer posição longa e abra uma posição curta com volume OrderVolume.
  4. Se o preço imprimir um novo mínimo, feche qualquer posição curta e abra uma posição longa com volume OrderVolume.
  5. Aplique ordens de proteção usando StartProtection. As distâncias de stop-loss e take-profit são expressas em pontos de preço por meio de os parâmetros StopLossPoints e TakeProfitPoints.
  6. Opcionalmente, acompanhe os lucros por TrailingStopPoints. O mecanismo de trilha mede até que ponto o preço se moveu em favor do posição e fecha-a quando o preço retrocede pelo número configurado de pontos.
  7. Lembre-se do tempo da vela da última entrada bem-sucedida para evitar a abertura de múltiplas negociações dentro da mesma barra, correspondendo ao TimeN proteção do script MQL.

A estratégia sempre mantém no máximo uma posição aberta. Fecha negociações existentes antes de entrar na direção oposta e nunca aumenta.

Parâmetros

Parâmetro Tipo Padrão Descrição
OrderVolume decimal 0.1 Volume enviado com cada ordem de mercado. Espelha a entrada Lots na versão MetaTrader.
TakeProfitPoints decimal 15 Distância até a ordem de lucro medida em faixas de preço. Defina como 0 para desativar as metas de lucro.
StopLossPoints decimal 999 Distância até o stop de proteção expressa em faixas de preço. Defina como 0 para negociar sem um stop fixo.
TrailingStopPoints decimal 0 Distância final opcional em faixas de preço. Quando zero, a lógica final é desabilitada.
ReversePoint int 250 Número de velas consideradas ao pesquisar as últimas oscilações máximas e mínimas. Valores maiores suavizam o ruído.
CandleType DataType TimeSpan.FromMinutes(5).TimeFrame() Agregação de velas analisada pela estratégia. Altere-o para corresponder ao período do gráfico usado em MetaTrader.

Notas de implementação

  • Highest e Lowest estão vinculados à assinatura de vela por meio do Bind API de alto nível, portanto, nenhuma fila de indicador manual é necessário.
  • StartProtection reproduz as distâncias originais de stop-loss e take-profit em unidades de preço absoluto. StockSharp lida com o colocação de pedido assim que uma nova posição aparecer.
  • Os trailing stops são implementados monitorando cada vela concluída. Quando o preço recua pelo número configurado de pontos de ao melhor preço alcançado após a entrada, a posição é fechada com uma ordem de mercado.
  • A classe armazena os níveis de reversão executados mais recentemente (_executedHighLevel e _executedLowLevel) para evitar duplicação entradas. Isso é equivalente às variáveis ​​Reverse_High / Reverse_Low no código MQL.
  • O campo _lastSignalTime espelha a variável TimeN e bloqueia vários pedidos dentro da mesma vela, evitando submissões duplas acidentais em mercados ilíquidos.

Diretrizes de uso

  1. Anexe a estratégia a um portfólio que suporte o instrumento selecionado e o tipo de vela.
  2. Ajuste OrderVolume para cumprir o tamanho do contrato e as regras de gerenciamento de risco do seu corretor.
  3. Ajuste ReversePoint para corresponder à volatilidade do ativo negociado. Valores mais altos geram menos reversões, mas mais significativas.
  4. Verifique se StopLossPoints, TakeProfitPoints e TrailingStopPoints são compatíveis com o PriceStep da segurança.
  5. Execute um backtest no StockSharp Designer ou Backtester para confirmar o comportamento antes de negociar capital real.
  6. Monitore a saída do log: mensagens informativas destacarão as mudanças de posição e podem ajudar a validar a conversão.

Como o indicador RPoint é aproximado com componentes integrados, pequenas diferenças em relação à execução MetaTrader são possível em dados históricos com lacunas ou regras de arredondamento diferentes. Sempre valide os resultados com seus próprios feeds de dados de mercado antes de confiar na estratégia na produção.

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>
/// Reverse-point breakout strategy converted from the MetaTrader 4 expert e_RPoint_250.
/// </summary>
public class RPoint250Strategy : Strategy
{
	private readonly StrategyParam<decimal> _orderVolume;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _trailingStopPoints;
	private readonly StrategyParam<int> _reversePoint;
	private readonly StrategyParam<DataType> _candleType;

	private Highest _highest;
	private Lowest _lowest;

	private decimal _lastHighLevel;
	private decimal _lastLowLevel;
	private decimal _executedHighLevel;
	private decimal _executedLowLevel;
	private DateTimeOffset? _lastSignalTime;
	private decimal _priceStep;
	private decimal _trailingDistance;
	private decimal? _bestLongPrice;
	private decimal? _bestShortPrice;

	public RPoint250Strategy()
	{
		_orderVolume = Param(nameof(OrderVolume), 1m)
			.SetDisplay("Order Volume", "Base volume for market entries.", "Trading")
			;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
			.SetDisplay("Take Profit Points", "Take profit distance expressed in price points.", "Risk")
			;

		_stopLossPoints = Param(nameof(StopLossPoints), 999m)
			.SetDisplay("Stop Loss Points", "Stop loss distance expressed in price points.", "Risk")
			;

		_trailingStopPoints = Param(nameof(TrailingStopPoints), 0m)
			.SetDisplay("Trailing Stop Points", "Optional trailing distance in price points.", "Risk")
			;

		_reversePoint = Param(nameof(ReversePoint), 250)
			.SetDisplay("Reverse Point Length", "Number of candles scanned for the latest reversal levels.", "Signals")
			.SetGreaterThanZero()
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle aggregation used for calculations.", "General");
	}

	/// <summary>
	/// Market order volume used for both entries and reversals.
	/// </summary>
	public decimal OrderVolume
	{
		get => _orderVolume.Value;
		set => _orderVolume.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Trailing-stop distance in price points.
	/// </summary>
	public decimal TrailingStopPoints
	{
		get => _trailingStopPoints.Value;
		set => _trailingStopPoints.Value = value;
	}

	/// <summary>
	/// Number of candles used to approximate the rPoint indicator.
	/// </summary>
	public int ReversePoint
	{
		get => _reversePoint.Value;
		set => _reversePoint.Value = value;
	}

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_highest = null;
		_lowest = null;
		_lastHighLevel = 0m;
		_lastLowLevel = 0m;
		_executedHighLevel = 0m;
		_executedLowLevel = 0m;
		_lastSignalTime = null;
		_priceStep = 0m;
		_trailingDistance = 0m;
		_bestLongPrice = null;
		_bestShortPrice = null;
	}

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

		_highest = new Highest { Length = Math.Max(1, ReversePoint) };
		_lowest = new Lowest { Length = Math.Max(1, ReversePoint) };

		_priceStep = Security?.PriceStep ?? 0m;
		if (_priceStep <= 0m)
			_priceStep = 1m;

		var takeDistance = TakeProfitPoints > 0m ? _priceStep * TakeProfitPoints : 0m;
		var stopDistance = StopLossPoints > 0m ? _priceStep * StopLossPoints : 0m;
		_trailingDistance = TrailingStopPoints > 0m ? _priceStep * TrailingStopPoints : 0m;

		// Apply the same static protection as in the original MQL script.
		var tp = takeDistance > 0m ? new Unit(takeDistance, UnitTypes.Absolute) : (Unit)null;
		var sl = stopDistance > 0m ? new Unit(stopDistance, UnitTypes.Absolute) : (Unit)null;
		if (tp != null || sl != null)
			StartProtection(tp, sl);

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

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

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

		if (!_highest.IsFormed || !_lowest.IsFormed)
			return;

		// Capture the latest swing levels as soon as they appear.
		if (highestValue == candle.HighPrice && highestValue != _lastHighLevel)
			_lastHighLevel = highestValue;

		if (lowestValue == candle.LowPrice && lowestValue != _lastLowLevel)
			_lastLowLevel = lowestValue;

		if (Position > 0)
		{
			_bestLongPrice = _bestLongPrice is null || candle.HighPrice > _bestLongPrice
				? candle.HighPrice
				: _bestLongPrice;

			if (!IsFormedAndOnlineAndAllowTrading())
				return;

			// Close the long position when price retraces by the trailing distance.
			if (_trailingDistance > 0m && _bestLongPrice is decimal bestLong && bestLong - candle.LowPrice >= _trailingDistance)
			{
				SellMarket(Position);
				_bestLongPrice = null;
				return;
			}

			// Reverse the position when a new high reversal point appears.
			if (_lastHighLevel != 0m && _lastHighLevel != _executedHighLevel)
			{
				SellMarket(Position);
				_bestLongPrice = null;
				return;
			}
		}
		else if (Position < 0)
		{
			_bestShortPrice = _bestShortPrice is null || candle.LowPrice < _bestShortPrice
				? candle.LowPrice
				: _bestShortPrice;

			if (!IsFormedAndOnlineAndAllowTrading())
				return;

			// Close the short position when price rallies by the trailing distance.
			if (_trailingDistance > 0m && _bestShortPrice is decimal bestShort && candle.HighPrice - bestShort >= _trailingDistance)
			{
				BuyMarket(-Position);
				_bestShortPrice = null;
				return;
			}

			// Reverse the position when a new low reversal point appears.
			if (_lastLowLevel != 0m && _lastLowLevel != _executedLowLevel)
			{
				BuyMarket(-Position);
				_bestShortPrice = null;
				return;
			}
		}
		else
		{
			_bestLongPrice = null;
			_bestShortPrice = null;

			if (!IsFormedAndOnlineAndAllowTrading())
				return;

			if (OrderVolume <= 0m)
				return;

			if (_lastSignalTime == candle.OpenTime)
				return;

			// Enter short when the reversal high changes.
			if (_lastHighLevel != 0m && _lastHighLevel != _executedHighLevel)
			{
				SellMarket(OrderVolume);
				_executedHighLevel = _lastHighLevel;
				_lastSignalTime = candle.OpenTime;
				_bestShortPrice = candle.ClosePrice;
				return;
			}

			// Enter long when the reversal low changes.
			if (_lastLowLevel != 0m && _lastLowLevel != _executedLowLevel)
			{
				BuyMarket(OrderVolume);
				_executedLowLevel = _lastLowLevel;
				_lastSignalTime = candle.OpenTime;
				_bestLongPrice = candle.ClosePrice;
			}
		}
	}
}