Ver en GitHub

Estrategia Donchian Macd

Estrategia que combina la ruptura del Canal Donchian con la confirmación de tendencia mediante MACD.

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

La estrategia espera una ruptura de Donchian y verifica el momentum con MACD. Las operaciones largas o cortas siguen el movimiento una vez que MACD está de acuerdo.

Dirigida a entusiastas de las rupturas que desean confirmación. Los stops se colocan usando un multiplicador de ATR.

Detalles

  • Criterios de entrada:
    • Largo: Price breaks Donchian high && MACD > Signal
    • Corto: Price breaks Donchian low && MACD < Signal
  • Largo/Corto: Ambos
  • Criterios de salida: Reversión del MACD
  • Stops: Porcentual usando StopLossPercent
  • Valores predeterminados:
    • DonchianPeriod = 20
    • MacdFast = 12
    • MacdSlow = 26
    • MacdSignal = 9
    • StopLossPercent = 2m
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoría: Ruptura
    • Dirección: Ambos
    • Indicadores: Donchian Channel, MACD
    • 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.Candles;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy combining Donchian Channel breakout with MACD trend confirmation.
/// </summary>
public class DonchianMacdStrategy : Strategy
{
	private readonly StrategyParam<int> _donchianPeriod;
	private readonly StrategyParam<int> _macdFast;
	private readonly StrategyParam<int> _macdSlow;
	private readonly StrategyParam<int> _macdSignal;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private DonchianChannels _donchian;
	private MovingAverageConvergenceDivergenceSignal _macd;
	
	private decimal? _previousHighest;
	private decimal? _previousLowest;
	private decimal? _previousMacd;
	private decimal? _previousSignal;
	private decimal? _entryPrice;
	private int _cooldown;

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

	/// <summary>
	/// MACD fast period.
	/// </summary>
	public int MacdFast
	{
		get => _macdFast.Value;
		set => _macdFast.Value = value;
	}

	/// <summary>
	/// MACD slow period.
	/// </summary>
	public int MacdSlow
	{
		get => _macdSlow.Value;
		set => _macdSlow.Value = value;
	}

	/// <summary>
	/// MACD signal period.
	/// </summary>
	public int MacdSignal
	{
		get => _macdSignal.Value;
		set => _macdSignal.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>
	/// Candle type for strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="DonchianMacdStrategy"/>.
	/// </summary>
	public DonchianMacdStrategy()
	{
		_donchianPeriod = Param(nameof(DonchianPeriod), 20)
			.SetRange(5, 50)
			
			.SetDisplay("Donchian Period", "Channel lookback period", "Indicators");

		_macdFast = Param(nameof(MacdFast), 12)
			.SetRange(8, 20)
			
			.SetDisplay("MACD Fast Period", "Fast EMA period for MACD", "Indicators");

		_macdSlow = Param(nameof(MacdSlow), 26)
			.SetRange(20, 40)
			
			.SetDisplay("MACD Slow Period", "Slow EMA period for MACD", "Indicators");

		_macdSignal = Param(nameof(MacdSignal), 9)
			.SetRange(5, 15)
			
			.SetDisplay("MACD Signal Period", "Signal line period for MACD", "Indicators");

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

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetRange(1m, 5m)
			
			.SetDisplay("Stop-Loss %", "Stop-loss percentage from entry price", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
		_previousHighest = 0;
		_previousLowest = decimal.MaxValue;
		_previousMacd = 0;
		_previousSignal = 0;
		_entryPrice = null;
		_cooldown = 0;
		_donchian = null;
		_macd = null;
	}

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

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

		_macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFast },
				LongMa = { Length = MacdSlow },
			},
			SignalMa = { Length = MacdSignal }
		};

		// Create subscription and bind indicators
		var subscription = SubscribeCandles(CandleType);
		
		subscription
			.BindEx(_donchian, _macd, ProcessCandle)
			.Start();

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

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

		// Wait until strategy and indicators are ready
		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
		var signalValue = macdTyped.Signal;
		var macdDec = macdTyped.Macd;
		var isBullishCross = _previousMacd <= _previousSignal && macdDec > signalValue;
		var isBearishCross = _previousMacd >= _previousSignal && macdDec < signalValue;

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

		// Check for breakouts with MACD trend confirmation
		// Long entry: Price breaks above Donchian high and MACD > Signal
		if (_cooldown == 0 && candle.ClosePrice > _previousHighest * 1.001m && Position <= 0 && isBullishCross)
		{
			CancelActiveOrders();
			
			var volume = Volume + Math.Abs(Position);
			BuyMarket(volume);
			_entryPrice = candle.ClosePrice;
			_cooldown = CooldownBars;
		}
		// Short entry: Price breaks below Donchian low and MACD < Signal
		else if (_cooldown == 0 && candle.ClosePrice < _previousLowest * 0.999m && Position >= 0 && isBearishCross)
		{
			CancelActiveOrders();
			
			var volume = Volume + Math.Abs(Position);
			SellMarket(volume);
			_entryPrice = candle.ClosePrice;
			_cooldown = CooldownBars;
		}
		// MACD trend reversal exit
		else if ((Position > 0 && isBearishCross) ||
				 (Position < 0 && isBullishCross))
		{
			ClosePosition();
			_entryPrice = null;
			_cooldown = CooldownBars;
		}

		var donchianTyped = (DonchianChannelsValue)donchianValue;

		// Update previous values for next candle
		_previousHighest = donchianTyped.UpperBand;
		_previousLowest = donchianTyped.LowerBand;
		_previousMacd = macdDec;
		_previousSignal = signalValue;
	}
}