Ver en GitHub

Estrategia Donchian Stochastic

Estrategia Donchian Channel + Stochastic. La estrategia entra al mercado cuando el precio rompe el Canal de Donchian con el Stochastic confirmando condiciones de sobreventa/sobrecompra.

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

Las rupturas más allá del canal de Donchian se confirman con el impulso del Stochastic. Las operaciones comienzan en cuanto el precio escapa del rango y el oscilador lo confirma.

Útil para traders que esperan un seguimiento inmediato. Un múltiplo de ATR establece el stop.

Detalles

  • Criterios de entrada:
    • Largo: Close > DonchianHigh && StochK < 20
    • Corto: Close < DonchianLow && StochK > 80
  • Largo/Corto: Ambos
  • Criterios de salida: Fallo de ruptura o señal opuesta
  • Stops: Basados en porcentaje usando StopLossPercent
  • Valores predeterminados:
    • DonchianPeriod = 20
    • StochPeriod = 14
    • StochK = 3
    • StochD = 3
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
    • StopLossPercent = 2m
  • Filtros:
    • Categoría: Ruptura
    • Dirección: Ambos
    • Indicadores: Donchian Channel, 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>
/// Donchian Channel + Stochastic strategy.
/// Strategy enters the market when the price breaks out of Donchian Channel with Stochastic confirming oversold/overbought conditions.
/// </summary>
public class DonchianStochasticStrategy : Strategy
{
	private readonly StrategyParam<int> _donchianPeriod;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<int> _stochK;
	private readonly StrategyParam<int> _stochD;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<decimal> _stopLossPercent;

	// Indicators
	private DonchianChannels _donchian;
	private StochasticOscillator _stochastic;
	private int _cooldown;

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

	/// <summary>
	/// Stochastic period.
	/// </summary>
	public int StochPeriod
	{
		get => _stochPeriod.Value;
		set => _stochPeriod.Value = value;
	}

	/// <summary>
	/// Stochastic %K 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>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

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

	/// <summary>
	/// Constructor.
	/// </summary>
	public DonchianStochasticStrategy()
	{
		_donchianPeriod = Param(nameof(DonchianPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Donchian Period", "Donchian Channel lookback period", "Indicators")
			
			.SetOptimize(10, 50, 5);

		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Period", "Stochastic oscillator period", "Indicators")
			
			.SetOptimize(5, 30, 5);

		_stochK = Param(nameof(StochK), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %K", "Stochastic %K period", "Indicators")
			
			.SetOptimize(1, 10, 1);

		_stochD = Param(nameof(StochD), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic %D", "Stochastic %D period", "Indicators")
			
			.SetOptimize(1, 10, 1);

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

		_cooldownBars = Param(nameof(CooldownBars), 100)
			.SetRange(5, 500)
			.SetDisplay("Cooldown Bars", "Bars between trades", "General");

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
			
			.SetOptimize(1m, 5m, 0.5m);
	}

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

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

		_donchian = null;
		_stochastic = null;
		_cooldown = 0;
	}

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

		// Create indicators
		_donchian = new DonchianChannels
		{
			Length = DonchianPeriod
		};

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

		// Enable position protection
		var takeProfitUnit = new Unit(0, UnitTypes.Absolute); // No take profit - we'll exit based on strategy rules
		var stopLossUnit = new Unit(StopLossPercent, UnitTypes.Percent);
		StartProtection(takeProfitUnit, stopLossUnit);

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

		// Setup chart
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _donchian);
			
			var secondArea = CreateChartArea();
			if (secondArea != null)
			{
				DrawIndicator(secondArea, _stochastic);
			}
			
			DrawOwnTrades(area);
		}
	}

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

		// Check if strategy is ready to trade
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var donchianTyped = (DonchianChannelsValue)donchianValue;
		
		if (donchianTyped.UpperBand is not decimal upperBand ||
			donchianTyped.LowerBand is not decimal lowerBand ||
			donchianTyped.Middle is not decimal middleBand)
		{
			return;
		}

		var stochTyped = (StochasticOscillatorValue)stochValue;
		
		if (stochTyped.K is not decimal stochK || stochTyped.D is not decimal stochD)
		{
			return;
		}

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

		// Trading logic:
		// Enter in trend direction with stochastic confirmation.
		if (candle.ClosePrice >= middleBand && stochK > 55 && Position == 0)
		{
			BuyMarket();
			_cooldown = CooldownBars;
			LogInfo($"Long entry: Price={candle.ClosePrice}, Middle Band={middleBand}, Stochastic %K={stochK}");
		}
		else if (candle.ClosePrice <= middleBand && stochK < 45 && Position == 0)
		{
			SellMarket();
			_cooldown = CooldownBars;
			LogInfo($"Short entry: Price={candle.ClosePrice}, Middle Band={middleBand}, Stochastic %K={stochK}");
		}
		// Exit long position when price falls below middle band
		else if (Position > 0 && candle.ClosePrice < middleBand)
		{
			SellMarket();
			_cooldown = CooldownBars;
			LogInfo($"Long exit: Price={candle.ClosePrice}, Middle Band={middleBand}");
		}
		// Exit short position when price rises above middle band
		else if (Position < 0 && candle.ClosePrice > middleBand)
		{
			BuyMarket();
			_cooldown = CooldownBars;
			LogInfo($"Short exit: Price={candle.ClosePrice}, Middle Band={middleBand}");
		}
	}
}