Открыть на GitHub

Donchian Scalper

Обзор

Donchian Scalper — перенос советника MetaTrader 4 DonchianScalperEA на StockSharp. Стратегия отслеживает границы канала Дончиана и экспоненциальную среднюю (EMA) с тем же периодом. Отложенный стоп-ордер выставляется только после того, как цена сделает откат через EMA, что сигнализирует о перезагрузке импульса перед возможным пробоем. Заявки ставятся на текущих экстремумах канала, а защитный стоп размещается на противоположной границе. Управление прибылью выполняется фиксированным тейк-профитом или адаптивными трейлинг-стопами, привязанными к выбранной структуре рынка.

Логика стратегии

Подготовка входа

  • Подтверждение отката – стратегия ждёт, пока одна из двух последних закрытых свечей не пересечёт EMA сверху вниз (для покупок) или снизу вверх (для продаж). Уровень пересечения смещается на настраиваемое значение «якоря» (Cross Anchor), чтобы фильтровать мелкие шумовые колебания.
  • Армирование пробойного ордера – когда условия отката выполнены и истекло время охлаждения, выставляется стоп-заявка на ближайшей границе канала Дончиана: верхняя граница для покупок и нижняя для продаж. Противоположная граница используется для расчёта первоначального стоп-лосса. Если уровни канала не менялись как минимум две свечи подряд, активные отложенные заявки автоматически перестраиваются на новые цены.

Управление позицией

  • Первичная защита – после срабатывания стоп-ордера стратегия размещает защитный стоп с заранее вычисленной ценой. Уровень стопа совпадает с противоположной границей Дончиана и может быть смещён внутрь канала параметром «Stop Loss (points)».
  • Контроль прибыли – доступны два режима:
    • Close At Profit – закрывает позицию, когда движение от средней цены входа превышает заданное тейк-профит расстояние.
    • Trailing – оставляет позицию открытой и периодически подтягивает защитный стоп. Варианты трейлинга: граница Дончиана, EMA или коридор на основе ATR.
  • Охлаждение – после полного закрытия позиции стратегия ждёт указанное число завершённых свечей, прежде чем выставлять новые пробойные заявки. Это повторяет требование оригинального советника выдерживать минимум три бара между сделками.

Параметры

  • Volume – объём заявок для входов и рыночных выходов.
  • Channel Period – период канала Дончиана и EMA.
  • Cross Anchor – дополнительная глубина (в пунктах), на которую цена должна откатиться перед постановкой пробойного ордера.
  • Stop Loss (points) – смещение от противоположной границы канала при расчёте первоначального стоп-лосса; значение 0 оставляет стоп прямо на границе.
  • Take Profit (points) – расстояние тейк-профита в режиме фиксированного выхода. В режиме трейлинга параметр игнорируется.
  • Candle Type – таймфрейм, используемый для расчёта индикаторов.
  • Profit Mode – выбор между фиксированным тейк-профитом и трейлинг-стопом.
  • Trailing Mode – тип трейлинг-стопа в режиме Trailing (граница Дончиана, EMA или ATR).
  • Cooldown Bars – сколько завершённых свечей нужно подождать после выхода в ноль, прежде чем снова активировать пробойные ордера.
  • ATR Period / ATR Multiplier – параметры ATR для волатильностного трейлинга; множитель определяет расстояние в ATR до защитного стопа.

Дополнительные сведения

  • Все цены заявок и стопов выравниваются по минимальному шагу цены инструмента.
  • При одновременном существовании длинного и короткого стоп-ордера исполнение одного автоматически отменяет противоположный, чтобы исключить хеджирование.
  • Если режим прибыли установлен на Close At Profit, а параметр Take Profit (points) равен нулю, позиция будет удерживаться до срабатывания защитного стопа.
  • Перенос выполнен на высокоуровневом API StockSharp: используется подписка на свечи, привязка индикаторов и готовые методы BuyStop/SellStop/SellMarket. Python-реализация в поставку не входит.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Donchian Channel scalper with EMA filter.
/// Buys on upper channel breakout above EMA, sells on lower breakout below EMA.
/// Exits at middle band.
/// </summary>
public class DonchianScalperStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;

	public int ChannelPeriod
	{
		get => _channelPeriod.Value;
		set => _channelPeriod.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public DonchianScalperStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Channel Period", "Donchian channel lookback", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

		var donchian = new DonchianChannels { Length = ChannelPeriod };
		var ema = new ExponentialMovingAverage { Length = ChannelPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(donchian, ema, (candle, donchianVal, emaVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (donchianVal is not DonchianChannelsValue dcValue)
					return;

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

				if (emaVal.IsEmpty)
					return;

				var emaValue = emaVal.GetValue<decimal>();
				var close = candle.ClosePrice;

				// Long: close breaks above upper Donchian and is above EMA
				if (Position == 0 && close >= upper && close > emaValue)
					BuyMarket();
				// Short: close breaks below lower Donchian and is below EMA
				else if (Position == 0 && close <= lower && close < emaValue)
					SellMarket();
			})
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

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