Donchain Counter-Channel System — портирование советника MetaTrader 4, опубликованного Michal Rutka в журнале Currency Trader в 2005 году. Стратегия рассчитывает Donchian-канал длиной 20 свечей на выбранном таймфрейме (по умолчанию дневном). Если нижняя граница канала на предыдущей свече поднялась выше значения двух свечей назад, это трактуется как попытка покупателей перехватить инициативу, и на следующей свече открывается длинная позиция по рынку. Если верхняя граница опустилась, система открывает короткую позицию. Защитный стоп всегда привязывается к противоположной границе канала, что полностью воспроизводит механику исходного эксперта.
В течение 24 часов допускается только одно открытие позиции: после входа включается таймер ожидания, и новые сигналы игнорируются до его истечения. Благодаря высокоуровневому API StockSharp значения Donchian-канала поступают вместе с завершёнными свечами через механизм BindEx.
Логика торговли
Подписаться на свечи типа CandleType (по умолчанию дневные) и вычислять индикатор DonchianChannels с периодом ChannelPeriod.
После закрытия каждой свечи:
При открытой длинной позиции подтягивать уровень стопа к текущей нижней границе канала и закрывать позицию, если минимум свечи пробивает этот уровень.
При открытой короткой позиции опускать стоп до текущей верхней границы и закрывать позицию при пробое максимумом свечи.
Если позиции нет, игнорировать сигналы, пока с момента последнего входа не прошло TradeCooldown.
Открывать лонг, когда нижняя граница Donchian на предыдущей свече выше, чем на свече до неё, — признак разворота канала вверх. Начальный стоп ставится на текущую нижнюю границу.
Открывать шорт, когда верхняя граница Donchian на предыдущей свече ниже, чем на свече до неё, — сигнал ослабления восходящего движения. Стоп размещается на текущей верхней границе.
Сопровождать позицию до тех пор, пока цена не пересечёт соответствующий стоп-уровень.
Параметры
Имя
Значение по умолчанию
Описание
Volume
1
Объём заявок для длинных и коротких позиций.
ChannelPeriod
20
Количество свечей для расчёта Donchian-канала.
TradeCooldown
1 день
Минимальный интервал между повторными входами.
CandleType
Дневные
Тип свечей, на которых строится канал.
Индикаторы и данные
Donchian Channels — источник верхней и нижней границ, служащих для определения разворотов и постановки стопов.
Дневные свечи (по умолчанию) — обеспечивают временную метку для контроля 24-часовой задержки и кормят индикатор новыми данными.
Особенности реализации
Обработчик свечей подписан через BindEx, что позволяет одномоментно получать типизированное значение DonchianChannelsValue и работать сразу с обеими границами.
Стоп-уровни моделируются программно: стратегия следит за максимумами и минимумами свечей, как и исходный советник, который переносил стопы по мере появления новых значений канала.
Таймер TradeCooldown обновляется только при открытии позиции, поэтому внутри суток повторный вход невозможен, даже если позиция была закрыта раньше.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Donchian counter-channel system.
/// Counter-trend: buys when lower channel turns up (reversal from support).
/// Sells when upper channel turns down (reversal from resistance).
/// </summary>
public class DonchainCounterChannelSystemStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevPrevHigh;
private decimal _prevPrevLow;
private int _barCount;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DonchainCounterChannelSystemStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 20)
.SetDisplay("Channel Period", "Donchian channel lookback", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevHigh = 0m; _prevLow = 0m; _prevPrevHigh = 0m; _prevPrevLow = 0m; _barCount = 0; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_barCount = 0;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
if (_barCount < 3)
{
_prevPrevHigh = _prevHigh;
_prevPrevLow = _prevLow;
_prevHigh = highest;
_prevLow = lowest;
return;
}
// Lower band turning up = support holding = buy signal
var lowerTurningUp = lowest > _prevLow && _prevLow <= _prevPrevLow;
// Upper band turning down = resistance holding = sell signal
var upperTurningDown = highest < _prevHigh && _prevHigh >= _prevPrevHigh;
if (lowerTurningUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (upperTurningDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevPrevHigh = _prevHigh;
_prevPrevLow = _prevLow;
_prevHigh = highest;
_prevLow = lowest;
}
}