Ver en GitHub

Estrategia de MACD Cleaner

Descripción general

La estrategia MACD Cleaner es una conversión del asesor experto "MACD Cleaner" de MetaTrader 5. Analiza velas completadas de un único marco temporal y coloca operaciones cuando la línea principal del MACD aumenta o disminuye de forma monótona durante tres barras cerradas consecutivas. El sistema siempre mantiene como máximo una posición direccional y cambia cuando el momentum se invierte.

Lógica de negociación

  • En cada vela terminada la estrategia lee la línea MACD calculada con los períodos de rápido, lento y señal configurados.
  • Si los últimos tres valores de MACD son no decrecientes, la estrategia prepara una entrada larga. Si existe una posición corta se cierra primero, luego se abre una nueva posición larga.
  • Si los últimos tres valores de MACD son no crecientes, la estrategia prepara una entrada corta. Las posiciones largas existentes se cierran antes de abrir la corta.
  • Los niveles de stop-loss y take-profit de protección se evalúan en los máximos y mínimos de las velas usando los desplazamientos basados en pips.
  • Cuando los parámetros de trailing están habilitados, el stop se mueve en la dirección de la operación una vez que el precio avanza al menos el paso de trailing configurado.
  • Todas las órdenes de salida se emiten como órdenes de mercado usando el volumen de posición agregado para asegurar que toda la posición esté cerrada.

Parámetros

Nombre Predeterminado Descripción
CandleType Velas de 1 hora Marco temporal usado para el cálculo de MACD y evaluación de órdenes.
TradeVolume 1 Volumen base enviado para una nueva posición. Si el lado opuesto está abierto, el volumen de posición absoluto se agrega para cerrarlo antes de revertir.
StopLossPips 35 Distancia de stop-loss en pips desde el precio de entrada. Establecer en cero para desactivar el stop.
TakeProfitPips 30 Distancia de take-profit en pips desde el precio de entrada. Establecer en cero para desactivar el objetivo.
TrailingStopPips 0 Distancia del trailing stop. Cuando es cero la lógica de trailing está desactivada.
TrailingStepPips 5 Movimiento favorable mínimo (en pips) requerido antes de ajustar el trailing stop. Se ignora cuando el trailing stop está desactivado.
MacdFastPeriod 15 Longitud de la EMA rápida para el indicador MACD.
MacdSlowPeriod 33 Longitud de la EMA lenta para el indicador MACD.
MacdSignalPeriod 11 Longitud de la EMA de señal para el indicador MACD.

Gestión de órdenes

  • Salidas largas: la estrategia emite una orden de venta de mercado cuando se alcanza el stop-loss, take-profit o nivel de trailing.
  • Salidas cortas: una orden de compra de mercado cierra la posición bajo las mismas condiciones, reflejadas para operaciones cortas.
  • Después de que la posición se cierra completamente, el estado del trailing se restablece para que la siguiente operación comience con niveles frescos.

Notas

  • El tamaño de pip se deriva automáticamente del instrumento. Para símbolos con 3 o 5 decimales el pip equivale a diez pasos de precio mínimo, imitando la implementación original de MetaTrader.
  • La lógica solo evalúa velas completadas y no actúa sobre cambios intrabarra.
  • Para desactivar la gestión de riesgo establezca las distancias en pips correspondientes en cero. El trailing requiere tanto TrailingStopPips como un TrailingStepPips positivo para funcionar.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class MacdCleanerStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MacdCleanerStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}