Ver en GitHub

Estrategia CS2011

Descripción general

La estrategia Cs2011 es un sistema de reversión traducido del asesor experto original cs2011.mq5. Supervisa el histograma MACD y la línea de señal en cada vela terminada y busca patrones de agotamiento alrededor de la línea cero. El port de C# mantiene las reglas de sincronización principales mientras las expone a través del nivel alto StockSharp API.

Lógica comercial

  • Reversiones de línea cero: cuando el valor MACD de la barra anterior está por encima de cero mientras que la barra anterior estaba por debajo de cero, la estrategia emite una señal corta. La transición opuesta (de positivo a negativo) emite una señal larga. Esto imita las entradas contrarias implementadas en el script MQL5.
  • Extremos de la línea de señal: la estrategia almacena las últimas tres lecturas de la línea de señal. Un máximo local mientras MACD permaneció negativo desencadena una entrada corta adicional; un mínimo local mientras MACD se mantuvo positivo desencadena una entrada larga. Esto reproduce las comprobaciones de patrones basadas en Sig[0], Sig[1] y Sig[2] en la fuente EA.
  • Las señales se evalúan solo en velas terminadas suministradas por SubscribeCandles, por lo que se ignoran los datos parciales.

Manejo de posiciones

  • La estrategia apunta a un tamaño de posición absoluto fijo (TargetVolume). Cuando llega una señal alcista, compra suficientes contratos para alcanzar +TargetVolume. Las señales bajistas hacen lo mismo para -TargetVolume. Se respeta la exposición existente en la misma dirección: no se realizan órdenes adicionales si ya se ha alcanzado el objetivo.
  • StartProtection refleja la configuración original de obtención de beneficios y límite de pérdidas. Las distancias de puntos se convierten en valores UnitTypes.Point y se pasan al módulo de riesgo integrado. Dejar cualquiera de los valores en 0 desactiva la barrera correspondiente.
  • Se utilizan ayudantes de alto nivel (BuyMarket, SellMarket) en lugar de la estructura de solicitud de bajo nivel de la versión MQL.

Parámetros

Nombre Predeterminado Descripción
TargetVolume 1 lote Tamaño de posición absoluto alcanzado después de una señal. Reemplaza la rutina de dimensionamiento de saldo Risk × de EA.
TakeProfitPoints 2200 Distancia en puntos de precio para la gestión del take-profit. 0 desactiva la toma de ganancias.
StopLossPoints 0 Distancia en puntos de precio para el stop-loss. 0 desactiva el stop-loss, coincidiendo con los valores predeterminados de EA.
FastEmaPeriod 30 Longitud rápida de EMA para el núcleo MACD.
SlowEmaPeriod 500 Longitud lenta de EMA para MACD.
SignalPeriod 36 Período de suavizado de la línea de señal.
CandleType 1 hour período de tiempo Fuente de vela utilizada por SubscribeCandles. Ajústelo para que coincida con el período del gráfico utilizado en MetaTrader.

Todos los parámetros se registran a través de Param() para que puedan optimizarse dentro de la interfaz de usuario del optimizador StockSharp.

Diferencias con la versión MQL5

  • La rutina de administración de dinero (Money_M) se basó en transacciones históricas y el saldo de la cuenta MetaTrader. Las estrategias StockSharp operan en carteras independientes del corredor, por lo tanto, el puerto expone un parámetro TargetVolume simple. Los usuarios pueden conectar su propia administración de dinero anulando el valor del parámetro o el método ExecuteSignals.
  • Las solicitudes de órdenes se simplifican a órdenes del mercado único. La infraestructura StockSharp maneja la lógica de reintento, la desviación basada en diferenciales y las verificaciones del contexto comercial.
  • La estrategia se ejecuta con suscripciones de velas en lugar del asistente personalizado IsNewBar. Esto garantiza que sólo se procesen velas completamente formadas.

Notas de uso

  1. Configure el valor, la cartera y el tipo de vela antes de lanzar la estrategia.
  2. Ajuste TargetVolume para que coincida con el tamaño de lote nominal deseado.
  3. Opcionalmente, ajuste TakeProfitPoints y StopLossPoints para reproducir los niveles de protección del EA original.
  4. Inicie la estrategia: los mensajes de registro registran cada activador comercial junto con la exposición objetivo.

El código contiene comentarios en línea en inglés que describen cada paso del proceso de portabilidad.


namespace StockSharp.Samples.Strategies;

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;

/// <summary>
/// MACD based reversal strategy converted from the original cs2011 MetaTrader 5 expert advisor.
/// It reacts to zero line crosses and local extremes of the MACD signal line.
/// </summary>
public class Cs2011Strategy : Strategy
{
	private readonly StrategyParam<decimal> _targetVolume;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _macdPrev1;
	private decimal? _macdPrev2;
	private decimal? _signalPrev1;
	private decimal? _signalPrev2;
	private decimal? _signalPrev3;

	/// <summary>
	/// Target absolute position in lots when a bullish signal appears.
	/// </summary>
	public decimal TargetVolume
	{
		get => _targetVolume.Value;
		set => _targetVolume.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in instrument points.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in instrument points.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Fast EMA length used in MACD calculation.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA length used in MACD calculation.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period of the MACD signal line.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

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

	/// <summary>
	/// Initialize strategy parameters with defaults close to the original expert.
	/// </summary>
	public Cs2011Strategy()
	{
		_targetVolume = Param(nameof(TargetVolume), 1m)
			.SetDisplay("Target Volume", "Absolute position size targeted on entries", "Risk")
			
			.SetOptimize(0.5m, 3m, 0.5m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 2200)
			.SetDisplay("Take Profit (points)", "Take-profit distance in price points", "Risk")
			
			.SetOptimize(200, 4000, 200);

		_stopLossPoints = Param(nameof(StopLossPoints), 0)
			.SetDisplay("Stop Loss (points)", "Stop-loss distance in price points", "Risk")
			
			.SetOptimize(0, 2000, 200);

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 30)
			.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
			
			.SetOptimize(10, 60, 5);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 50)
			.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
			
			.SetOptimize(200, 700, 20);

		_signalPeriod = Param(nameof(SignalPeriod), 36)
			.SetDisplay("Signal Period", "Signal line period for MACD", "Indicators")
			
			.SetOptimize(10, 60, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Source timeframe for MACD", "General");
	}

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

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

		_macdPrev1 = null;
		_macdPrev2 = null;
		_signalPrev1 = null;
		_signalPrev2 = null;
		_signalPrev3 = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		Volume = TargetVolume;

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastEmaPeriod },
				LongMa = { Length = SlowEmaPeriod }
			},
			SignalMa = { Length = SignalPeriod }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessCandle)
			.Start();

		var chartArea = CreateChartArea();
		if (chartArea != null)
		{
			DrawCandles(chartArea, subscription);
			DrawIndicator(chartArea, macd);
			DrawOwnTrades(chartArea);
		}

		// removed StartProtection
	}

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

		if (indicatorValue is not IMovingAverageConvergenceDivergenceSignalValue macdValue)
			return;

		if (macdValue.Macd is not decimal macd || macdValue.Signal is not decimal signal)
			return;

		var prevMacd1 = _macdPrev1;
		var prevMacd2 = _macdPrev2;
		var prevSignal1 = _signalPrev1;
		var prevSignal2 = _signalPrev2;
		var prevSignal3 = _signalPrev3;

		var upSignal = false;
		var downSignal = false;

		if (prevMacd1.HasValue && prevMacd2.HasValue)
		{
			if (prevMacd1 > 0m && prevMacd2 < 0m)
				downSignal = true;

			if (prevMacd1 < 0m && prevMacd2 > 0m)
				upSignal = true;
		}

		if (prevMacd2.HasValue && prevSignal1.HasValue && prevSignal2.HasValue && prevSignal3.HasValue)
		{
			if (prevMacd2 < 0m && prevSignal1 < prevSignal2 && prevSignal2 > prevSignal3)
				downSignal = true;

			if (prevMacd2 > 0m && prevSignal1 > prevSignal2 && prevSignal2 < prevSignal3)
				upSignal = true;
		}

		if (upSignal || downSignal)
			ExecuteSignals(upSignal, downSignal);

		_macdPrev2 = _macdPrev1;
		_macdPrev1 = macd;
		_signalPrev3 = _signalPrev2;
		_signalPrev2 = _signalPrev1;
		_signalPrev1 = signal;
	}

	private void ExecuteSignals(bool upSignal, bool downSignal)
	{
		// removed IsFormedAndOnlineAndAllowTrading guard

		if (upSignal)
		{
			var targetPosition = TargetVolume;
			var difference = targetPosition - Position;
			if (difference > 0m)
			{
				BuyMarket(difference);
				LogInfo($"Buy signal executed. Target={targetPosition:F2}, current position after order={Position + difference:F2}");
			}
		}

		if (downSignal)
		{
			var targetPosition = -TargetVolume;
			var difference = targetPosition - Position;
			if (difference < 0m)
			{
				SellMarket(-difference);
				LogInfo($"Sell signal executed. Target={targetPosition:F2}, current position after order={Position - difference:F2}");
			}
		}
	}
}