Ver en GitHub

Estrategia de reversión RPoint 250

La Estrategia de reversión RPoint 250 es una StockSharp versión del MetaTrader 4 asesor experto e_RPoint_250. El robot original se basa en un indicador personalizado llamado RPoint que resalta el máximo y el mínimo más recientes. Porque ese indicador es no disponible en StockSharp, la conversión reproduce el mismo comportamiento con los indicadores integrados Highest y Lowest. Cada vez que un nuevo extremo reemplaza al previamente detectado, la estrategia inmediatamente invierte la posición y restaura la misma. Lógica de stop-loss, take-profit y trailing definida en la versión MQL.

Flujo de trabajo comercial

  1. Suscríbase a la serie de velas especificada por CandleType (predeterminado: velas de 5 minutos).
  2. Realice un seguimiento del máximo y mínimo móvil de las últimas ReversePoint barras. Estos valores representan los niveles de RPoint emulados.
  3. Si el precio alcanza un nuevo máximo, cierre cualquier posición larga y abra una posición corta con volumen OrderVolume.
  4. Si el precio imprime un nuevo mínimo, cierre cualquier posición corta y abra una posición larga con volumen OrderVolume.
  5. Aplicar órdenes de protección usando StartProtection. Las distancias de stop-loss y take-profit se expresan en puntos de precio mediante los parámetros StopLossPoints y TakeProfitPoints.
  6. Opcionalmente, siga las ganancias por TrailingStopPoints. El motor de seguimiento mide hasta qué punto se ha movido el precio a favor del posición y la cierra cuando el precio retrocede en el número de puntos configurado.
  7. Recuerde el tiempo de la vela de la última entrada exitosa para evitar abrir múltiples operaciones dentro de la misma barra, coincidiendo con el TimeN salvaguarda del script MQL.

La estrategia siempre mantiene como máximo una posición abierta. Cierra las operaciones existentes antes de entrar en la dirección opuesta y nunca se amplía.

Parámetros

Parámetro Tipo Predeterminado Descripción
OrderVolume decimal 0.1 Volumen enviado con cada orden de mercado. Refleja la entrada Lots en la versión MetaTrader.
TakeProfitPoints decimal 15 Distancia a la orden de toma de ganancias medida en puntos de precio. Establezca en 0 para deshabilitar los objetivos de ganancias.
StopLossPoints decimal 999 Distancia al tope de protección expresada en puntos de precio. Establezca en 0 para operar sin un tope fijo.
TrailingStopPoints decimal 0 Distancia de seguimiento opcional en puntos de precio. Cuando es cero, la lógica de seguimiento está desactivada.
ReversePoint int 250 Número de velas consideradas al buscar el último máximo y mínimo. Los valores más altos suavizan el ruido.
CandleType DataType TimeSpan.FromMinutes(5).TimeFrame() Agregación de velas analizada por la estrategia. Cámbielo para que coincida con el período de tiempo del gráfico utilizado en MetaTrader.

Notas de implementación

  • Highest y Lowest están vinculados a la suscripción de velas a través del Bind API de alto nivel, por lo que no hay colas de indicadores manuales. requerido.
  • StartProtection reproduce las distancias originales de stop-loss y take-profit en unidades de precio absoluto. StockSharp maneja el colocación del pedido una vez que aparece una nueva posición.
  • Los trailingstops se implementan monitoreando cada vela completada. Cuando el precio retrocede según el número configurado de puntos desde el mejor precio alcanzado después de la entrada, la posición se cierra con una orden de mercado.
  • La clase almacena los niveles de reversión ejecutados más recientes (_executedHighLevel y _executedLowLevel) para evitar duplicados. entradas. Esto es equivalente a las variables Reverse_High / Reverse_Low en el código MQL.
  • El campo _lastSignalTime refleja la variable TimeN y bloquea múltiples órdenes dentro de la misma vela, evitando presentaciones dobles accidentales en mercados ilíquidos.

Pautas de uso

  1. Adjunte la estrategia a una cartera que admita el instrumento y el tipo de vela seleccionados.
  2. Ajuste OrderVolume para cumplir con el tamaño del contrato y las reglas de gestión de riesgos de su corredor.
  3. Ajuste ReversePoint para que coincida con la volatilidad del activo negociado. Los valores más altos producen menos cambios pero más significativos.
  4. Verifique que StopLossPoints, TakeProfitPoints y TrailingStopPoints sean compatibles con el valor PriceStep.
  5. Ejecute una prueba retrospectiva en StockSharp Designer o Backtester para confirmar el comportamiento antes de operar con capital real.
  6. Supervise la salida del registro: los mensajes informativos resaltarán los cambios de posición y pueden ayudar a validar la conversión.

Debido a que el indicador RPoint se aproxima con componentes integrados, se observan diferencias menores con respecto a la ejecución de MetaTrader. posible en datos históricos con lagunas o diferentes reglas de redondeo. Valide siempre los resultados con sus propios feeds de datos de mercado antes de confiar en la estrategia en producción.

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