Ver en GitHub

Estrategia de Hoop Master Breakout

Descripción general

  • Convertida del asesor experto de MetaTrader 5 "Hoop master 2" por Vladimir Karputov.
  • Construye una caja de ruptura alrededor del precio actual y arma tanto órdenes de compra como de venta stop cada vez que cierra una nueva vela.
  • Automáticamente replica el comportamiento de MT5 de duplicar el tamaño del lote después de una operación perdedora y restablecerlo después de un ciclo rentable.

Lógica de negociación

  1. Suscribirse a la serie de velas configurada y esperar solo velas terminadas. Una nueva vela actúa como el "tick" que vuelve a armar las órdenes pendientes.
  2. Cuando la estrategia está plana:
    • Colocar un buy stop IndentPips puntos por encima del último cierre.
    • Colocar un sell stop IndentPips puntos por debajo del último cierre.
    • Convertir los pips de MetaTrader en unidades de precio absolutas usando el PriceStep del instrumento y el ajuste de dígitos fraccionarios (×10 para cotizaciones de 3 o 5 decimales).
  3. Cada orden pendiente almacena sus propios niveles de stop-loss y take-profit. Una vez que la orden se ejecuta, la orden opuesta se cancela y la protección almacenada se recrea con órdenes nativas de bolsa (SellStop/SellLimit para largos, BuyStop/BuyLimit para cortos).
  4. Si una orden protectora cierra la posición, la orden adjunta restante se cancela para evitar salidas duplicadas.
  5. La lógica de trailing stop opcional mueve el stop protector a favor de la operación una vez que el precio ha avanzado al menos TrailingStopPips y la mejora supera TrailingStepPips.
  6. Después de cada ciclo de plano a plano, se evalúa el PnL realizado. Un ciclo negativo multiplica el volumen de trabajo por LossMultiplier; de lo contrario, el volumen se restablece al Volume base.

Parámetros

Parámetro Descripción Predeterminado Notas
Volume Tamaño de orden base usado al armar nuevas órdenes pendientes. Propiedad Volume de la estrategia Se duplica después de un ciclo perdedor según LossMultiplier.
StopLossPips Distancia de stop-loss en pips de MetaTrader. 25 Convertido a precio usando el asistente de tamaño de pip. 0 desactiva el stop.
TakeProfitPips Distancia de take-profit en pips de MetaTrader. 70 Convertido a precio. 0 desactiva el objetivo.
TrailingStopPips Distancia entre el precio y el trailing stop. 0 Establecer en 0 para desactivar el trailing.
TrailingStepPips Mejora mínima antes de mover el trailing stop. 5 Solo se usa cuando TrailingStopPips es mayor que cero.
IndentPips Desplazamiento añadido al último cierre al armar órdenes pendientes. 15 Asegura que las órdenes stop estén fuera del ruido de precio inmediato.
LossMultiplier Multiplicador aplicado al siguiente ciclo después de una pérdida. 2 Implementa el dimensionamiento de posición estilo martingala del EA MT5.
CandleType Tipo/marco temporal de vela que activa el re-armado. Marco temporal de 1 hora Cambiar para coincidir con el gráfico usado en las pruebas.

Gestión monetaria y protecciones

  • Cada entrada ejecutada reconstruye inmediatamente su stop-loss y take-profit como órdenes reales de bolsa para que las protecciones funcionen incluso si la estrategia se desconecta.
  • StartProtection() se invoca durante el inicio para liquidar posiciones extraviadas de ejecuciones anteriores.
  • La lógica de trailing ajusta las órdenes stop existentes en lugar de enviar salidas de mercado, manteniendo el comportamiento consistente con las modificaciones de MT5.

Notas de implementación

  • Sigue la API de alto nivel de StockSharp: suscripciones de velas, BuyStop/SellStop para entradas, y BuyLimit/SellLimit para órdenes de take-profit.
  • Todos los comentarios textuales dentro del código están en inglés, mientras que la documentación externa (este README y traducciones) proporciona descripciones detalladas para los usuarios.
  • La conversión de pips de MetaTrader respeta los símbolos de dígitos fraccionarios (3 o 5 decimales) multiplicando el paso del broker por 10, coincidiendo con la lógica m_adjusted_point del EA original.
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;

public class HoopMasterBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public HoopMasterBreakoutStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}