Открыть на GitHub

Стратегия EA OBJPROP Chart ID

Обзор

Стратегия EA OBJPROP Chart ID воссоздает ориентированное на визуализацию поведение оригинального примера MetaTrader 5. Она строит полосы канала Дончиана на трех синхронизированных таймфреймах: основной график отображает торговый интервал, а две вспомогательные панели демонстрируют контекст H4 и Daily. Такой подход повторяет идею исходного эксперта, который объединял несколько графиков и индикаторов в одном рабочем пространстве.

Ключевые особенности

  • Мульти-таймфреймовая визуализация — автоматически подписывается на свечи основного, H4 и дневного таймфреймов для выбранного инструмента.
  • Единая длина канала Дончиана — один и тот же период применяется ко всем таймфреймам, что упрощает сравнение границ канала.
  • Интеграция на высоком уровне — использует диаграммы StockSharp для отрисовки цен, каналов и сделок без ручного создания низкоуровневых объектов.
  • База для расширения — сохраняет последние значения верхней и нижней границ для каждого таймфрейма, облегчая добавление торговых правил в будущем.

Параметры

Параметр Описание Категория Значение по умолчанию
ChannelLength Длина канала Дончиана, общая для всех таймфреймов. Indicators 22
PrimaryCandleType Основной таймфрейм, используемый для торговли и верхней панели. General Свечи 30 минут
H4CandleType Вспомогательный таймфрейм H4, показанный во второй панели. General Свечи 4 часа
DailyCandleType Вспомогательный дневной таймфрейм, показанный в третьей панели. General Дневные свечи

Все параметры доступны через интерфейс StockSharp, поддерживают оптимизацию и настройку без изменения исходного кода.

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

  1. Инициализирует три индикатора DonchianChannels с одинаковой длиной.
  2. Подписывается на свечные серии основного, H4 и дневного таймфреймов для текущего инструмента.
  3. Привязывает каждую подписку к своему индикатору через высокоуровневый API, обеспечивая инкрементальный расчет значений.
  4. Создает одну основную и до двух дополнительных областей графика, где отображаются свечи, каналы и сделки стратегии.
  5. Сохраняет последние верхние и нижние границы каналов для каждого таймфрейма, что упрощает внедрение торговых фильтров.

Реализация ориентирована на визуализацию и не размещает заявки, повторяя замысел исходного 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;
	}
}