Ver en GitHub

Estrategia Ichimoku Stochastic

Estrategia basada en los indicadores Ichimoku Cloud y Stochastic Oscillator. Entra largo cuando el precio está por encima de Kumo (nube), Tenkan > Kijun, y el Stochastic está en sobrevendido (< 20). Entra corto cuando el precio está por debajo de Kumo, Tenkan < Kijun, y el Stochastic está en sobrecomprado (> 80).

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

Ichimoku define la tendencia y los niveles de soporte mientras Stochastic determina el momento de entrada en los retrocesos. Las operaciones se abren cuando el oscilador se resetea dentro de la dirección predominante de la nube.

Los traders que prefieren indicadores estructurados pueden encontrarlo práctico. Los stops de ATR cubren reversiones abruptas.

Detalles

  • Criterios de entrada:
    • Largo: Price > Cloud && StochK < 20
    • Corto: Price < Cloud && StochK > 80
  • Largo/Corto: Ambos
  • Criterios de salida:
    • Ruptura de la nube en dirección contraria
  • Stops: Usa los límites de la nube Ichimoku
  • Valores predeterminados:
    • TenkanPeriod = 9
    • KijunPeriod = 26
    • SenkouPeriod = 52
    • StochPeriod = 14
    • StochK = 3
    • StochD = 3
    • CandleType = TimeSpan.FromMinutes(30).TimeFrame()
  • Filtros:
    • Categoría: Reversión a la media
    • Dirección: Ambos
    • Indicadores: Ichimoku Cloud, Stochastic Oscillator
    • 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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on Ichimoku Cloud and Stochastic Oscillator indicators.
/// Enters long when price is above Kumo (cloud), Tenkan > Kijun, and Stochastic is oversold (< 20)
/// Enters short when price is below Kumo, Tenkan < Kijun, and Stochastic is overbought (> 80)
/// </summary>
public class IchimokuStochasticStrategy : Strategy
{
	private readonly StrategyParam<int> _tenkanPeriod;
	private readonly StrategyParam<int> _kijunPeriod;
	private readonly StrategyParam<int> _senkouPeriod;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<int> _stochK;
	private readonly StrategyParam<int> _stochD;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<DataType> _candleType;
	private int _cooldown;

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

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

	/// <summary>
	/// Senkou Span period
	/// </summary>
	public int SenkouPeriod
	{
		get => _senkouPeriod.Value;
		set => _senkouPeriod.Value = value;
	}

	/// <summary>
	/// Stochastic %K period
	/// </summary>
	public int StochPeriod
	{
		get => _stochPeriod.Value;
		set => _stochPeriod.Value = value;
	}
	
	/// <summary>
	/// Stochastic %K smoothing period
	/// </summary>
	public int StochK
	{
		get => _stochK.Value;
		set => _stochK.Value = value;
	}
	
	/// <summary>
	/// Stochastic %D period
	/// </summary>
	public int StochD
	{
		get => _stochD.Value;
		set => _stochD.Value = value;
	}

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

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

	/// <summary>
	/// Constructor
	/// </summary>
	public IchimokuStochasticStrategy()
	{
		_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Tenkan-sen Period", "Period for Tenkan-sen line", "Ichimoku")
			
			.SetOptimize(7, 12, 1);

		_kijunPeriod = Param(nameof(KijunPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("Kijun-sen Period", "Period for Kijun-sen line", "Ichimoku")
			
			.SetOptimize(20, 30, 2);

		_senkouPeriod = Param(nameof(SenkouPeriod), 52)
			.SetGreaterThanZero()
			.SetDisplay("Senkou Span Period", "Period for Senkou Span B line", "Ichimoku")
			
			.SetOptimize(40, 60, 5);

		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Period", "Period for Stochastic Oscillator", "Stochastic")
			
			.SetOptimize(10, 20, 2);
			
		_stochK = Param(nameof(StochK), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %K", "Smoothing for Stochastic %K line", "Stochastic")
			
			.SetOptimize(1, 5, 1);
			
		_stochD = Param(nameof(StochD), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %D", "Period for Stochastic %D line", "Stochastic")
			
			.SetOptimize(1, 5, 1);

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

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

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

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

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

		// Create indicators
		var ichimoku = new Ichimoku
		{
			Tenkan = { Length = TenkanPeriod },
			Kijun = { Length = KijunPeriod },
			SenkouB = { Length = SenkouPeriod }
		};

		var stochastic = new StochasticOscillator
		{
			K = { Length = StochK },
			D = { Length = StochD },
		};

		// Subscribe to candles and bind indicators
		var subscription = SubscribeCandles(CandleType);
		
		subscription
			.BindEx(ichimoku, stochastic, ProcessCandle)
			.Start();

		// Setup chart visualization if available
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ichimoku);
			
			// Create a separate area for Stochastic
			var stochArea = CreateChartArea();
			if (stochArea != null)
			{
				DrawIndicator(stochArea, stochastic);
			}
			
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue, IIndicatorValue stochValue)
	{
		// Skip unfinished candles
		if (candle.State != CandleStates.Finished)
			return;
		
		// Check if strategy is ready to trade
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Get additional values from Ichimoku
		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;

		// Current price (close of the candle)
		var price = candle.ClosePrice;
		
		// Check if price is above/below Kumo cloud
		var isAboveKumo = price > Math.Max(senkouA, senkouB);
		var isBelowKumo = price < Math.Min(senkouA, senkouB);
		
		// Check Tenkan/Kijun cross (trend direction)
		var isBullishCross = tenkan > kijun;
		var isBearishCross = tenkan < kijun;

		var stochTyped = (StochasticOscillatorValue)stochValue;

		// Get Stochastic %K value
		if (stochTyped.K is not decimal stochasticK)
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		// Trading logic
		if (isAboveKumo && isBullishCross && stochasticK < 15 && Position <= 0)
		{
			BuyMarket(Volume + Math.Abs(Position));
			_cooldown = CooldownBars;
		}
		else if (isBelowKumo && isBearishCross && stochasticK > 85 && Position >= 0)
		{
			SellMarket(Volume + Math.Abs(Position));
			_cooldown = CooldownBars;
		}
		else if (isBearishCross && Position > 0)
		{
			SellMarket(Position);
			_cooldown = CooldownBars;
		}
		else if (isBullishCross && Position < 0)
		{
			BuyMarket(Math.Abs(Position));
			_cooldown = CooldownBars;
		}
	}
}