Стратегия EA OBJPROP Chart ID воссоздает ориентированное на визуализацию поведение оригинального примера MetaTrader 5. Она строит полосы канала Дончиана на трех синхронизированных таймфреймах: основной график отображает торговый интервал, а две вспомогательные панели демонстрируют контекст H4 и Daily. Такой подход повторяет идею исходного эксперта, который объединял несколько графиков и индикаторов в одном рабочем пространстве.
Ключевые особенности
Мульти-таймфреймовая визуализация — автоматически подписывается на свечи основного, H4 и дневного таймфреймов для выбранного инструмента.
Единая длина канала Дончиана — один и тот же период применяется ко всем таймфреймам, что упрощает сравнение границ канала.
Интеграция на высоком уровне — использует диаграммы StockSharp для отрисовки цен, каналов и сделок без ручного создания низкоуровневых объектов.
База для расширения — сохраняет последние значения верхней и нижней границ для каждого таймфрейма, облегчая добавление торговых правил в будущем.
Параметры
Параметр
Описание
Категория
Значение по умолчанию
ChannelLength
Длина канала Дончиана, общая для всех таймфреймов.
Indicators
22
PrimaryCandleType
Основной таймфрейм, используемый для торговли и верхней панели.
General
Свечи 30 минут
H4CandleType
Вспомогательный таймфрейм H4, показанный во второй панели.
General
Свечи 4 часа
DailyCandleType
Вспомогательный дневной таймфрейм, показанный в третьей панели.
General
Дневные свечи
Все параметры доступны через интерфейс StockSharp, поддерживают оптимизацию и настройку без изменения исходного кода.
Логика стратегии
Инициализирует три индикатора DonchianChannels с одинаковой длиной.
Подписывается на свечные серии основного, H4 и дневного таймфреймов для текущего инструмента.
Привязывает каждую подписку к своему индикатору через высокоуровневый API, обеспечивая инкрементальный расчет значений.
Создает одну основную и до двух дополнительных областей графика, где отображаются свечи, каналы и сделки стратегии.
Сохраняет последние верхние и нижние границы каналов для каждого таймфрейма, что упрощает внедрение торговых фильтров.
Реализация ориентирована на визуализацию и не размещает заявки, повторяя замысел исходного MQL-примера, сосредоточенного на построении панели мониторинга.
Практические рекомендации
Убедитесь, что для выбранного инструмента доступна историческая информация на всех используемых таймфреймах, иначе панели могут оставаться пустыми.
При необходимости можно заменить параметры таймфреймов на другие типы TimeFrame, например 15 минут или неделю.
Для добавления торговой логики используйте методы ProcessPrimary, ProcessH4, ProcessDaily, где уже доступны актуальные границы каналов.
Особенности конверсии
В MetaTrader дочерние графики создавались объектами OBJ_CHART; в StockSharp используется создание областей графика, полностью соответствующее высокоуровневой архитектуре платформы.
Управление индикаторами выполняется через вызовы BindEx, что устраняет необходимость ручного получения хэндлов и синхронизирует значения с новыми свечами.
Не требуется явное удаление объектов — подписки и отображение очищаются автоматически при остановке стратегии.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// EA Objprop Chart Id strategy: Standard Deviation breakout.
/// Buys when StdDev crosses above threshold with bullish candle, sells on bearish cross.
/// </summary>
public class EaObjpropChartIdStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevStdDev;
private decimal _prevRange;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public EaObjpropChartIdStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 50)
.SetGreaterThanZero()
.SetDisplay("Period", "EMA period", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevStdDev = 0;
_prevRange = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevStdDev = 0;
_prevRange = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var stdDev = new ExponentialMovingAverage { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(stdDev, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal stdDevValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0)
return;
if (_hasPrev && stdDevValue > 0 && _prevRange > 0)
{
var expanding = range > _prevRange * 1.2m;
var bullish = candle.ClosePrice > candle.OpenPrice;
var bearish = candle.ClosePrice < candle.OpenPrice;
if (expanding && bullish && candle.ClosePrice > stdDevValue && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (expanding && bearish && candle.ClosePrice < stdDevValue && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevStdDev = stdDevValue;
_prevRange = range;
_hasPrev = true;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ea_objprop_chart_id_strategy(Strategy):
def __init__(self):
super(ea_objprop_chart_id_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 50)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_std_dev = 0.0
self._prev_range = 0.0
self._candles_since_trade = 4
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(ea_objprop_chart_id_strategy, self).OnReseted()
self._prev_std_dev = 0.0
self._prev_range = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
def OnStarted2(self, time):
super(ea_objprop_chart_id_strategy, self).OnStarted2(time)
self._prev_std_dev = 0.0
self._prev_range = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, self._process_candle).Start()
def _process_candle(self, candle, std_dev_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
std_val = float(std_dev_value)
range_val = high - low
if range_val <= 0:
return
if self._has_prev and std_val > 0 and self._prev_range > 0:
expanding = range_val > self._prev_range * 1.2
bullish = close > open_price
bearish = close < open_price
if expanding and bullish and close > std_val and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif expanding and bearish and close < std_val and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_std_dev = std_val
self._prev_range = range_val
self._has_prev = True
def CreateClone(self):
return ea_objprop_chart_id_strategy()