Ver en GitHub

Estrategia Donchian con Hurst Exponent

La estrategia Donchian Hurst Exponent opera basándose en rupturas del Canal Donchian con filtro de Hurst Exponent.

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

Las señales se activan cuando Donchian confirma cambios de tendencia en datos intradía (5m). Esto hace que el método sea adecuado para traders activos.

Los stops se basan en múltiplos de ATR y factores como DonchianPeriod, HurstPeriod. Ajuste estos valores predeterminados para equilibrar el riesgo y la recompensa.

Detalles

  • Criterios de entrada: consulte la implementación para conocer las condiciones de los indicadores.
  • Largo/Corto: Ambos direcciones.
  • Criterios de salida: señal opuesta o lógica de stop.
  • Stops: Sí, utilizando cálculos basados en indicadores.
  • Valores predeterminados:
    • DonchianPeriod = 20
    • HurstPeriod = 100
    • HurstThreshold = 0.5m
    • StopLossPercent = 2m
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: Donchian, Hurst, Exponent
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Intradía (5m)
    • 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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that trades based on Donchian Channel breakouts with Hurst Exponent filter.
/// Enters position when price breaks Donchian Channel with Hurst Exponent indicating trend persistence.
/// </summary>
public class DonchianHurstStrategy : Strategy
{
	private readonly StrategyParam<int> _donchianPeriod;
	private readonly StrategyParam<int> _hurstPeriod;
	private readonly StrategyParam<decimal> _hurstThreshold;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _hurstValue;
	private decimal? _previousUpper;
	private decimal? _previousLower;
	private decimal? _previousMiddle;

	/// <summary>
	/// Strategy parameter: Donchian Channel period.
	/// </summary>
	public int DonchianPeriod
	{
		get => _donchianPeriod.Value;
		set => _donchianPeriod.Value = value;
	}

	/// <summary>
	/// Strategy parameter: Hurst Exponent calculation period.
	/// </summary>
	public int HurstPeriod
	{
		get => _hurstPeriod.Value;
		set => _hurstPeriod.Value = value;
	}

	/// <summary>
	/// Strategy parameter: Hurst Exponent threshold for trend persistence.
	/// </summary>
	public decimal HurstThreshold
	{
		get => _hurstThreshold.Value;
		set => _hurstThreshold.Value = value;
	}

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

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public DonchianHurstStrategy()
	{
		_donchianPeriod = Param(nameof(DonchianPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Donchian Period", "Period for Donchian Channel indicator", "Indicator Settings");

		_hurstPeriod = Param(nameof(HurstPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Hurst Period", "Period for Hurst Exponent calculation", "Indicator Settings");

		_hurstThreshold = Param(nameof(HurstThreshold), 0.45m)
			.SetRange(0, 1)
			.SetDisplay("Hurst Threshold", "Minimum Hurst Exponent value for trend persistence (>0.5 is trending)", "Indicator Settings");

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss %", "Stop Loss percentage from entry price", "Risk Management");

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

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

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

		_hurstValue = 0;
		_previousUpper = null;
		_previousLower = null;
		_previousMiddle = null;
	}

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

		// Create Donchian Channel indicator
		var donchian = new DonchianChannels
		{
			Length = DonchianPeriod
		};

		// Create FractalDimension indicator for Hurst calculation
		// We use 1 - FractalDimension to get Hurst Exponent (H = 2 - D)
		var fractalDimension = new FractalDimension
		{
			Length = HurstPeriod
		};

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

		// Bind indicators to subscription and start
		subscription
			.BindEx(donchian, fractalDimension, ProcessIndicators)
			.Start();

		// Add chart visualization
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, donchian);
			DrawOwnTrades(area);
		}

		// Start position protection with percentage-based stop-loss
		StartProtection(
			takeProfit: new Unit(0), // No take profit, using Donchian Channel for exit
			stopLoss: new Unit(StopLossPercent, UnitTypes.Percent)
		);
	}

	private void ProcessIndicators(ICandleMessage candle, IIndicatorValue donchianValue, IIndicatorValue fractalDimensionValue)
	{
		// Skip unfinished candles
		if (candle.State != CandleStates.Finished)
			return;

		// --- FractalDimension logic (was ProcessFractalDimension) ---
		decimal fractalDimension = fractalDimensionValue.ToDecimal();
		_hurstValue = 2m - fractalDimension;

		// Log Hurst Exponent value periodically
		if (candle.OpenTime.Second == 0 && candle.OpenTime.Minute % 15 == 0)
		{
			LogInfo($"Current Hurst Exponent: {_hurstValue} (>{HurstThreshold} indicates trend persistence)");
		}

		// --- Donchian logic (was ProcessDonchianChannel) ---
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var donchianTyped = (DonchianChannelsValue)donchianValue;

		// Convert indicator values to decimal
		if (donchianTyped.UpperBand is not decimal upper ||
			donchianTyped.LowerBand is not decimal lower ||
			donchianTyped.Middle is not decimal middle)
		{
			return;
		}

		if (!_previousUpper.HasValue || !_previousLower.HasValue || !_previousMiddle.HasValue)
		{
			_previousUpper = upper;
			_previousLower = lower;
			_previousMiddle = middle;
			return;
		}

		if (_hurstValue > HurstThreshold)
		{
			if (candle.ClosePrice > _previousUpper.Value && Position <= 0)
				BuyMarket(Volume + Math.Abs(Position));
			else if (candle.ClosePrice < _previousLower.Value && Position >= 0)
				SellMarket(Volume + Math.Abs(Position));
		}

		if (Position > 0 && candle.ClosePrice < _previousMiddle.Value)
			SellMarket(Position);
		else if (Position < 0 && candle.ClosePrice > _previousMiddle.Value)
			BuyMarket(-Position);

		_previousUpper = upper;
		_previousLower = lower;
		_previousMiddle = middle;
	}
}