Estrategia de Canal de Negociación
La Estrategia de Canal de Negociación opera rompimientos y retrocesos alrededor de un canal de precios Donchian. Cuando la banda superior permanece sin cambios y el precio la toca o cierra por debajo de ella pero por encima del pivote, se abre una posición larga. La lógica opuesta se utiliza para entradas cortas. El stop loss se coloca más allá de la banda opuesta por el valor del ATR. Un trailing stop opcional puede ajustar el stop a medida que la operación avanza en beneficio.
Parámetros
ChannelPeriod— longitud del canal Donchian.AtrPeriod— período ATR para el cálculo del stop loss.Trailing— distancia del trailing stop en unidades de precio (0 desactiva el trailing).CandleType— tipo de vela utilizado para los cálculos.
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>
/// Trade Channel breakout strategy.
/// Uses Highest/Lowest channel and ATR for stop management.
/// </summary>
public class TradeChannelStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevUpper;
private decimal _prevLower;
private decimal _stopPrice;
private bool _hasPrev;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TradeChannelStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Channel Period", "Donchian channel period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR length for stop calculation", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevUpper = 0;
_prevLower = 0;
_stopPrice = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType)
.Bind(highest, lowest, atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal upper, decimal lower, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev)
{
_prevUpper = upper;
_prevLower = lower;
_hasPrev = true;
return;
}
if (atrVal <= 0)
{
_prevUpper = upper;
_prevLower = lower;
return;
}
var close = candle.ClosePrice;
// Breakout above channel => long
if (close >= _prevUpper && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_stopPrice = lower - atrVal;
}
// Breakout below channel => short
else if (close <= _prevLower && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_stopPrice = upper + atrVal;
}
// Manage long
else if (Position > 0)
{
// Trailing stop
var newStop = close - atrVal * 2;
if (newStop > _stopPrice) _stopPrice = newStop;
if (candle.LowPrice <= _stopPrice)
{
SellMarket();
_stopPrice = 0;
}
}
// Manage short
else if (Position < 0)
{
var newStop = close + atrVal * 2;
if (newStop < _stopPrice) _stopPrice = newStop;
if (candle.HighPrice >= _stopPrice)
{
BuyMarket();
_stopPrice = 0;
}
}
_prevUpper = upper;
_prevLower = lower;
}
}
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 Highest, Lowest, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class trade_channel_strategy(Strategy):
def __init__(self):
super(trade_channel_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 20) \
.SetDisplay("Channel Period", "Donchian channel period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR length for stop calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_upper = 0.0
self._prev_lower = 0.0
self._stop_price = 0.0
self._has_prev = False
@property
def channel_period(self):
return self._channel_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(trade_channel_strategy, self).OnReseted()
self._prev_upper = 0.0
self._prev_lower = 0.0
self._stop_price = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(trade_channel_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, atr, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, upper, lower, atr_val):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_upper = upper
self._prev_lower = lower
self._has_prev = True
return
if atr_val <= 0:
self._prev_upper = upper
self._prev_lower = lower
return
close = candle.ClosePrice
# Breakout above channel => long
if close >= self._prev_upper and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._stop_price = lower - atr_val
# Breakout below channel => short
elif close <= self._prev_lower and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._stop_price = upper + atr_val
# Manage long
elif self.Position > 0:
# Trailing stop
new_stop = close - atr_val * 2
if new_stop > self._stop_price:
self._stop_price = new_stop
if candle.LowPrice <= self._stop_price:
self.SellMarket()
self._stop_price = 0
# Manage short
elif self.Position < 0:
new_stop = close + atr_val * 2
if new_stop < self._stop_price:
self._stop_price = new_stop
if candle.HighPrice >= self._stop_price:
self.BuyMarket()
self._stop_price = 0
self._prev_upper = upper
self._prev_lower = lower
def CreateClone(self):
return trade_channel_strategy()