Ver en GitHub

Estrategia CoensioTrader1 V06

Descripción general

CoensioTrader1 V06 es una estrategia de seguimiento de tendencia con rompimiento, distribuida originalmente como un Expert Advisor de MetaTrader. La versión para StockSharp conserva la lógica de reconocimiento de patrones discrecional y elimina las funciones específicas del broker y de internet presentes en la implementación MQL. La estrategia opera sobre un único instrumento y marco temporal, utilizando Bollinger Bands y una media móvil exponencial doble (DEMA) para identificar movimientos de agotamiento seguidos de reanudación de tendencia.

El robot original permitía operar con hasta seis pares de divisas con conjuntos de parámetros individuales, admitía licencias basadas en DLL y reportaba resultados de optimización a un servidor remoto. Esos servicios auxiliares se omiten intencionalmente en esta versión. El foco está en el flujo central de entradas y salidas que reacciona a los rechazos de las Bollinger Bands confirmados por la estructura de swings y la pendiente del DEMA.

Lógica de la estrategia

  1. Suscripción a datos – la estrategia se suscribe al tipo de vela configurado (por defecto: 1 hora) y vincula Bollinger Bands junto con un DEMA.
  2. Rechazo de Bollinger Bands – las señales se evalúan en la última vela completamente cerrada.
    • Configuración Largo
      • La vela abrió por debajo de la Bollinger Band inferior anterior y cerró de nuevo por encima de ella (ruptura fallida hacia abajo).
      • La vela creó un mínimo más alto comparado con la barra anterior, mientras que esa barra anterior hizo un mínimo más bajo comparado con su predecesora (estructura tipo doble suelo).
      • El DEMA sube estrictamente a lo largo de las últimas tres observaciones (valor actual > anterior > segundo anterior).
    • Configuración Corto
      • La vela abrió por encima de la Bollinger Band superior anterior y cerró de nuevo por debajo de ella (ruptura fallida hacia arriba).
      • La vela hizo un máximo más bajo comparado con la barra anterior, mientras que esa barra anterior hizo un máximo más alto comparado con su predecesora (estructura de doble techo).
      • El DEMA cae estrictamente a lo largo de las últimas tres observaciones.
  3. Ejecución de órdenes – las órdenes de mercado se envían inmediatamente después de que la señal se confirma en una vela terminada. Se puede habilitar el aplanamiento opcional de posición en señales opuestas.
  4. Gestión de riesgos – las distancias opcionales de stop-loss y take-profit se proporcionan a través de StartProtection. Ambas son compensaciones de precio absoluto; la funcionalidad de trailing stop del expert original no se reproduce.

Parámetros

Nombre Descripción Por defecto
BollingerPeriod Período para el cálculo de las Bollinger Bands. 30
BollingerDeviation Multiplicador de desviación estándar para las bandas. 1.5
DemaPeriod Longitud de la media móvil exponencial doble utilizada para confirmación de tendencia. 20
StopLossDistance Compensación absoluta de stop-loss pasada a StartProtection. Establecer en cero para deshabilitar. 0 (absolute)
TakeProfitDistance Compensación absoluta de take-profit pasada a StartProtection. Establecer en cero para deshabilitar. 0 (absolute)
CloseOnSignal Cerrar la posición actual antes de abrir una nueva en la dirección opuesta. false
CandleType Tipo de datos de vela o marco temporal. Por defecto es 1 hora. 1h

Notas de uso

  • La versión de StockSharp opera únicamente en el Strategy.Security principal. Para imitar el comportamiento multi-símbolo del expert original, lance instancias de estrategia separadas con conjuntos de parámetros distintos.
  • La lógica de dimensionamiento de lotes MQL (RiskMax, LotSize, LotBalanceDivider) no fue traducida. Configure Volume en la estrategia o mediante el gestor de riesgos según las reglas de su cartera.
  • La activación basada en DLL, el registro remoto de optimización y las rutinas de dibujo UI presentes en los archivos MQL fueron eliminadas intencionalmente.
  • Los valores de stop-loss y take-profit son distancias de precio absolutas. Adáptelos al tamaño de tick o valor de pip del instrumento al configurar la estrategia.
  • El mecanismo de paso de trailing-stop original no está implementado. Si se requiere gestión de trailing, añada un módulo de riesgo dedicado sobre esta estrategia.
  • Todos los comentarios del código y las lógicas se mantienen en inglés según lo solicitado; las traducciones del README se proporcionan por separado.

Diferencias con la versión MQL

  • Gestión multi-símbolo: reemplazada con un diseño de instrumento único para mayor claridad y facilidad de prueba.
  • Redes y licencias: eliminadas; no se realizan solicitudes HTTP externas ni llamadas a DLL.
  • Dimensionamiento de órdenes: simplificado para depender del manejo estándar de Volume de StockSharp.
  • Objetos visuales: las anotaciones de gráfico de MetaTrader (flechas, etiquetas, temas de color) no se recrean. Use los helpers de gráficos de StockSharp si se requiere visualización.
  • Trailing stop: no portado; solo se configuran las órdenes protectoras iniciales.

Esta documentación pretende ser exhaustiva para que la versión portada pueda ser revisada, probada y extendida sin necesidad de leer el código fuente 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;
	}
}