Ver en GitHub

Estrategia de Reversión Hull MA

La Hull Moving Average responde rápidamente a los cambios de precio manteniéndose suave. Un cambio en su dirección puede anticipar una reversión a corto plazo. Esta estrategia monitorea valores consecutivos de Hull MA y opera cuando la pendiente cambia.

Las pruebas indican una rentabilidad anual media de aproximadamente el 154%. Funciona mejor en el mercado de acciones.

Cuando la media móvil pasa de caer a subir, se abre una posición larga. Un cambio de subir a caer inicia una posición corta. El riesgo se controla mediante un stop basado en ATR colocado más allá de la vela reciente.

Las salidas dependen de ese stop de protección, capturando una porción del movimiento que sigue al cambio de momentum resaltado por la Hull MA.

Detalles

  • Criterios de entrada: La pendiente de la Hull MA cambia de dirección.
  • Largo/Corto: Ambos.
  • Criterios de salida: Stop-loss.
  • Stops: Sí, basado en ATR.
  • Valores predeterminados:
    • HmaPeriod = 9
    • AtrMultiplier = 2 ATR
    • CandleType = 15 minute
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: Hull MA, ATR
    • Stops: Sí
    • Complejidad: Básico
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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;

/// <summary>
/// Hull MA Reversal strategy.
/// Enters long when Hull MA changes direction from down to up.
/// Enters short when Hull MA changes direction from up to down.
/// Uses cooldown to control trade frequency.
/// </summary>
public class HullMaReversalStrategy : Strategy
{
	private readonly StrategyParam<int> _hmaPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _prevHma;
	private decimal _prevPrevHma;
	private int _cooldown;

	/// <summary>
	/// HMA period.
	/// </summary>
	public int HmaPeriod
	{
		get => _hmaPeriod.Value;
		set => _hmaPeriod.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Cooldown bars.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public HullMaReversalStrategy()
	{
		_hmaPeriod = Param(nameof(HmaPeriod), 9)
			.SetRange(5, 20)
			.SetDisplay("HMA Period", "Period for Hull Moving Average", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_cooldownBars = Param(nameof(CooldownBars), 500)
			.SetRange(1, 1000)
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHma = default;
		_prevPrevHma = default;
		_cooldown = default;
	}

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

		_prevHma = 0;
		_prevPrevHma = 0;
		_cooldown = 0;

		var hma = new HullMovingAverage { Length = HmaPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_prevHma == 0)
		{
			_prevHma = hmaValue;
			return;
		}

		if (_prevPrevHma == 0)
		{
			_prevPrevHma = _prevHma;
			_prevHma = hmaValue;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevPrevHma = _prevHma;
			_prevHma = hmaValue;
			return;
		}

		// Direction change detection
		var dirChangedUp = _prevHma < _prevPrevHma && hmaValue > _prevHma;
		var dirChangedDown = _prevHma > _prevPrevHma && hmaValue < _prevHma;

		if (Position == 0 && dirChangedUp)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
		else if (Position == 0 && dirChangedDown)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position > 0 && dirChangedDown)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position < 0 && dirChangedUp)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}

		_prevPrevHma = _prevHma;
		_prevHma = hmaValue;
	}
}