Ver en GitHub

Estrategia Hull Ma Adx

Estrategia basada en Hull Moving Average y ADX. Entra en largo cuando el HMA sube y ADX > 25 (tendencia fuerte). Entra en corto cuando el HMA baja y ADX > 25 (tendencia fuerte). Sale cuando ADX < 20 (tendencia debilitándose).

Las pruebas indican un rendimiento anual promedio de aproximadamente 178%. Funciona mejor en el mercado de acciones.

Hull MA muestra la tendencia, mientras que ADX confirma su intensidad. Las entradas siguen la pendiente de Hull cuando ADX indica fortaleza.

Efectiva para operadores que se centran en tendencias suaves con confirmación. Los stops basados en ATR mantienen las pérdidas bajo control.

Detalles

  • Criterios de entrada:
    • Largo: HullMA turning up && ADX > 25
    • Corto: HullMA turning down && ADX > 25
  • Largo/Corto: Ambos
  • Criterios de salida: reversión de Hull MA
  • Stops: Basados en ATR usando AtrMultiplier
  • Valores predeterminados:
    • HmaPeriod = 9
    • AdxPeriod = 14
    • AtrMultiplier = 2m
    • CandleType = TimeSpan.FromMinutes(15).TimeFrame()
  • Filtros:
    • Categoría: Tendencia
    • Dirección: Ambos
    • Indicadores: Hull MA, Moving Average, ADX
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Medio plazo
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
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;

using StockSharp.Algo;
using StockSharp.Algo.Candles;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on Hull Moving Average and ADX.
/// Enters long when HMA increases and ADX > 25 (strong trend).
/// Enters short when HMA decreases and ADX > 25 (strong trend).
/// Exits when ADX < 20 (weakening trend).
/// </summary>
public class HullMaAdxStrategy : Strategy
{
	private readonly StrategyParam<int> _hmaPeriod;
	private readonly StrategyParam<int> _adxPeriod;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _stopLossPercent;

	private HullMovingAverage _hma;
	private AverageDirectionalIndex _adx;
	private AverageTrueRange _atr;

	private decimal _prevHmaValue;
	private decimal _prevAdxValue;
	private int _cooldown;
	private bool _hasPrevSlope;
	private bool _prevSlopeUp;

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

	/// <summary>
	/// ADX indicator period.
	/// </summary>
	public int AdxPeriod
	{
		get => _adxPeriod.Value;
		set => _adxPeriod.Value = value;
	}

	/// <summary>
	/// Bars to wait between trades.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// ATR multiplier for stop loss calculation.
	/// </summary>
	public decimal AtrMultiplier
	{
		get => _atrMultiplier.Value;
		set => _atrMultiplier.Value = value;
	}

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

	/// <summary>
	/// Stop-loss percentage.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="HullMaAdxStrategy"/>.
	/// </summary>
	public HullMaAdxStrategy()
	{
		_hmaPeriod = Param(nameof(HmaPeriod), 9)
			.SetDisplay("HMA Period", "Period for Hull Moving Average calculation", "Indicators")
			
			.SetOptimize(5, 15, 2);

		_adxPeriod = Param(nameof(AdxPeriod), 14)
			.SetDisplay("ADX Period", "Period for Average Directional Movement Index", "Indicators")
			
			.SetOptimize(10, 20, 2);

		_cooldownBars = Param(nameof(CooldownBars), 80)
			.SetRange(1, 200)
			.SetDisplay("Cooldown Bars", "Bars between trades", "General");

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetDisplay("ATR Multiplier", "ATR multiplier for stop loss calculation", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe of data for strategy", "General");

		_stopLossPercent = Param(nameof(StopLossPercent), 1.0m)
			.SetNotNegative()
			.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk Management")
			
			.SetOptimize(0.5m, 2.0m, 0.5m);
	}

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

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

		_hma?.Reset();
		_adx?.Reset();
		_atr?.Reset();

		_prevHmaValue = 0;
		_prevAdxValue = 0;
		_cooldown = 0;
		_hasPrevSlope = false;
		_prevSlopeUp = false;
	}

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

		// Create indicators
		_hma = new() { Length = HmaPeriod };
		_adx = new() { Length = AdxPeriod };
		_atr = new() { Length = 14 };

		// Create subscription
		var subscription = SubscribeCandles(CandleType);

		// Process candles with indicators
		subscription
				.BindEx(_hma, _adx, _atr, ProcessCandle)
				.Start();

		// Setup chart visualization
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _hma);
			DrawOwnTrades(area);

			// ADX in separate area
			var adxArea = CreateChartArea();
			if (adxArea != null)
			{
				DrawIndicator(adxArea, _adx);
			}
		}

	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue hmaValue, IIndicatorValue adxValue, IIndicatorValue atrValue)
	{
		// Skip unfinished candles
		if (candle.State != CandleStates.Finished)
			return;

		var typedAdx = (AverageDirectionalIndexValue)adxValue;

		if (typedAdx.MovingAverage is not decimal adx)
			return;

		var hma = hmaValue.ToDecimal();

		// Detect HMA direction
		bool hmaIncreasing = hma > _prevHmaValue;
		bool hmaDecreasing = hma < _prevHmaValue;
		if (!_hasPrevSlope)
		{
			_hasPrevSlope = true;
			_prevSlopeUp = hmaIncreasing;
		}

		if (_cooldown > 0)
			_cooldown--;

		var slopeTurnedUp = !_prevSlopeUp && hmaIncreasing;
		var slopeTurnedDown = _prevSlopeUp && hmaDecreasing;

		// Trading logic
		if (_cooldown == 0 && slopeTurnedUp && Position <= 0)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
		else if (_cooldown == 0 && slopeTurnedDown && Position >= 0)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position != 0 && (slopeTurnedUp || slopeTurnedDown))
		{
			if (Position > 0)
				SellMarket();
			else
				BuyMarket();
			_cooldown = CooldownBars;
		}

		// Store current values for next candle
		_prevHmaValue = hma;
		_prevAdxValue = adx;
		_prevSlopeUp = hmaIncreasing;
	}
}