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
Initializes three Donchian Channel indicators with the same length parameter.
Subscribes to the selected primary, H4, and Daily candle series for the current security.
Binds each subscription to its respective channel indicator using the high-level API, ensuring indicator values are computed incrementally.
Creates one main chart area and up to two auxiliary areas where candles, channels, and the strategy's trades are drawn.
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;
}
}
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()