GitHub で見る

CoensioTrader1 V06 Strategy

Overview

CoensioTrader1 V06 is a trend-following breakout strategy originally distributed as a MetaTrader Expert Advisor. The StockSharp port keeps the discretionary pattern recognition logic while removing the broker- and internet-specific features from the MQL implementation. The strategy operates on a single security and timeframe, using Bollinger Bands and a double exponential moving average (DEMA) to identify exhaustion moves followed by trend resumption.

The original robot allowed trading up to six currency pairs with individual parameter sets, supported DLL-based licensing, and reported optimization results to a remote server. Those auxiliary services are intentionally omitted in this port. The focus is the core entry and exit workflow that reacts to Bollinger Band rejections confirmed by swing structure and the DEMA slope.

Strategy Logic

  1. Data subscription – the strategy subscribes to the configured candle type (default: 1 hour) and binds Bollinger Bands together with a DEMA.
  2. Bollinger Band rejection – signals are evaluated on the last fully closed candle.
    • Long setup
      • Candle opened below the previous lower Bollinger Band and closed back above it (failed breakdown).
      • Candle created a higher low compared to the bar before it, while that earlier bar made a lower low compared to its predecessor (double-bottom style structure).
      • The DEMA is strictly rising across the last three observations (current value > previous > second previous).
    • Short setup
      • Candle opened above the previous upper Bollinger Band and closed back below it (failed breakout).
      • Candle made a lower high compared to the bar before it, while that earlier bar made a higher high compared to its predecessor (double-top structure).
      • The DEMA is strictly falling across the last three observations.
  3. Order execution – market orders are sent immediately after the signal is confirmed on a finished candle. Optional position flattening on opposite signals can be enabled.
  4. Risk management – optional stop-loss and take-profit distances are provided through StartProtection. Both are absolute price offsets; trailing stop functionality from the original expert is not reproduced.

Parameters

Name Description Default
BollingerPeriod Period for Bollinger Band calculation. 30
BollingerDeviation Standard deviation multiplier for the bands. 1.5
DemaPeriod Length of the double exponential moving average used for trend confirmation. 20
StopLossDistance Absolute stop-loss offset passed to StartProtection. Set to zero to disable. 0 (absolute)
TakeProfitDistance Absolute take-profit offset passed to StartProtection. Set to zero to disable. 0 (absolute)
CloseOnSignal Close the current position before opening a new one in the opposite direction. false
CandleType Candle data type or timeframe. Defaults to 1-hour time frame. 1h

Usage Notes

  • The StockSharp version trades only the primary Strategy.Security. To mimic the multi-symbol behavior of the original expert, launch separate strategy instances with distinct parameter sets.
  • The MQL lot-sizing logic (RiskMax, LotSize, LotBalanceDivider) was not translated. Configure Volume on the strategy or via risk manager according to your portfolio rules.
  • The DLL-based activation, remote optimization logging, and UI drawing routines present in the MQL files were intentionally removed.
  • Stop-loss and take-profit values are absolute price distances. Adapt them to the instrument’s tick size or pip value when configuring the strategy.
  • The original trailing-stop step mechanic is not implemented. If trailing management is required, layer a dedicated risk module on top of this strategy.
  • All code comments and logics are kept in English as requested, while README translations are provided separately.

Differences from the MQL Version

  • Multi-symbol management: replaced with a single-instrument design for clarity and easier testing.
  • Networking and licensing: removed; no external HTTP requests or DLL calls are performed.
  • Order sizing: simplified to rely on StockSharp’s standard Volume handling.
  • Visual objects: chart annotations from MetaTrader (arrows, labels, color themes) are not recreated. Use StockSharp chart helpers if visualization is required.
  • Trailing stop: not ported; only the initial protective orders are configured.

This documentation aims to be exhaustive so that the port can be reviewed, tested, and extended without needing to read the original MQL source.

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the CoensioTrader1 V06 MQL strategy.
/// The strategy buys after a lower Bollinger Band rejection paired with a higher low pattern and bullish DEMA trend.
/// It sells after an upper Bollinger Band rejection with a lower high structure and bearish DEMA trend.
/// </summary>
public class CoensioTrader1V06Strategy : Strategy
{
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerDeviation;
	private readonly StrategyParam<int> _demaPeriod;
	private readonly StrategyParam<Unit> _stopLossDistance;
	private readonly StrategyParam<Unit> _takeProfitDistance;
	private readonly StrategyParam<bool> _closeOnSignal;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevOpen;
	private decimal? _prevHigh;
	private decimal? _prevLow;
	private decimal? _prevClose;

	private decimal? _prev2High;
	private decimal? _prev2Low;
	private decimal? _prev3High;
	private decimal? _prev3Low;

	private decimal? _prevUpperBand;
	private decimal? _prevLowerBand;

	private decimal? _prevDema;
	private decimal? _prev2Dema;

	/// <summary>
	/// Initializes a new instance of the <see cref="CoensioTrader1V06Strategy"/> class.
	/// </summary>
	public CoensioTrader1V06Strategy()
	{
		_bollingerPeriod = Param(nameof(BollingerPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "Length of Bollinger Bands", "Indicators")
			;

		_bollingerDeviation = Param(nameof(BollingerDeviation), 1.5m)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Deviation", "Standard deviation multiplier", "Indicators")
			;

		_demaPeriod = Param(nameof(DemaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("DEMA Period", "Length of double exponential moving average", "Indicators")
			;

		_stopLossDistance = Param(nameof(StopLossDistance), new Unit(0m, UnitTypes.Absolute))
			.SetDisplay("Stop Loss", "Absolute stop loss offset from entry", "Risk");

		_takeProfitDistance = Param(nameof(TakeProfitDistance), new Unit(0m, UnitTypes.Absolute))
			.SetDisplay("Take Profit", "Absolute take profit offset from entry", "Risk");

		_closeOnSignal = Param(nameof(CloseOnSignal), false)
			.SetDisplay("Close On Opposite Signal", "Close current trades when opposite setup appears", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for signal calculations", "General");

		Volume = 0.01m;
	}

	/// <summary>
	/// Bollinger Bands period.
	/// </summary>
	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	/// <summary>
	/// Bollinger Bands standard deviation multiplier.
	/// </summary>
	public decimal BollingerDeviation
	{
		get => _bollingerDeviation.Value;
		set => _bollingerDeviation.Value = value;
	}

	/// <summary>
	/// Double exponential moving average period.
	/// </summary>
	public int DemaPeriod
	{
		get => _demaPeriod.Value;
		set => _demaPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance from entry price.
	/// </summary>
	public Unit StopLossDistance
	{
		get => _stopLossDistance.Value;
		set => _stopLossDistance.Value = value;
	}

	/// <summary>
	/// Take-profit distance from entry price.
	/// </summary>
	public Unit TakeProfitDistance
	{
		get => _takeProfitDistance.Value;
		set => _takeProfitDistance.Value = value;
	}

	/// <summary>
	/// Close current position when an opposite signal appears.
	/// </summary>
	public bool CloseOnSignal
	{
		get => _closeOnSignal.Value;
		set => _closeOnSignal.Value = value;
	}

	/// <summary>
	/// Candle type used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevOpen = null;
		_prevHigh = null;
		_prevLow = null;
		_prevClose = null;

		_prev2High = null;
		_prev2Low = null;
		_prev3High = null;
		_prev3Low = null;

		_prevUpperBand = null;
		_prevLowerBand = null;

		_prevDema = null;
		_prev2Dema = null;
	}

	/// <inheritdoc />
	private BollingerBands _bollinger;
	private DoubleExponentialMovingAverage _dema;

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_bollinger = new BollingerBands
		{
			Length = BollingerPeriod,
			Width = BollingerDeviation
		};

		_dema = new DoubleExponentialMovingAverage
		{
			Length = DemaPeriod
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var bollingerResult = _bollinger.Process(candle);
		var demaResult = _dema.Process(candle);

		if (bollingerResult.IsEmpty || demaResult.IsEmpty || !_bollinger.IsFormed || !_dema.IsFormed)
		{
			_prevOpen = candle.OpenPrice;
			_prevClose = candle.ClosePrice;
			_prevLow = candle.LowPrice;
			_prevHigh = candle.HighPrice;
			return;
		}

		var bollingerValue = (BollingerBandsValue)bollingerResult;
		var upper = bollingerValue.UpBand ?? 0m;
		var lower = bollingerValue.LowBand ?? 0m;
		var demaValue = demaResult.GetValue<decimal>();

		if (_prevOpen.HasValue && _prevClose.HasValue && _prevLow.HasValue && _prevHigh.HasValue &&
			_prevLowerBand.HasValue && _prevUpperBand.HasValue && _prevDema.HasValue)
		{
			// Long setup: lower band rejection with rising DEMA.
			var crossedLowerBand = _prevLow.Value <= _prevLowerBand.Value && _prevClose.Value > _prevLowerBand.Value;
			var bullishTrend = demaValue > _prevDema.Value;

			if (crossedLowerBand && bullishTrend)
			{
				if (CloseOnSignal && Position < 0)
					{
					if (Position > 0) SellMarket(Position);
					else if (Position < 0) BuyMarket(-Position);
				}

				if (Position <= 0)
					BuyMarket();
			}

			// Short setup: upper band rejection with falling DEMA.
			var crossedUpperBand = _prevHigh.Value >= _prevUpperBand.Value && _prevClose.Value < _prevUpperBand.Value;
			var bearishTrend = demaValue < _prevDema.Value;

			if (crossedUpperBand && bearishTrend)
			{
				if (CloseOnSignal && Position > 0)
					{
					if (Position > 0) SellMarket(Position);
					else if (Position < 0) BuyMarket(-Position);
				}

				if (Position >= 0)
					SellMarket();
			}
		}

		_prev3Low = _prev2Low;
		_prev3High = _prev2High;
		_prev2Low = _prevLow;
		_prev2High = _prevHigh;

		_prevLowerBand = lower;
		_prevUpperBand = upper;

		_prev2Dema = _prevDema;
		_prevDema = demaValue;

		_prevOpen = candle.OpenPrice;
		_prevClose = candle.ClosePrice;
		_prevLow = candle.LowPrice;
		_prevHigh = candle.HighPrice;
	}
}