Auf GitHub ansehen

Exp DEMA Range Channel Tm Plus Strategy

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

  1. Subscribe to candles defined by CandleType and start the DEMA indicators.
  2. 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.
  3. 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.
  4. Close existing positions if the opposite signal appears or if the time filter expires.
  5. Enter new trades by sending market orders sized as Volume + |Position| to flip from the opposite side when required.
  6. 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();
		}
	}
}