Ver no GitHub

Estratégia eRP250ReversePoint

Esta estratégia é uma portagem para StockSharp do consultor especialista do MetaTrader 5 e_RP_250. O sistema original opera reversões detectadas por um indicador personalizado rPoint. Como esse indicador não está disponível no StockSharp, a conversão recria o mesmo comportamento com rastreadores de preço mais alto e mais baixo em janela móvel. Quando um novo swing high ou swing low aparece, a estratégia inverte a posição e anexa a mesma lógica de stop-loss, take-profit e trailing opcional que a versão MQL.

O código fonte original não publicou resultados de desempenho verificados, portanto você deve realizar sua própria avaliação antes de implantar a estratégia em produção.

Lógica de trading

  • Subscrever velas definidas pelo parâmetro CandleType (velas de 5 minutos por padrão).
  • Rastrear o maior máximo e o menor mínimo nas últimas ReversePoint barras (250 por padrão).
  • Quando a vela atual estabelece um novo maior máximo, fechar qualquer posição comprada e abrir uma posição vendida.
  • Quando a vela atual estabelece um novo menor mínimo, fechar qualquer posição vendida e abrir uma posição comprada.
  • Os níveis protetores de stop-loss e take-profit são expressos em pontos de preço e são reproduzidos através de StartProtection.
  • Stops trailing opcionais bloqueiam lucros uma vez que o preço se move pelo número de pontos configurado.

Apenas uma posição está ativa a qualquer momento. A estratégia também bloqueia ordens duplicadas durante a mesma vela ao lembrar o último tempo de execução, replicando a proteção TimeN do script MQL.

Parâmetros

Parâmetro Descrição
TakeProfitPoints Distância em pontos de preço para a ordem de take-profit (padrão 15). Definir como zero para desabilitar a tomada de lucro automática.
StopLossPoints Distância em pontos de preço para a ordem de stop-loss (padrão 999). Definir como zero para operar sem stop fixo.
TrailingStopPoints Distância opcional de stop trailing em pontos de preço (padrão 0 desabilita a lógica de trailing).
ReversePoint Número de velas usadas para detectar pontos de reversão. Valores maiores reagem mais lentamente mas filtram o ruído.
CandleType Agregação de velas a analisar. O padrão é um período de 5 minutos mas pode ser alterado para qualquer DataType.

Gestão de posição

  • StartProtection aplica as mesmas distâncias de stop-loss e take-profit que o especialista MT5.
  • O stop trailing rastreia o preço mais favorável após a entrada e sai quando o preço reverte pelo valor configurado.
  • Sinais de reversão do lado oposto fecham imediatamente a posição atual antes de abrir uma nova.

Notas de uso

  • Certifique-se de que a fonte de dados suporta o tipo de vela selecionado, caso contrário nenhum sinal será gerado.
  • A estratégia depende de preços decimais. Verifique se a propriedade PriceStep do ativo reflete corretamente o valor do ponto.
  • Teste diferentes valores de ReversePoint para adaptar a sensibilidade ao rompimento à volatilidade do instrumento operado.
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 e_RP_250 MQL script.
/// </summary>
public class ERp250Strategy : Strategy
{
	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 _latestHighSignal;
	private decimal _latestLowSignal;
	private decimal _lastExecutedHigh;
	private decimal _lastExecutedLow;
	private DateTimeOffset? _lastSignalTime;
	private decimal? _bestLongPrice;
	private decimal? _bestShortPrice;
	private decimal _trailingDistance;

	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	public decimal TrailingStopPoints
	{
		get => _trailingStopPoints.Value;
		set => _trailingStopPoints.Value = value;
	}

	public int ReversePoint
	{
		get => _reversePoint.Value;
		set => _reversePoint.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public ERp250Strategy()
	{
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 15m)
			.SetDisplay("Take Profit Points", "Take profit distance in price points", "Risk")
			;

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

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

		_reversePoint = Param(nameof(ReversePoint), 400)
			.SetDisplay("Reverse Point Length", "Candles used to confirm reversal points", "Signals")
			.SetGreaterThanZero()
			;

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

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

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

		_highest = null;
		_lowest = null;
		_latestHighSignal = 0m;
		_latestLowSignal = 0m;
		_lastExecutedHigh = 0m;
		_lastExecutedLow = 0m;
		_lastSignalTime = null;
		_bestLongPrice = null;
		_bestShortPrice = null;
		_trailingDistance = 0m;
	}

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

		_highest = new Highest { Length = ReversePoint };
		_lowest = new Lowest { Length = ReversePoint };

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

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

		// Enable protective orders that match the original stop and take-profit distances.
		StartProtection(
			takeDistance > 0m ? new Unit(takeDistance, UnitTypes.Absolute) : default,
			stopDistance > 0m ? new Unit(stopDistance, UnitTypes.Absolute) : default
		);

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

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

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

		var highValue = _highest.Process(new DecimalIndicatorValue(_highest, candle.HighPrice, candle.OpenTime) { IsFinal = true }).ToNullableDecimal();
		var lowValue = _lowest.Process(new DecimalIndicatorValue(_lowest, candle.LowPrice, candle.OpenTime) { IsFinal = true }).ToNullableDecimal();

		if (highValue is null || lowValue is null)
			return;

		// Update the latest reversal levels detected by the rolling highest/lowest indicators.
		if (highValue.Value == candle.HighPrice)
			_latestHighSignal = candle.HighPrice;

		if (lowValue.Value == candle.LowPrice)
			_latestLowSignal = candle.LowPrice;

		// Manage an existing long position by trailing profits and reacting to opposite signals.
		if (Position > 0)
		{
			_bestLongPrice = (_bestLongPrice is null || candle.HighPrice > _bestLongPrice) ? candle.HighPrice : _bestLongPrice;

			if (_trailingDistance > 0m && _bestLongPrice is decimal bestLong && bestLong - candle.ClosePrice >= _trailingDistance)
			{
				SellMarket();
				_bestLongPrice = null;
				return;
			}

			if (_latestHighSignal != 0m && _latestHighSignal != _lastExecutedHigh)
			{
				SellMarket();
				_bestLongPrice = null;
				return;
			}
		}
		else if (Position < 0)
		{
			_bestShortPrice = (_bestShortPrice is null || candle.LowPrice < _bestShortPrice) ? candle.LowPrice : _bestShortPrice;

			if (_trailingDistance > 0m && _bestShortPrice is decimal bestShort && candle.ClosePrice - bestShort >= _trailingDistance)
			{
				BuyMarket();
				_bestShortPrice = null;
				return;
			}

			if (_latestLowSignal != 0m && _latestLowSignal != _lastExecutedLow)
			{
				BuyMarket();
				_bestShortPrice = null;
				return;
			}
		}
		else
		{
			_bestLongPrice = null;
			_bestShortPrice = null;
		}

		if (Position != 0)
			return;

		// Avoid placing more than one order within the same candle.
		if (_lastSignalTime == candle.OpenTime)
			return;

		// Execute a new short position when a fresh reversal high is detected.
		if (_latestHighSignal != 0m && _latestHighSignal != _lastExecutedHigh)
		{
			SellMarket();
			_lastExecutedHigh = _latestHighSignal;
			_lastSignalTime = candle.OpenTime;
			_bestShortPrice = candle.ClosePrice;
			_bestLongPrice = null;
			return;
		}

		// Execute a new long position when a fresh reversal low is detected.
		if (_latestLowSignal != 0m && _latestLowSignal != _lastExecutedLow)
		{
			BuyMarket();
			_lastExecutedLow = _latestLowSignal;
			_lastSignalTime = candle.OpenTime;
			_bestLongPrice = candle.ClosePrice;
			_bestShortPrice = null;
		}
	}
}