Стратегия Exp DEMA Range Channel Tm Plus переносит одноимённого советника MetaTrader в инфраструктуру StockSharp. Она строит канал по двойным экспоненциальным скользящим средним (DEMA), рассчитанным по экстремумам свечей, и отслеживает «цвет» свечи, который выдаёт исходный индикатор DEMA Range Channel. Появление нового цвета интерпретируется как пробой канала и используется для открытия или закрытия позиций. Управление капиталом упрощено: применяется объём из свойства Volume, а стоп-ордера задаются только через параметры защиты.
Логика работы
Построение канала
DEMA по максимумам и минимумам свечей рассчитываются с одинаковым периодом MaPeriod.
Значения каналов сдвигаются вперёд на Shift баров, как и в оригинальном индикаторе, поэтому проверяется уровень, который существовал несколько баров назад.
Параметр PriceShiftPoints добавляет к верхней линии и вычитает от нижней линии заданный сдвиг в пунктах (PriceStep).
Цветовые состояния
Закрытие выше верхней линии трактуется как бычий сигнал. Если тело свечи бычье, цвет соответствует индексу 3, если медвежье — индексу 2.
Закрытие ниже нижней линии формирует медвежий сигнал (индекс 0 или 1 в зависимости от тела свечи).
Открытие позиций
Буфер цветов хранит последние значения, и стратегия анализирует цвет, появившийся SignalBar баров назад. Если предыдущий цвет был другим, событие считается новым пробоем.
При включённом EnableBuyEntry открывается длинная позиция, когда фиксируется свежий бычий пробой.
При включённом EnableSellEntry открывается короткая позиция при новом медвежьем пробое.
Закрытие позиций
EnableBuyExit позволяет закрывать длинные позиции при любом медвежьем сигнале.
EnableSellExit закрывает короткие позиции при бычьем сигнале.
Параметры UseHoldingLimit и HoldingMinutes реализуют тайм-аут из исходного советника: позиция закрывается, если удерживается дольше заданного количества минут.
Защитные приказы
При ненулевых StopLossPoints и/или TakeProfitPoints вызывается StartProtection. Расстояния переводятся в абсолютное изменение цены и используются для выставления защитных приказов с рыночным исполнением.
Параметры
Параметр
Описание
MaPeriod
Период DEMA для верхней и нижней границ канала.
Shift
Число баров, на которое значения канала сдвигаются вперёд перед сравнением.
PriceShiftPoints
Дополнительный отступ в пунктах (в долях PriceStep), расширяющий или сужающий канал.
SignalBar
На сколько баров назад смотреть для определения свежего пробоя (0 — текущая свеча, 1 — последняя закрытая и т.д.).
EnableBuyEntry / EnableSellEntry
Включают/выключают открытие длинных и коротких позиций.
EnableBuyExit / EnableSellExit
Управляют закрытием длинных/коротких позиций по противоположным сигналам.
UseHoldingLimit
Включает ограничение времени удержания позиции.
HoldingMinutes
Максимальное время удержания в минутах. Значение 0 деактивирует тайм-аут при включённом флаге.
StopLossPoints / TakeProfitPoints
Дистанции защитных приказов в пунктах. При значении > 0 переводятся в абсолютное изменение цены и используются в StartProtection.
CandleType
Тип и таймфрейм свечей (по умолчанию 8-часовые, как в оригинальном файле).
Последовательность действий
Подписаться на свечи CandleType и инициализировать индикаторы DEMA.
Сохранять последние значения канала в очередях, чтобы иметь доступ к уровню, актуальному Shift баров назад.
После закрытия свечи вычислить новый цвет и поместить его в буфер; проверить, появился ли новый пробой согласно SignalBar.
Закрыть активные позиции при противоположном сигнале либо при превышении лимита удержания.
Открывать новые сделки рыночными ордерами объёмом Volume + |Position|, что позволяет разворачиваться без накопления позиций.
Обновлять метку времени открытия позиции, чтобы корректно считать тайм-аут.
Дополнительные замечания
Поток свечей должен поступать в хронологическом порядке; нарушение порядка исказит работу сдвига канала.
Перед запуском задайте желаемый объём через свойство Volume. Алгоритмы расчёта лота из MQL-версии в данном порте не используются.
При работе на реальном счёте рекомендуются явные значения стоп-лосса и тейк-профита для защиты от неожиданных движений.
Встроенные функции построения графика отображают свечи и сделки, что позволяет визуально проверить соответствие сигналов пробоя.
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>
/// DEMA channel breakout strategy.
/// Uses fast and slow DEMA crossover to detect trend changes.
/// </summary>
public class ExpDemaRangeChannelTmPlusStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public ExpDemaRangeChannelTmPlusStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast DEMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow DEMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fastDema = new DoubleExponentialMovingAverage { Length = FastPeriod };
var slowDema = new DoubleExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastDema, slowDema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastDema);
DrawIndicator(area, slowDema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast;
_prevSlow = slow;
if (!prevAbove && currAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (prevAbove && !currAbove)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 DoubleExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_dema_range_channel_tm_plus_strategy(Strategy):
def __init__(self):
super(exp_dema_range_channel_tm_plus_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast Period", "Fast DEMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow Period", "Slow DEMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(exp_dema_range_channel_tm_plus_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(exp_dema_range_channel_tm_plus_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_dema = DoubleExponentialMovingAverage()
fast_dema.Length = self.FastPeriod
slow_dema = DoubleExponentialMovingAverage()
slow_dema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_dema, slow_dema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_dema)
self.DrawIndicator(area, slow_dema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif prev_above and not curr_above:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return exp_dema_range_channel_tm_plus_strategy()