Ver no GitHub

Estratégia CoensioTrader1 V06

Visão geral

CoensioTrader1 V06 é uma estratégia de seguidor de tendência com rompimento originalmente distribuída como um Expert Advisor do MetaTrader. O port para StockSharp mantém a lógica de reconhecimento de padrões discricionária enquanto remove as funcionalidades específicas do broker e da internet da implementação MQL. A estratégia opera em um único instrumento e período, usando Bollinger Bands e uma média móvel exponencial dupla (DEMA) para identificar movimentos de exaustão seguidos de retomada de tendência.

O robô original permitia negociar até seis pares de moedas com conjuntos de parâmetros individuais, suportava licenciamento baseado em DLL e reportava resultados de otimização para um servidor remoto. Esses serviços auxiliares são omitidos intencionalmente neste port. O foco é o fluxo central de entrada e saída que reage a rejeições de Bollinger Bands confirmadas pela estrutura de swing e pela inclinação do DEMA.

Lógica da estratégia

  1. Subscrição de dados – a estratégia se inscreve no tipo de candle configurado (padrão: 1 hora) e vincula Bollinger Bands juntamente com um DEMA.
  2. Rejeição de Bollinger Bands – os sinais são avaliados no último candle completamente fechado.
    • Configuração Comprado
      • O candle abriu abaixo da Bollinger Band inferior anterior e fechou de volta acima dela (rompimento falso para baixo).
      • O candle criou uma mínima mais alta em comparação com a barra anterior, enquanto essa barra anterior fez uma mínima mais baixa em comparação com seu predecessor (estrutura estilo fundo duplo).
      • O DEMA está estritamente subindo ao longo das últimas três observações (valor atual > anterior > segundo anterior).
    • Configuração Vendido
      • O candle abriu acima da Bollinger Band superior anterior e fechou de volta abaixo dela (rompimento falso para cima).
      • O candle fez uma máxima mais baixa em comparação com a barra anterior, enquanto essa barra anterior fez uma máxima mais alta em comparação com seu predecessor (estrutura de topo duplo).
      • O DEMA está estritamente caindo ao longo das últimas três observações.
  3. Execução de ordens – ordens de mercado são enviadas imediatamente após o sinal ser confirmado em um candle terminado. O achatamento opcional de posição em sinais opostos pode ser habilitado.
  4. Gestão de risco – distâncias opcionais de stop-loss e take-profit são fornecidas através de StartProtection. Ambas são offsets de preço absoluto; a funcionalidade de trailing stop do expert original não é reproduzida.

Parâmetros

Nome Descrição Padrão
BollingerPeriod Período para o cálculo das Bollinger Bands. 30
BollingerDeviation Multiplicador de desvio padrão para as bandas. 1.5
DemaPeriod Comprimento da média móvel exponencial dupla usada para confirmação de tendência. 20
StopLossDistance Offset absoluto de stop-loss passado para StartProtection. Definir como zero para desabilitar. 0 (absolute)
TakeProfitDistance Offset absoluto de take-profit passado para StartProtection. Definir como zero para desabilitar. 0 (absolute)
CloseOnSignal Fechar a posição atual antes de abrir uma nova na direção oposta. false
CandleType Tipo de dado de candle ou período. Padrão é o período de 1 hora. 1h

Notas de uso

  • A versão StockSharp negocia apenas o Strategy.Security principal. Para imitar o comportamento multi-símbolo do expert original, lance instâncias de estratégia separadas com conjuntos de parâmetros distintos.
  • A lógica de dimensionamento de lotes MQL (RiskMax, LotSize, LotBalanceDivider) não foi traduzida. Configure Volume na estratégia ou via gerenciador de risco de acordo com as regras do seu portfólio.
  • A ativação baseada em DLL, o registro remoto de otimização e as rotinas de desenho de UI presentes nos arquivos MQL foram intencionalmente removidos.
  • Os valores de stop-loss e take-profit são distâncias de preço absolutas. Adapte-os ao tamanho do tick ou valor de pip do instrumento ao configurar a estratégia.
  • O mecanismo original de passo de trailing-stop não está implementado. Se o gerenciamento de trailing for necessário, adicione um módulo de risco dedicado sobre esta estratégia.
  • Todos os comentários do código e lógicas são mantidos em inglês conforme solicitado; as traduções do README são fornecidas separadamente.

Diferenças em relação à versão MQL

  • Gestão multi-símbolo: substituída por um design de instrumento único para maior clareza e testes mais fáceis.
  • Redes e licenciamento: removidos; nenhuma solicitação HTTP externa ou chamada de DLL é realizada.
  • Dimensionamento de ordens: simplificado para depender do tratamento padrão de Volume do StockSharp.
  • Objetos visuais: anotações de gráfico do MetaTrader (setas, rótulos, temas de cor) não são recriados. Use os helpers de gráfico do StockSharp se visualização for necessária.
  • Trailing stop: não portado; apenas as ordens protetoras iniciais são configuradas.

Esta documentação pretende ser exaustiva para que o port possa ser revisado, testado e estendido sem necessidade de ler o código fonte MQL original.

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