Ver en GitHub

Estrategia de activación del comerciante de patrones Macd

Descripción general

Macd Pattern Trader Trigger Strategy traslada el MetaTrader 4 asesor experto MacdPatternTraderv05cb a la estrategia de alto nivel de StockSharp API. El sistema intercambia patrones de histograma MACD puros, buscando una estructura de doble techo debajo de la línea cero para abrir posiciones cortas y una imagen especular de doble fondo por encima de la línea cero para abrir posiciones largas. La gestión comercial refleja el EA original: cada entrada se envía al mercado con un stop loss fijo configurable y una toma de ganancias medida en puntos del instrumento.

Lógica estratégica

Flujo de indicadores

  • Una suscripción de una sola vela impulsa la lógica (predeterminado: velas de 15 minutos). Cada vela terminada alimenta un indicador MovingAverageConvergenceDivergence configurado con los parámetros MT4 inusuales (fast = 13, slow = 5, signal = 1) utilizados por la fuente EA.
  • Sólo se utiliza la línea principal MACD. La estrategia almacena en buffer los últimos tres valores completados para emular iMACD(..., MODE_MAIN, shift=1..3) de MetaTrader.

Configuración alcista (entradas largas)

  1. Condición de armado: la línea MACD debe elevarse por encima de Bullish Trigger (predeterminado 0.0015). Esto prepara la estrategia para buscar la secuencia de retroceso. Cualquier caída por debajo de cero borra el estado inmediatamente.
  2. Ventana de retroceso: una vez armado, el MACD tiene que volver a caer por debajo de Bullish Reset (predeterminado 0.0005). Esto marca el área potencial de retroceso. La ventana permanece activa hasta que se confirma un patrón válido o MACD se vuelve negativo.
  3. Confirmación de patrón: mientras la ventana está activa, las últimas tres lecturas almacenadas en el búfer MACD deben satisfacer:
    • macd_curr > macd_last (el impulso vuelve a subir),
    • macd_last < macd_last3 (la barra anterior estableció el swing bajo),
    • macd_curr > Bullish Reset y macd_last < Bullish Reset (el precio rebota desde la zona de retroceso poco profunda).
  4. Ejecución – cuando se confirma, la estrategia compra en el mercado. Si existe una posición corta, el tamaño de la orden incluye automáticamente el volumen necesario para aplanarse antes de establecer la exposición larga.

Configuración bajista (entradas cortas)

  1. Condición de armado: la línea MACD debe caer por debajo de -Bearish Trigger (predeterminado -0.0015). Cualquier movimiento por encima de cero borra todo estado bajista.
  2. Ventana de retroceso: una vez armado, el MACD tiene que rebotar por encima de -Bearish Reset (predeterminado -0.0005).
  3. Confirmación de patrón: mientras la ventana está abierta, los valores almacenados en el búfer deben cumplir:
    • macd_curr < macd_last,
    • macd_last > macd_last3,
    • macd_curr < -Bearish Reset y macd_last > -Bearish Reset.
  4. Ejecución: se envía una orden de venta de mercado. Si existe una posición larga, su volumen se incluye en la orden, por lo que la cuenta termina netamente corta por el tamaño de operación configurado.

Gestión de riesgos

  • Stop loss fijo/takeprofit – las distancias se especifican en puntos (escalones de precio). La estrategia los multiplica por el PriceStep del instrumento y llama a StartProtection para reproducir el comportamiento SL/TP original. Establecer una distancia en 0 desactiva el nivel respectivo.
  • Una señal por ventana: después de realizar un pedido, los indicadores de armado y ventana se borran para evitar entradas repetidas del mismo patrón MACD.

Parámetros

  • Volumen comercial – volumen de órdenes de mercado. Las posiciones opuestas se cierran automáticamente antes de abrir la nueva operación.
  • EMA rápida / EMA lenta / Señal EMA – MACD longitudes. Los valores predeterminados replican al asesor original, pero pueden optimizarse.
  • Activador/reinicio alcista: umbrales MACD positivos (en unidades de indicador) que arman la configuración larga y definen su zona de retroceso.
  • Disparador/reinicio bajista: umbrales absolutos MACD para la configuración corta. El disparador se aplica con un signo negativo durante el tiempo de ejecución.
  • Stop Loss / Take Profit – distancias en puntos (escalones de precio). Un valor de 0 deshabilita la protección correspondiente.
  • Tipo de vela: período de tiempo utilizado para el cálculo de MACD y las decisiones comerciales.

Notas de implementación

  • El API de alto nivel de StockSharp se utiliza en todas partes: SubscribeCandles alimenta el indicador y StartProtection refleja la gestión comercial MT4.
  • El búfer de historial de MACD garantiza que la lógica de decisión funcione en las tres barras terminadas anteriores, coincidiendo con las llamadas shift=1..3 de MetaTrader.
  • No hay una versión Python de esta estrategia en el paquete API, solo la implementación C#.
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>
/// Port of the MetaTrader advisor MacdPatternTrader.
/// Implements the MACD histogram double-top/double-bottom pattern with adaptive arming logic.
/// </summary>
public class MacdPatternTraderTriggerStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _bullishTrigger;
	private readonly StrategyParam<decimal> _bullishReset;
	private readonly StrategyParam<decimal> _bearishTrigger;
	private readonly StrategyParam<decimal> _bearishReset;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergenceSignal _macd = null!;

	private decimal? _macdPrev1;
	private decimal? _macdPrev2;
	private decimal? _macdPrev3;

	private bool _bullishArmed;
	private bool _bullishWindow;
	private bool _bullishReady;

	private bool _bearishArmed;
	private bool _bearishWindow;
	private bool _bearishReady;

	private decimal _priceStep = 1m;

	/// <summary>
	/// Trade volume used for market orders.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Fast EMA length of the MACD indicator.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA length of the MACD indicator.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

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

	/// <summary>
	/// Positive MACD value that arms the bullish pattern.
	/// </summary>
	public decimal BullishTrigger
	{
		get => _bullishTrigger.Value;
		set => _bullishTrigger.Value = value;
	}

	/// <summary>
	/// Threshold that marks the bullish pullback zone.
	/// </summary>
	public decimal BullishReset
	{
		get => _bullishReset.Value;
		set => _bullishReset.Value = value;
	}

	/// <summary>
	/// Negative MACD value that arms the bearish pattern.
	/// </summary>
	public decimal BearishTrigger
	{
		get => _bearishTrigger.Value;
		set => _bearishTrigger.Value = value;
	}

	/// <summary>
	/// Threshold that marks the bearish pullback zone.
	/// </summary>
	public decimal BearishReset
	{
		get => _bearishReset.Value;
		set => _bearishReset.Value = value;
	}

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

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

	/// <summary>
	/// Candle type that drives the MACD calculation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public MacdPatternTraderTriggerStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume for entries", "Trading")
			
			.SetOptimize(0.05m, 0.3m, 0.05m);

		_fastPeriod = Param(nameof(FastPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA length for MACD", "Indicators")
			
			.SetOptimize(8, 18, 1);

		_slowPeriod = Param(nameof(SlowPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA length for MACD", "Indicators")
			
			.SetOptimize(4, 15, 1);

		_signalPeriod = Param(nameof(SignalPeriod), 1)
			.SetGreaterThanZero()
			.SetDisplay("Signal EMA", "Signal EMA length for MACD", "Indicators")
			
			.SetOptimize(1, 5, 1);

		_bullishTrigger = Param(nameof(BullishTrigger), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Bullish Trigger", "MACD level that arms the bullish pattern", "Logic");

		_bullishReset = Param(nameof(BullishReset), 20m)
			.SetGreaterThanZero()
			.SetDisplay("Bullish Reset", "MACD pullback threshold for bullish setup", "Logic");

		_bearishTrigger = Param(nameof(BearishTrigger), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Bearish Trigger", "Absolute MACD level that arms the bearish pattern", "Logic");

		_bearishReset = Param(nameof(BearishReset), 20m)
			.SetGreaterThanZero()
			.SetDisplay("Bearish Reset", "MACD pullback threshold for bearish setup", "Logic");

		_stopLossPoints = Param(nameof(StopLossPoints), 100m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in points", "Risk")
			
			.SetOptimize(50m, 200m, 25m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 300m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in points", "Risk")
			
			.SetOptimize(100m, 400m, 50m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for indicator calculations", "Data");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_macdPrev1 = null;
		_macdPrev2 = null;
		_macdPrev3 = null;
		_bullishArmed = false;
		_bullishWindow = false;
		_bullishReady = false;
		_bearishArmed = false;
		_bearishWindow = false;
		_bearishReady = false;
	}

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

		_priceStep = Security?.PriceStep ?? 1m;
		if (_priceStep <= 0m)
			_priceStep = 1m;

		var takeProfit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints * _priceStep, UnitTypes.Absolute) : null;
		var stopLoss = StopLossPoints > 0m ? new Unit(StopLossPoints * _priceStep, UnitTypes.Absolute) : null;

		StartProtection(takeProfit, stopLoss);

		_macd = new MovingAverageConvergenceDivergenceSignal();
		_macd.Macd.ShortMa.Length = FastPeriod;
		_macd.Macd.LongMa.Length = SlowPeriod;
		_macd.SignalMa.Length = SignalPeriod;

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

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

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

		if (!macdValue.IsFinal || macdValue is not MovingAverageConvergenceDivergenceSignalValue macdLineValue)
			return;

		if (macdLineValue.Macd is not decimal currentMacd)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			ShiftHistory(currentMacd);
			return;
		}

		if (_macdPrev1 is null || _macdPrev2 is null || _macdPrev3 is null)
		{
			ShiftHistory(currentMacd);
			return;
		}

		var macdCurr = _macdPrev1.Value;
		var macdLast = _macdPrev2.Value;
		var macdLast3 = _macdPrev3.Value;

		EvaluateBearishPattern(macdCurr, macdLast, macdLast3);
		EvaluateBullishPattern(macdCurr, macdLast, macdLast3);

		ShiftHistory(currentMacd);
	}

	private void EvaluateBullishPattern(decimal macdCurr, decimal macdLast, decimal macdLast3)
	{
		if (macdCurr < 0m)
		{
			_bullishArmed = false;
			_bullishWindow = false;
			_bullishReady = false;
		}
		else
		{
			if (!_bullishArmed && macdCurr > BullishTrigger)
				_bullishArmed = true;

			if (_bullishArmed && macdCurr < BullishReset)
			{
				_bullishArmed = false;
				_bullishWindow = true;
			}
		}

		if (_bullishWindow && macdCurr > macdLast && macdLast < macdLast3 && macdCurr > BullishReset && macdLast < BullishReset)
		{
			_bullishReady = true;
			_bullishWindow = false;
		}

		if (!_bullishReady)
			return;

		var volumeToBuy = TradeVolume + Math.Max(0m, -Position);
		if (volumeToBuy > 0m)
			BuyMarket(volumeToBuy);

		_bullishReady = false;
		_bullishArmed = false;
		_bullishWindow = false;
	}

	private void EvaluateBearishPattern(decimal macdCurr, decimal macdLast, decimal macdLast3)
	{
		if (macdCurr > 0m)
		{
			_bearishArmed = false;
			_bearishWindow = false;
			_bearishReady = false;
		}
		else
		{
			if (!_bearishArmed && macdCurr < -BearishTrigger)
				_bearishArmed = true;

			if (_bearishArmed && macdCurr > -BearishReset)
			{
				_bearishArmed = false;
				_bearishWindow = true;
			}
		}

		if (_bearishWindow && macdCurr < macdLast && macdLast > macdLast3 && macdCurr < -BearishReset && macdLast > -BearishReset)
		{
			_bearishReady = true;
			_bearishWindow = false;
		}

		if (!_bearishReady)
			return;

		var volumeToSell = TradeVolume + Math.Max(0m, Position);
		if (volumeToSell > 0m)
			SellMarket(volumeToSell);

		_bearishReady = false;
		_bearishArmed = false;
		_bearishWindow = false;
	}

	private void ShiftHistory(decimal current)
	{
		_macdPrev3 = _macdPrev2;
		_macdPrev2 = _macdPrev1;
		_macdPrev1 = current;
	}
}