Ver en GitHub

MARE5.1 Estrategia de cruce de turnos

Descripción general

La estrategia MARE5.1 es una adaptación de C# del asesor experto MetaTrader 4 MARE5_1.mq4. El robot original operaba con datos M1 y se basaba en un par de promedios móviles simples evaluados en tres compensaciones históricas diferentes para detectar cambios de régimen. Esta implementación StockSharp reproduce el comportamiento con parámetros configurables, adjunta órdenes de protección al estilo MetaTrader y expone un filtro de ventana de negociación detallado.

Lógica de trading

  1. Datos de mercado
    • Una suscripción de vela única definida por CandleType (predeterminado: 1 minuto) alimenta los cálculos.
    • Cada vela se procesa sólo después de cerrarse para evitar el uso de barras a medio formar.
  2. Indicadores
    • Dos instancias SimpleMovingAverage representan los componentes rápido (FastPeriod) y lento (SlowPeriod).
    • Ambos promedios se desplazan hacia adelante en MovingAverageShift, exactamente como el argumento ma_shift en la función MQL iMA.
    • Se obtienen copias retrasadas adicionales de cada promedio con turnos de MovingAverageShift + 2 y MovingAverageShift + 5 para reflejar las llamadas originales de iMA(..., shift=2/5).
  3. Detección de señal
    • La diferencia entre los promedios debe exceder al menos un paso de precio (Point en términos de MetaTrader). Si el instrumento tiene cero PriceStep, cualquier diferencia positiva es suficiente.
    • Vender configuración:
      • La vela anterior debe ser bajista (Close < Open).
      • El promedio lento desplazado actual es mayor que el promedio rápido.
      • Dos y cinco velas atrás, el promedio rápido todavía estaba por encima del promedio lento, lo que indica un cambio de impulso.
    • Comprar configuración:
      • La vela anterior debe ser alcista (Close > Open).
      • El promedio rápido desplazado actual es mayor que el promedio lento.
      • Dos y cinco velas atrás, el promedio lento seguía liderando, confirmando una transición de condiciones bajistas a alcistas.
    • Sólo puede haber una posición abierta a la vez, replicando la guardia OrdersTotal() < 1 del EA.
  4. Filtro de tiempo
    • Solo se permite operar cuando la hora de cierre de la vela evaluada cae dentro del intervalo [TimeOpenHour, TimeCloseHour].
    • Si la hora de finalización es menor que la hora de inicio, la ventana se considera durante la noche (por ejemplo, de 22 a 5).

Gestión del riesgo

  • StartProtection está configurado con una distancia de stop-loss y take-profit convertida de MetaTrader puntos en compensaciones de precio absoluto utilizando el instrumento PriceStep.
  • No se implementa ningún trailing stop porque el código original declaraba TrailingStop pero nunca lo usó.
  • Los pedidos se envían con el volumen definido por TradeVolume. La estrategia no piramidal ni escala posiciones.

Parámetros

Nombre Descripción Predeterminado Notas
TradeVolume Tamaño del lote para entradas al mercado. 7.8 Redondeado según reglas de intercambio por el conector StockSharp.
FastPeriod Período de la media móvil simple rápida. 13 Controla la rapidez con la que reacciona la estrategia a los cambios de precios.
SlowPeriod Período de la media móvil simple lenta. 55 Proporciona la referencia de tendencia a largo plazo.
MovingAverageShift Desplazamiento hacia adelante aplicado a ambas medias móviles. 2 Coincide con el parámetro ma_shift de la función MQL iMA.
StopLossPoints Distancia de parada de protección en MetaTrader puntos. 80 Convertido a un desplazamiento absoluto a través del instrumento PriceStep.
TakeProfitPoints Distancia objetivo de ganancias en MetaTrader puntos. 110 Establezca en 0 para deshabilitar la obtención de ganancias.
TimeOpenHour Comienzo de la ventana de negociación permitida (hora, 0–23). 8 Evaluado frente al tiempo de cierre de la vela.
TimeCloseHour Fin de la ventana de negociación permitida (hora, 0 a 23). 14 Puede ser inferior a TimeOpenHour para abarcar la medianoche.
CandleType Plazo utilizado para la suscripción de velas. 1 minute Se puede proporcionar cualquier otro valor TimeFrame().

Notas de implementación

  • El indicador Shift se utiliza internamente para reproducir las compensaciones históricas exactas de la implementación MQL sin acceder directamente a los buffers del indicador.
  • IsDifferenceSatisfied encapsula la comparación de umbral de puntos, manteniendo la estrategia compatible con instrumentos que tienen diferentes tamaños de ticks.
  • La verificación de la ventana de negociación utiliza tiempos de cierre de velas, que es la mejor aproximación de Hour() de MetaTrader cuando solo se procesan velas terminadas.
  • Todos los comentarios están escritos en inglés y el código se basa únicamente en el API (SubscribeCandles().Bind(...)) de alto nivel como lo exigen las pautas del proyecto.

Diferencias en comparación con la versión MQL

  • Las señales se evalúan en velas cerradas, lo que elimina el posible repintado que podría ocurrir en los ticks dentro de la barra en MetaTrader.
  • Las órdenes de limitación de pérdidas y toma de ganancias las maneja StartProtection en lugar de adjuntarlas manualmente a cada llamada de OrderSend.
  • La entrada TrailingStop no utilizada se omitió intencionalmente para evitar exponer un parámetro no funcional.
  • El filtro de tiempo admite sesiones nocturnas por diseño, mientras que el EA original asumía implícitamente TimeOpen <= TimeClose.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MARE5.1 Shift Crossover: SMA crossover with ATR stops.
/// </summary>
public class Mare51ShiftCrossoverStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastSmaLength;
	private readonly StrategyParam<int> _slowSmaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public Mare51ShiftCrossoverStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastSmaLength = Param(nameof(FastSmaLength), 13)
			.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");
		_slowSmaLength = Param(nameof(SlowSmaLength), 55)
			.SetDisplay("Slow SMA", "Slow SMA period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastSmaLength { get => _fastSmaLength.Value; set => _fastSmaLength.Value = value; }
	public int SlowSmaLength { get => _slowSmaLength.Value; set => _slowSmaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
		var fastEma = new ExponentialMovingAverage { Length = FastSmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowSmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}