Ver en GitHub

Estrategia de Hoop Master

Descripción general

La estrategia Hoop Master es un sistema de ruptura pendiente que mantiene continuamente dos órdenes stop alrededor del precio actual. El asesor experto de MetaTrader 5 original coloca un buy stop por encima del mercado y un sell stop por debajo del mercado. Cuando un lado se activa, la orden opuesta se cancela y ambos lados se recrean con un volumen mayor. El port de StockSharp sigue la misma idea gestionando órdenes stop y un dimensionamiento martingala opcional dentro de una sola clase de estrategia.

La estrategia también puede adjuntar órdenes de stop-loss y take-profit protectoras a cualquier posición abierta. Un módulo de trailing stop mueve gradualmente el stop protector cuando el mercado avanza en la dirección de la operación.

Lógica de trading

  1. En cada vela completada, la estrategia recalcula los niveles de colocación para los stops de ruptura.
  2. Si no hay posición abierta, tanto un buy stop como un sell stop se registran a una distancia configurable en pips desde el bid/ask actual.
  3. Cuando cualquier stop pendiente se llena, el stop opuesto se elimina. Nuevos stops de ruptura se envían inmediatamente usando el doble del volumen base.
  4. Después de que se abre una operación, la estrategia crea órdenes independientes de stop-loss y take-profit. Un motor de trailing puede mover el stop hacia el precio una vez que el movimiento sea lo suficientemente grande.
  5. Cuando la posición se cierra, todas las órdenes de protección se cancelan y las órdenes de ruptura se re-inicializan con el volumen base en la siguiente señal.

Parámetros

Parámetro Descripción
Candle Type Tipo de datos de vela usado para la lógica barra a barra.
Order Volume Volumen base para cada orden de ruptura. El paso martingala usa el doble de esta cantidad.
Stop Loss (pips) Distancia en pips entre el precio de entrada y la orden stop protectora. Establecer en 0 para deshabilitar.
Take Profit (pips) Distancia en pips entre el precio de entrada y la orden objetivo protectora. Establecer en 0 para deshabilitar.
Trailing Stop (pips) Distancia usada al mover el trailing stop. Establecer en 0 para deshabilitar el trailing.
Trailing Step (pips) Mejora de precio mínima (en pips) requerida antes de que se actualice el trailing stop.
Indent (pips) Offset, medido en pips, añadido por encima del ask y por debajo del bid al colocar stops de ruptura.

Detalles de gestión de órdenes

  • La estrategia rastrea continuamente las mejores cotizaciones de bid/ask. Cuando las cotizaciones no están disponibles, recurre al último precio de operación o al cierre de vela.
  • Todas las órdenes se alinean al paso de precio del instrumento para evitar precios inválidos.
  • Las órdenes de stop y take-profit protectoras se reemplazan cada vez que aparece una nueva posición.
  • El trailing solo funciona cuando tanto la distancia de trailing como los parámetros de paso están por encima de cero. El stop se mueve en la dirección de la operación cuando la mejora deseada es suficientemente grande.

Notas

  • Asegúrate de que el bróker o simulador conectado soporte órdenes stop y límite para el instrumento seleccionado.
  • El paso martingala puede aumentar la exposición rápidamente. Ajusta el volumen base para mantenerse dentro de límites de riesgo aceptables.
  • La estrategia espera recibir datos de Nivel1 (bid/ask) junto con datos de velas para que los precios de ruptura puedan calcularse con precisión.
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 HoopMasterStrategy : 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 HoopMasterStrategy()
	{
		_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;
	}
}