Ver en GitHub

Estrategia de Reversión del Canal Donchian

Los Canales Donchian marcan los máximos y mínimos recientes durante un período elegido. Los precios que perforan esos límites y luego se revierten pueden señalar agotamiento. Esta estrategia observa cierres de vuelta dentro del canal después de una ruptura breve.

Las pruebas indican una rentabilidad anual media de aproximadamente el 157%. Funciona mejor en el mercado de criptomonedas.

Si el cierre anterior estaba por debajo de la banda inferior y el cierre actual vuelve a subir por encima de ella, se toma una operación larga. A la inversa, si el cierre anterior estaba por encima de la banda superior y el precio cae de vuelta dentro, se abre una posición corta. Un stop porcentual gestiona el riesgo en ambos casos.

Al operar solo después de una ruptura fallida, este enfoque intenta capturar movimientos falsos que se retraen rápidamente.

Detalles

  • Criterios de entrada: El precio cierra de vuelta dentro del Canal Donchian tras superar la banda superior o inferior.
  • Largo/Corto: Ambos.
  • Criterios de salida: Stop-loss.
  • Stops: Sí, basado en porcentaje.
  • Valores predeterminados:
    • Period = 20
    • StopLoss = 2%
    • CandleType = 15 minute
  • Filtros:
    • Categoría: Reversión
    • Dirección: Ambos
    • Indicadores: Donchian Channel
    • Stops: Sí
    • Complejidad: Básico
    • Marco temporal: Intradía
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Medio
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Donchian Reversal strategy.
/// Enters long when price bounces from the lower Donchian Channel band.
/// Enters short when price bounces from the upper Donchian Channel band.
/// Exits at middle band.
/// Uses cooldown to control trade frequency.
/// </summary>
public class DonchianReversalStrategy : Strategy
{
	private readonly StrategyParam<int> _period;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cooldownBars;

	private decimal _prevClose;
	private int _cooldown;

	/// <summary>
	/// Donchian period.
	/// </summary>
	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

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

	/// <summary>
	/// Cooldown bars.
	/// </summary>
	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public DonchianReversalStrategy()
	{
		_period = Param(nameof(Period), 20)
			.SetRange(10, 40)
			.SetDisplay("Period", "Period for Donchian Channel", "Indicators");

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

		_cooldownBars = Param(nameof(CooldownBars), 500)
			.SetRange(1, 1000)
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
	}

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

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

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

		_prevClose = 0;
		_cooldown = 0;

		var donchian = new DonchianChannels { Length = Period };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(donchian, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue donchianIv)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!donchianIv.IsFormed)
			return;

		var dv = (IDonchianChannelsValue)donchianIv;

		if (dv.UpperBand is not decimal upper ||
			dv.LowerBand is not decimal lower ||
			dv.Middle is not decimal middle)
			return;

		if (_prevClose == 0)
		{
			_prevClose = candle.ClosePrice;
			return;
		}

		if (_cooldown > 0)
		{
			_cooldown--;
			_prevClose = candle.ClosePrice;
			return;
		}

		// Bounce from lower band = bullish
		var bouncedFromLower = _prevClose <= lower && candle.ClosePrice > lower;
		// Bounce from upper band = bearish
		var bouncedFromUpper = _prevClose >= upper && candle.ClosePrice < upper;

		if (Position == 0 && bouncedFromLower)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}
		else if (Position == 0 && bouncedFromUpper)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position > 0 && candle.ClosePrice >= middle && bouncedFromUpper)
		{
			SellMarket();
			_cooldown = CooldownBars;
		}
		else if (Position < 0 && candle.ClosePrice <= middle && bouncedFromLower)
		{
			BuyMarket();
			_cooldown = CooldownBars;
		}

		_prevClose = candle.ClosePrice;
	}
}