The Exp DEMA Range Channel Tm Plus strategy ports the original MetaTrader expert advisor into StockSharp's high-level API. It builds a double exponential moving average (DEMA) channel around price extremes and interprets candle colors produced by the channel to decide when to trade. The implementation keeps the money management logic simple, relying on the platform Volume property and optional protective orders while reproducing the breakout and time-out rules of the source code.
Core Logic
Channel construction
Two DEMA indicators with the same period are calculated: one on candle highs and one on candle lows.
Their outputs are shifted forward by a configurable number of bars (Shift) to match how the original custom indicator draws the channel.
A price offset in points (PriceShiftPoints) can be added to widen or tighten the channel.
Signal colors
A candle closing above the shifted upper band is considered bullish.
A candle closing below the shifted lower band is considered bearish.
Candle body direction (close ≥ open or close ≤ open) is preserved to mimic the four possible colors (0–3) of the MQL indicator.
Entry conditions
The strategy looks back SignalBar bars to evaluate the latest breakout color and confirms that the previous bar did not already show the same signal. This captures the moment a new breakout appears.
Long entries are allowed only when EnableBuyEntry is true and the detected color corresponds to an upward breakout.
Short entries require EnableSellEntry and a downward breakout.
Exit conditions
Long positions can be closed on any downward breakout if EnableBuyExit is enabled.
Short positions can be closed on upward breakouts if EnableSellExit is enabled.
Positions can also be closed after a configurable holding time (HoldingMinutes) if UseHoldingLimit is true, mirroring the time filter from the expert advisor.
Risk control
Optional take-profit and stop-loss distances (in price points) activate StartProtection, which issues protective orders using market execution when the thresholds are hit.
Parameters
Parameter
Description
MaPeriod
DEMA period used for both upper and lower channel lines.
Shift
Number of bars the DEMA lines are shifted forward before comparisons.
PriceShiftPoints
Additional distance, measured in price points (multiples of PriceStep), added to the upper line and subtracted from the lower line.
SignalBar
Number of bars back used to evaluate the breakout color. 0 means current bar, 1 the last closed bar, etc.
EnableBuyEntry / EnableSellEntry
Toggle for long and short breakout entries.
EnableBuyExit / EnableSellExit
Toggle for exiting long or short positions on opposite signals.
UseHoldingLimit
Enables closing positions after HoldingMinutes minutes in the market.
HoldingMinutes
Maximum holding time before a forced close; set to 0 to disable while keeping the flag true.
StopLossPoints / TakeProfitPoints
Protective distances in price points. When greater than zero they are converted to absolute price offsets and passed to StartProtection.
CandleType
Candle type and timeframe used for all calculations (defaults to 8-hour candles as in the MQL script).
Trading Workflow
Subscribe to candles defined by CandleType and start the DEMA indicators.
Store the most recent channel values in queues so the algorithm can reference the value that existed Shift bars earlier, reproducing the original indicator shift.
When a candle finishes, compute its breakout color and push it into a rolling buffer. Use the buffer to identify fresh up or down breakouts according to SignalBar.
Close existing positions if the opposite signal appears or if the time filter expires.
Enter new trades by sending market orders sized as Volume + |Position| to flip from the opposite side when required.
Update the internal timestamp of the active position to keep the holding-time filter accurate.
Notes
The strategy assumes chart data is processed in chronological order. When running in backtests or live trading, ensure the candle stream is ordered to maintain the correct shift behavior.
Volume must be set on the strategy before start-up (via UI or code) to control position sizing. Money management modes from the MQL expert are intentionally not replicated.
Because protective orders are optional, remember to configure both stop-loss and take-profit values when deploying to production environments.
The chart helper draws candles and executed trades automatically, allowing visual verification that channel breakouts trigger the expected entries and exits.
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()