Открыть на GitHub

Стратегия CoensioTrader1 V06

Общее описание

CoensioTrader1 V06 — это эксперт MetaTrader, который ищет ложные пробои границ полос Боллинджера и подтверждает сигналы по структуре экстремумов и наклону DEMA. Перенос в StockSharp сохраняет ключевые торговые правила и отказывается от сетевых проверок лицензии, загрузки логов и другой инфраструктуры, присутствовавшей в MQL-коде. Реализация рассчитана на один инструмент и один таймфрейм, что упрощает тестирование и эксплуатацию.

Изначально советник позволял работать с шестью валютными парами, отправлял результаты оптимизации на сервер разработчика и рисовал на графике служебные объекты. В версии для StockSharp оставлена только торговая логика: формирование сигналов, открытие/закрытие позиций и базовое управление рисками через StartProtection.

Логика торговли

  1. Подписка на данные — стратегия подписывается на выбранный тип свечей (по умолчанию 1 час) и привязывает к потоку свечей индикаторы Bollinger Bands и DEMA.
  2. Анализ закрытой свечи — сигналы рассчитываются после появления новой полностью сформированной свечи.
    • Покупка
      • Свеча открылась ниже предыдущей нижней полосы Боллинджера и закрылась выше неё (ложный пробой вниз).
      • Минимум свечи выше минимума предыдущей свечи, а та, в свою очередь, обновила минимум ещё более ранней свечи (паттерн «двойное дно»).
      • Значение DEMA последовательно растёт в течение трёх отсчётов (текущий > предыдущего > предпредыдущего).
    • Продажа
      • Свеча открылась выше предыдущей верхней полосы Боллинджера и закрылась ниже неё (ложный пробой вверх).
      • Максимум свечи ниже максимума предыдущей свечи, при этом предыдущая свеча выше ещё более ранней (паттерн «двойная вершина»).
      • Значение DEMA последовательно снижается.
  3. Исполнение сделок — при выполнении условий стратегия отправляет рыночные заявки. Опция CloseOnSignal позволяет закрыть противоположную позицию перед открытием новой.
  4. Риск-менеджмент — используются опциональные уровни стоп-лосса и тейк-профита, задаваемые в абсолютных ценовых величинах. Шаговый трейлинг из оригинального MQL-скрипта не реализован.

Параметры

Параметр Назначение Значение по умолчанию
BollingerPeriod Период расчёта полос Боллинджера. 30
BollingerDeviation Множитель стандартного отклонения. 1.5
DemaPeriod Период двойной экспоненциальной средней (DEMA). 20
StopLossDistance Абсолютная дистанция стоп-лосса, передаваемая в StartProtection (0 отключает стоп). 0 (абсолют)
TakeProfitDistance Абсолютная дистанция тейк-профита, передаваемая в StartProtection. 0 (абсолют)
CloseOnSignal Закрывать ли текущую позицию при появлении обратного сигнала. false
CandleType Тип/таймфрейм свечей. 1 час

Практические замечания

  • Стратегия работает только с инструментом, указанным в Strategy.Security. Для обслуживания нескольких инструментов запустите несколько экземпляров с разными настройками.
  • Алгоритм расчёта объёма из MQL (RiskMax, LotSize, LotBalanceDivider) не переносился. Используйте свой риск-менеджмент и свойство Volume стратегии.
  • Весь код, связанный с DLL, сетевыми запросами и отрисовкой объектов на графике, удалён. Для визуализации используйте стандартные средства StockSharp.
  • Значения стопов задаются в абсолютных ценовых величинах, поэтому их нужно адаптировать под шаг цены конкретного инструмента.
  • Механизм пошагового трейлинга из оригинала отсутствует. При необходимости добавьте собственный модуль сопровождения позиции.
  • Комментарии в исходнике приведены на английском языке; к стратегии прилагаются детальные описания на английском, китайском и русском.

Отличия от MQL-версии

  • Мультивалютность: стратегия обслуживает только один инструмент, что облегчает контроль состояния.
  • Сетевая активность: в порте нет HTTP-запросов, файловой телеметрии и проверок лицензии.
  • Размер позиций: размер сделки определяется общими настройками StockSharp.
  • Графические элементы: стрелки и подписи с графика MetaTrader не воспроизводятся.
  • Трейлинг: только начальные стоп/тейк, без автоматического подтягивания.

Документация максимально подробна, чтобы упростить аудит и дальнейшее развитие стратегии без необходимости изучать исходный MQL-код.

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;
	}
}