Auf GitHub ansehen

EA OBJPROP Chart ID Strategy

Overview

The EA OBJPROP Chart ID Strategy recreates the chart-focused behavior of the original MetaTrader 5 example by displaying Donchian Channel envelopes on three synchronized timeframes. The primary chart hosts the trading timeframe while two auxiliary panels visualize the H4 and Daily context. This setup mirrors the original Expert Advisor that stacked multiple charts and indicators in a single workspace for visual analysis.

Key Features

  • Multi-timeframe visualization – automatically subscribes to primary, H4, and Daily candles for the selected security.
  • Unified Donchian Channel length – applies the same channel period to every timeframe to keep the envelopes comparable.
  • High-level chart integration – relies on StockSharp chart areas to render price series, Donchian Channels, and executed trades, reproducing the MQL layout without low-level object manipulation.
  • Extensible foundation – stores the latest channel boundaries for each timeframe, making it straightforward to extend the strategy with breakout or confirmation logic in the future.

Parameters

Parameter Description Category Default
ChannelLength Length of the Donchian Channel used across all subscribed timeframes. Indicators 22
PrimaryCandleType Main timeframe used for trading and as the top chart panel. General 30-minute candles
H4CandleType Auxiliary H4 timeframe displayed in a secondary panel. General 4-hour candles
DailyCandleType Auxiliary Daily timeframe displayed in a tertiary panel. General 1-day candles

All parameters are available through StockSharp parameter UI, support optimization, and may be fine-tuned without changing the code.

Strategy Logic

  1. Initializes three Donchian Channel indicators with the same length parameter.
  2. Subscribes to the selected primary, H4, and Daily candle series for the current security.
  3. Binds each subscription to its respective channel indicator using the high-level API, ensuring indicator values are computed incrementally.
  4. Creates one main chart area and up to two auxiliary areas where candles, channels, and the strategy's trades are drawn.
  5. Stores the most recent upper and lower channel boundaries for each timeframe, enabling custom decision rules to be added later on.

The current implementation is visualization-only and does not submit orders. This mirrors the original MetaTrader code, which focused on composing a dashboard of charts without automated trading logic.

Usage Notes

  • Ensure the selected security has historical data for every timeframe used by the strategy to populate all chart areas.
  • You can change any of the timeframe parameters to other TimeFrame data types (e.g., 15 minutes or weekly candles) if different context panels are required.
  • Additional trade logic can be layered in the processing methods (ProcessPrimary, ProcessH4, ProcessDaily) by reacting to the stored channel levels.

Conversion Notes

  • The MetaTrader example created child charts via OBJ_CHART objects; the StockSharp version replaces that with chart areas created by the high-level API, which is better integrated with the platform.
  • Indicator management is performed via BindEx calls instead of manual handle creation, ensuring values are synchronized with incoming candles.
  • Object deletion routines are not required because StockSharp automatically disposes subscriptions and chart bindings when the strategy stops.
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;
	}
}