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);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class donchian_scalper_strategy(Strategy):
def __init__(self):
super(donchian_scalper_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._channel_period = self.Param("ChannelPeriod", 50)
self._highs = []
self._lows = []
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def ChannelPeriod(self):
return self._channel_period.Value
@ChannelPeriod.setter
def ChannelPeriod(self, value):
self._channel_period.Value = value
def OnReseted(self):
super(donchian_scalper_strategy, self).OnReseted()
self._highs = []
self._lows = []
def OnStarted2(self, time):
super(donchian_scalper_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
ema = ExponentialMovingAverage()
ema.Length = self.ChannelPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._process_candle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent))
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
ema_val = float(ema_value)
period = self.ChannelPeriod
self._highs.append(high)
self._lows.append(low)
while len(self._highs) > period:
self._highs.pop(0)
self._lows.pop(0)
if len(self._highs) < period:
return
upper = max(self._highs)
lower = min(self._lows)
# Long: close breaks above upper Donchian and is above EMA
if self.Position == 0 and close >= upper and close > ema_val:
self.BuyMarket()
# Short: close breaks below lower Donchian and is below EMA
elif self.Position == 0 and close <= lower and close < ema_val:
self.SellMarket()
def CreateClone(self):
return donchian_scalper_strategy()