Ver en GitHub

Estrategia Simple MACD

Simple MACD replica la lógica del asesor MQL5 Simple_MACD.mq5 en StockSharp. La estrategia sigue la pendiente de la línea principal del MACD calculada en velas completadas y continúa añadiendo a la posición siempre que la pendiente permanezca en la misma dirección.

Descripción general

  • Mercado: cualquier instrumento con datos de velas y horario de trading continuo.
  • Indicador principal: Convergencia/Divergencia de Medias Móviles (MACD) usando medias móviles exponenciales 12/26 y señal 9.
  • Enfoque: seguimiento de momentum. La estrategia compara las dos lecturas de MACD completadas más recientes y va largo cuando la línea sube, o corto cuando la línea cae.
  • Tipo de orden: solo órdenes de mercado. Cada señal agrega la cantidad requerida para cerrar la posición opuesta y añade el volumen de trading configurado encima, imitando el asesor experto original.

Notas de Conversión

  • El bot MQL5 se activaba una vez por nueva barra comparando MACD(1) y MACD(2) (las dos barras completadas anteriores). En StockSharp, la misma comparación se ejecuta cuando una vela termina, antes de que comience la siguiente barra.
  • La versión MQL dependía de la enumeración explícita de posiciones y verificaciones manuales de volumen. La versión StockSharp agrega el volumen automáticamente con llamadas BuyMarket/SellMarket y el parámetro TradeVolume de la estrategia.
  • Las verificaciones de cobertura del código MQL no son necesarias porque StockSharp rastrea directamente la posición neta.

Reglas de Trading

Entrada y Escalado

  1. Calcular la línea principal del MACD en cada vela terminada.
  2. Almacenar los dos últimos valores del MACD y compararlos:
    • Si MACD(1) > MACD(2), la pendiente es alcista. La estrategia compra un volumen igual a TradeVolume + max(0, -Posición) para cerrar cortos y añadir nuevos largos.
    • Si MACD(1) < MACD(2), la pendiente es bajista. La estrategia vende TradeVolume + max(0, Posición) para cerrar largos y añadir nuevos cortos.
  3. Si ambos valores son iguales, no se envían nuevas órdenes.

Gestión de Posiciones

  • La estrategia continúa apilando órdenes en la dirección actual mientras la pendiente del MACD no cambie de signo, al igual que el asesor original que enviaba una compra o venta en cada barra calificada.
  • Las señales opuestas eliminan cualquier exposición abierta antes de construir la nueva posición.
  • No se incorporan niveles de stop-loss ni toma de ganancias; el control de riesgo depende de reglas externas de gestión de dinero o supervisión manual.

Salvaguardas Adicionales

  • El trading se omite hasta que el indicador MACD esté completamente formado.
  • Solo se procesan velas completadas (CandleStates.Finished), previniendo acciones prematuras en datos parciales.
  • Los mensajes de registro rastrean cada operación y muestran los dos valores del MACD usados para tomar la decisión para un análisis de backtesting más fácil.

Parámetros

Parámetro Valor predeterminado Descripción
FastPeriod 12 Longitud de la EMA rápida para el cálculo del MACD.
SlowPeriod 26 Longitud de la EMA lenta para el cálculo del MACD.
SignalPeriod 9 Período de la EMA de señal retenido para compatibilidad con la configuración original.
TradeVolume 0.1 Volumen añadido en cada señal antes de contabilizar la reversión de posición.
CandleType Marco temporal de 1 minuto Tipo de vela usado para alimentar el indicador. Ajustable a cualquier marco temporal deseado.

Todos los parámetros están expuestos como parámetros de estrategia y marcados como optimizables donde sea relevante.

Visualización

  • La estrategia crea automáticamente un área de gráfico (cuando está disponible) con las velas de precio y superpone la salida del indicador MACD.
  • Las operaciones propias se dibujan en el gráfico para mostrar con qué frecuencia la estrategia escala posiciones en condiciones de tendencia.

Uso Recomendado

  • Aplicar en instrumentos con tendencia donde el momentum persiste por varias barras; los mercados en rango causarán frecuentes reversiones y operaciones whipsaw.
  • Combinar con gestión de riesgo a nivel de portafolio ya que la lógica base no tiene mecanismo de stop intrínseco.
  • Considerar optimizar el TradeVolume y los períodos del MACD para el instrumento y marco temporal objetivo.

Archivos

  • CS/SimpleMacdStrategy.cs – implementación en StockSharp de la lógica de la estrategia.
  • README.md, README_ru.md, README_zh.md – documentación detallada en tres idiomas.
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;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simple MACD slope-following strategy converted from MQL5 Simple_MACD.mq5.
/// The strategy evaluates the MACD main line on completed candles and builds positions accordingly.
/// </summary>
public class SimpleMacdStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergence _macd = null!;
	private decimal? _previousMacdValue;
	private decimal? _prePreviousMacdValue;
	private int? _previousSlope;

	/// <summary>
	/// Fast EMA period used for the MACD main line.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period used for the MACD main line.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Signal EMA period used by the MACD indicator.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	/// <summary>
	/// Trading volume applied when new orders are sent.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Candle type used to feed the MACD indicator.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize Simple MACD strategy with default parameters.
	/// </summary>
	public SimpleMacdStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetDisplay("MACD Fast Period", "Fast EMA length for MACD calculation", "Indicators")
			
			.SetOptimize(6, 18, 2);

		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetDisplay("MACD Slow Period", "Slow EMA length for MACD calculation", "Indicators")
			
			.SetOptimize(20, 40, 2);

		_signalPeriod = Param(nameof(SignalPeriod), 9)
			.SetDisplay("MACD Signal Period", "Signal EMA length maintained for compatibility", "Indicators")
			
			.SetOptimize(6, 18, 1);

		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
			.SetDisplay("Trade Volume", "Order volume used for each signal", "Risk")
			
			.SetOptimize(0.1m, 1m, 0.1m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for MACD calculations", "General");
	}

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

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

		_previousMacdValue = null;
		_prePreviousMacdValue = null;
		_previousSlope = null;
	}

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

		// Configure MACD indicator to match the source MQL strategy settings.
		_macd = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = FastPeriod },
			LongMa = { Length = SlowPeriod },
		};

		// Subscribe to candle data and bind the MACD indicator.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_macd, ProcessCandle)
			.Start();

		// Prepare visual elements when charts are available.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _macd);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
	{
		// React only to completed candles to avoid premature decisions.
		if (candle.State != CandleStates.Finished)
			return;

		// Ensure the indicator produced a valid value.
		if (!_macd.IsFormed || !macdValue.IsFinal)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macdLine = macdValue.ToDecimal();

		// Accumulate historical MACD values for slope calculations.
		if (_previousMacdValue is null)
		{
			_previousMacdValue = macdLine;
			return;
		}

		if (_prePreviousMacdValue is null)
		{
			_prePreviousMacdValue = _previousMacdValue;
			_previousMacdValue = macdLine;
			return;
		}

		var macdPrev = _previousMacdValue.Value;
		var macdPrevPrev = _prePreviousMacdValue.Value;

		var currentSlope = macdPrev > macdPrevPrev ? 1 : macdPrev < macdPrevPrev ? -1 : 0;

		if (currentSlope == 0)
		{
			_prePreviousMacdValue = _previousMacdValue;
			_previousMacdValue = macdLine;
			return;
		}

		if (_previousSlope == currentSlope)
		{
			_prePreviousMacdValue = _previousMacdValue;
			_previousMacdValue = macdLine;
			return;
		}

		if (currentSlope > 0)
		{
			// Close shorts and open (or add to) longs when the MACD slope turns positive.
			var volumeToBuy = TradeVolume + Math.Max(0m, -Position);
			if (volumeToBuy > 0m)
			{
				BuyMarket(volumeToBuy);
				LogInfo($"Bullish slope detected. MACD(1)={macdPrev:F5}, MACD(2)={macdPrevPrev:F5}. Buying {volumeToBuy}.");
			}
		}
		else
		{
			// Close longs and open (or add to) shorts when the MACD slope turns negative.
			var volumeToSell = TradeVolume + Math.Max(0m, Position);
			if (volumeToSell > 0m)
			{
				SellMarket(volumeToSell);
				LogInfo($"Bearish slope detected. MACD(1)={macdPrev:F5}, MACD(2)={macdPrevPrev:F5}. Selling {volumeToSell}.");
			}
		}

		// Update stored values so the next candle compares the two previous MACD readings.
		_previousSlope = currentSlope;
		_prePreviousMacdValue = _previousMacdValue;
		_previousMacdValue = macdLine;
	}
}