Canal Donchian
Estrategia basada en el Canal Donchian.
Las pruebas indican un retorno anual promedio de aproximadamente 52%. Funciona mejor en el mercado de criptomonedas.
La Ruptura del Canal Donchian opera nuevos máximos y mínimos basados en el rango del canal. Un cierre más allá de la banda superior señala fortaleza, mientras que una caída por debajo de la banda inferior invita a cortos. Las salidas ocurren cuando el precio regresa al punto medio.
El canal se calcula a partir del máximo más alto y el mínimo más bajo durante una ventana de lookback. Cuando el precio perfora estos límites, el sistema espera una expansión de volatilidad y se posiciona en consecuencia.
Detalles
- Criterios de entrada: Señales basadas en Price Action.
- Largo/Corto: Ambos directions.
- Criterios de salida: Señal opuesta.
- Stops: No.
- Valores predeterminados:
ChannelPeriod= 20CandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Price Action
- Stops: No
- Complejidad: Básico
- Marco temporal: Intradía (5m)
- 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 Donchian Channel.
/// It enters long when the close breaks above the upper band of the previous candles and short below the lower band,
/// and exits when the close returns to the channel midpoint.
/// </summary>
public class DonchianChannelStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
// Current state
// Channel of the candles before the current one.
private decimal? _prevUpperBand;
private decimal? _prevLowerBand;
private decimal? _prevMiddle;
/// <summary>
/// Period for Donchian Channel.
/// </summary>
public int ChannelPeriod
{
get => _channelPeriod.Value;
set => _channelPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the Donchian Channel strategy.
/// </summary>
public DonchianChannelStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Channel Period", "Period for Donchian Channel calculation", "Indicators")
.SetOptimize(10, 50, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevUpperBand = null;
_prevLowerBand = null;
_prevMiddle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var donchian = new DonchianChannels { Length = ChannelPeriod };
// Create subscription and bind indicators
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(donchian, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, donchian);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue donchianValue)
{
if (candle.State != CandleStates.Finished)
return;
var donchianTyped = (DonchianChannelsValue)donchianValue;
if (donchianTyped.UpperBand is not decimal upperValue ||
donchianTyped.LowerBand is not decimal lowerValue ||
donchianTyped.Middle is not decimal midValue)
{
return;
}
// A close can only break out of the channel the candles before it formed.
var upper = _prevUpperBand;
var lower = _prevLowerBand;
var middle = _prevMiddle;
_prevUpperBand = upperValue;
_prevLowerBand = lowerValue;
_prevMiddle = midValue;
if (upper is not decimal up || lower is not decimal down || middle is not decimal mid)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (close > up && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
}
else if (close < down && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
}
else if (Position > 0 && close <= mid)
{
SellMarket(Position);
}
else if (Position < 0 && close >= mid)
{
BuyMarket(-Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import DonchianChannels
from StockSharp.Algo.Strategies import Strategy
class donchian_channel_strategy(Strategy):
"""
Strategy based on Donchian Channel.
Enters long when the close breaks above the upper band of the previous candles, short below the lower band.
Exits when the close returns to the channel midpoint.
"""
def __init__(self):
super(donchian_channel_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 20).SetGreaterThanZero().SetDisplay("Channel Period", "Period for Donchian Channel calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
# Channel of the candles before the current one.
self._prev_upper_band = None
self._prev_lower_band = None
self._prev_middle = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(donchian_channel_strategy, self).OnReseted()
# Channel of the candles before the current one.
self._prev_upper_band = None
self._prev_lower_band = None
self._prev_middle = None
def OnStarted2(self, time):
super(donchian_channel_strategy, self).OnStarted2(time)
donchian = DonchianChannels()
donchian.Length = self._channel_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(donchian, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, donchian)
self.DrawOwnTrades(area)
def _process_candle(self, candle, donchian_val):
if candle.State != CandleStates.Finished:
return
if donchian_val.UpperBand is None or donchian_val.LowerBand is None or donchian_val.Middle is None:
return
# A close can only break out of the channel the candles before it formed.
upper = self._prev_upper_band
lower = self._prev_lower_band
middle = self._prev_middle
self._prev_upper_band = donchian_val.UpperBand
self._prev_lower_band = donchian_val.LowerBand
self._prev_middle = donchian_val.Middle
if upper is None or lower is None or middle is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
position = self.Position
if close > upper and position <= 0:
self.BuyMarket(self.Volume + abs(position))
elif close < lower and position >= 0:
self.SellMarket(self.Volume + abs(position))
elif position > 0 and close <= middle:
self.SellMarket(position)
elif position < 0 and close >= middle:
self.BuyMarket(-position)
def CreateClone(self):
return donchian_channel_strategy()