Ver en GitHub

Estrategia de Ichimoku Hurst Exponent

La estrategia Ichimoku Hurst Exponent se basa en el indicador Ichimoku Kinko Hyo con filtro de exponente Hurst.

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

Las señales se activan cuando Hurst confirma cambios de tendencia en datos intradía (15m). Este método es adecuado para operadores activos.

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

Detalles

  • Criterios de entrada: ver implementación para condiciones de indicadores.
  • Largo/Corto: Ambos direcciones.
  • Criterios de salida: señal opuesta o lógica de stop.
  • Stops: Sí, usando cálculos basados en indicadores.
  • Valores predeterminados:
    • TenkanPeriod = 9
    • KijunPeriod = 26
    • SenkouSpanBPeriod = 52
    • HurstPeriod = 100
    • HurstThreshold = 0.5m
    • CandleType = TimeSpan.FromMinutes(15).TimeFrame()
  • Filtros:
    • Categoría: Seguimiento de tendencia
    • Dirección: Ambos
    • Indicadores: Hurst, Exponent
    • Stops: Sí
    • Complejidad: Intermedio
    • Marco temporal: Intradía (15m)
    • 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.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on Ichimoku Kinko Hyo indicator with Hurst exponent filter.
/// </summary>
public class IchimokuHurstStrategy : Strategy
{
	private readonly StrategyParam<int> _tenkanPeriod;
	private readonly StrategyParam<int> _kijunPeriod;
	private readonly StrategyParam<int> _senkouSpanBPeriod;
	private readonly StrategyParam<int> _hurstPeriod;
	private readonly StrategyParam<decimal> _hurstThreshold;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _signalCooldownBars;

	private Ichimoku _ichimoku;

	// Data for Hurst exponent calculations
	private readonly List<decimal> _prices = [];
	private decimal _hurstExponent;
	private decimal? _prevTenkan;
	private decimal? _prevKijun;
	private int _cooldownRemaining;

	/// <summary>
	/// Tenkan-sen (conversion line) period.
	/// </summary>
	public int TenkanPeriod
	{
		get => _tenkanPeriod.Value;
		set => _tenkanPeriod.Value = value;
	}

	/// <summary>
	/// Kijun-sen (base line) period.
	/// </summary>
	public int KijunPeriod
	{
		get => _kijunPeriod.Value;
		set => _kijunPeriod.Value = value;
	}

	/// <summary>
	/// Senkou Span B (leading span B) period.
	/// </summary>
	public int SenkouSpanBPeriod
	{
		get => _senkouSpanBPeriod.Value;
		set => _senkouSpanBPeriod.Value = value;
	}

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

	/// <summary>
	/// Hurst exponent threshold for trend strength.
	/// </summary>
	public decimal HurstThreshold
	{
		get => _hurstThreshold.Value;
		set => _hurstThreshold.Value = value;
	}

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

	/// <summary>
	/// Bars to wait between position changes.
	/// </summary>
	public int SignalCooldownBars
	{
		get => _signalCooldownBars.Value;
		set => _signalCooldownBars.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="IchimokuHurstStrategy"/>.
	/// </summary>
	public IchimokuHurstStrategy()
	{
		_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
		.SetDisplay("Tenkan Period", "Tenkan-sen (conversion line) period", "Ichimoku")
		
		.SetOptimize(5, 15, 1);

		_kijunPeriod = Param(nameof(KijunPeriod), 26)
		.SetDisplay("Kijun Period", "Kijun-sen (base line) period", "Ichimoku")
		
		.SetOptimize(20, 40, 2);

		_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
		.SetDisplay("Senkou Span B Period", "Senkou Span B (leading span B) period", "Ichimoku")
		
		.SetOptimize(40, 70, 5);

		_hurstPeriod = Param(nameof(HurstPeriod), 100)
		.SetDisplay("Hurst Period", "Hurst exponent calculation period", "Hurst Exponent")
		
		.SetOptimize(50, 200, 10);

		_hurstThreshold = Param(nameof(HurstThreshold), 0.5m)
		.SetDisplay("Hurst Threshold", "Hurst exponent threshold for trend strength", "Hurst Exponent")
		
		.SetOptimize(0.45m, 0.6m, 0.05m);

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

		_signalCooldownBars = Param(nameof(SignalCooldownBars), 6)
		.SetGreaterThanZero()
		.SetDisplay("Signal Cooldown", "Bars to wait between reversals", "Trading");
	}

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

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

		_prices.Clear();
		_hurstExponent = 0.5m; // Default Hurst exponent value
		_prevTenkan = null;
		_prevKijun = null;
		_cooldownRemaining = 0;
	}

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

		// Create Ichimoku indicator
		_ichimoku = new Ichimoku
		{
			Tenkan = { Length = TenkanPeriod },
			Kijun = { Length = KijunPeriod },
			SenkouB = { Length = SenkouSpanBPeriod }
		};

		// Create subscription and bind indicator
		var subscription = SubscribeCandles(CandleType);

		subscription
		.BindEx(_ichimoku, ProcessCandle)
		.Start();

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

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

		// Store Ichimoku values
		var ichimokuTyped = (IchimokuValue)ichimokuValue;

		if (ichimokuTyped.Tenkan is not decimal tenkan)
		return;

		if (ichimokuTyped.Kijun is not decimal kijun)
		return;

		if (ichimokuTyped.SenkouA is not decimal senkouA)
		return;

		if (ichimokuTyped.SenkouB is not decimal senkouB)
		return;

		// Update price data for Hurst exponent calculation
		_prices.Add(candle.ClosePrice);

		// Keep only the number of prices needed for Hurst calculation
		while (_prices.Count > HurstPeriod)
		_prices.RemoveAt(0);

		// Calculate Hurst exponent when we have enough data
		if (_prices.Count >= HurstPeriod)
		CalculateHurstExponent();

		// Continue with position checks
		if (!IsFormedAndOnlineAndAllowTrading())
		return;

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		// Check if price is above/below Kumo (cloud)
		var isPriceAboveKumo = candle.ClosePrice > Math.Max(senkouA, senkouB);
		var isPriceBelowKumo = candle.ClosePrice < Math.Min(senkouA, senkouB);

		if (_prevTenkan is decimal previousTenkan && _prevKijun is decimal previousKijun)
		{
			var crossUp = previousTenkan <= previousKijun && tenkan > kijun;
			var crossDown = previousTenkan >= previousKijun && tenkan < kijun;
			var longExit = Position > 0 && (isPriceBelowKumo || crossDown);
			var shortExit = Position < 0 && (isPriceAboveKumo || crossUp);

			if (longExit)
			{
				SellMarket(Position);
				_cooldownRemaining = SignalCooldownBars;
			}
			else if (shortExit)
			{
				BuyMarket(Math.Abs(Position));
				_cooldownRemaining = SignalCooldownBars;
			}
			else if (_cooldownRemaining == 0 && isPriceAboveKumo && crossUp && _hurstExponent > HurstThreshold && Position <= 0)
			{
				BuyMarket(Volume + Math.Abs(Position));
				_cooldownRemaining = SignalCooldownBars;
			}
			else if (_cooldownRemaining == 0 && isPriceBelowKumo && crossDown && _hurstExponent > HurstThreshold && Position >= 0)
			{
				SellMarket(Volume + Math.Abs(Position));
				_cooldownRemaining = SignalCooldownBars;
			}
		}

		_prevTenkan = tenkan;
		_prevKijun = kijun;
	}

	private void CalculateHurstExponent()
	{
		// This is a simplified Hurst exponent calculation using R/S analysis
		// Note: A full implementation would use multiple time scales

		// Calculate log returns
		List<decimal> logReturns = [];
		for (int i = 1; i < _prices.Count; i++)
		{
			if (_prices[i-1] != 0)
			logReturns.Add((decimal)Math.Log((double)(_prices[i] / _prices[i-1])));
		}

		if (logReturns.Count < 10)
		return;

		// Calculate mean
		decimal mean = logReturns.Sum() / logReturns.Count;

		// Calculate cumulative deviation series
		List<decimal> cumulativeDeviation = [];
		decimal sum = 0;

		foreach (var logReturn in logReturns)
		{
			sum += (logReturn - mean);
			cumulativeDeviation.Add(sum);
		}

		// Calculate range (max - min of cumulative deviation)
		decimal range = cumulativeDeviation.Max() - cumulativeDeviation.Min();

		// Calculate standard deviation
		decimal sumSquares = logReturns.Sum(x => (x - mean) * (x - mean));
		decimal stdDev = (decimal)Math.Sqrt((double)(sumSquares / logReturns.Count));

		if (stdDev == 0)
		return;

		// Calculate R/S statistic
		decimal rs = range / stdDev;

		// Hurst = log(R/S) / log(N)
		decimal logN = (decimal)Math.Log((double)logReturns.Count);
		if (logN != 0)
		_hurstExponent = (decimal)Math.Log((double)rs) / logN;

	}
}