Открыть на GitHub

Donchain Counter-Channel System

Обзор

Donchain Counter-Channel System — портирование советника MetaTrader 4, опубликованного Michal Rutka в журнале Currency Trader в 2005 году. Стратегия рассчитывает Donchian-канал длиной 20 свечей на выбранном таймфрейме (по умолчанию дневном). Если нижняя граница канала на предыдущей свече поднялась выше значения двух свечей назад, это трактуется как попытка покупателей перехватить инициативу, и на следующей свече открывается длинная позиция по рынку. Если верхняя граница опустилась, система открывает короткую позицию. Защитный стоп всегда привязывается к противоположной границе канала, что полностью воспроизводит механику исходного эксперта.

В течение 24 часов допускается только одно открытие позиции: после входа включается таймер ожидания, и новые сигналы игнорируются до его истечения. Благодаря высокоуровневому API StockSharp значения Donchian-канала поступают вместе с завершёнными свечами через механизм BindEx.

Логика торговли

  1. Подписаться на свечи типа CandleType (по умолчанию дневные) и вычислять индикатор DonchianChannels с периодом ChannelPeriod.
  2. После закрытия каждой свечи:
    • При открытой длинной позиции подтягивать уровень стопа к текущей нижней границе канала и закрывать позицию, если минимум свечи пробивает этот уровень.
    • При открытой короткой позиции опускать стоп до текущей верхней границы и закрывать позицию при пробое максимумом свечи.
    • Если позиции нет, игнорировать сигналы, пока с момента последнего входа не прошло TradeCooldown.
    • Открывать лонг, когда нижняя граница Donchian на предыдущей свече выше, чем на свече до неё, — признак разворота канала вверх. Начальный стоп ставится на текущую нижнюю границу.
    • Открывать шорт, когда верхняя граница Donchian на предыдущей свече ниже, чем на свече до неё, — сигнал ослабления восходящего движения. Стоп размещается на текущей верхней границе.
  3. Сопровождать позицию до тех пор, пока цена не пересечёт соответствующий стоп-уровень.

Параметры

Имя Значение по умолчанию Описание
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;
	}
}