Ver en GitHub

Estrategia Asistente MACD Parabolic SAR

Descripción General

Esta estrategia es una conversión de StockSharp del Asesor Experto de MetaTrader generado por el Asistente MQL5 que combina el impulso MACD con la dirección de tendencia del Parabolic SAR. La lógica reproduce el mecanismo de puntuación del asistente asignando una puntuación normalizada (0..100) a cada indicador y luego ponderando las contribuciones antes de tomar decisiones de trading.

Lógica de Trading

  • Indicadores
    • MACD (12, 24, 9): el signo del histograma define si el impulso alcista (histograma > 0) o el impulso bajista (histograma < 0) está activo.
    • Parabolic SAR (0.02, 0.2): el precio de cierre por encima del punto SAR se interpreta como una tendencia alcista, y por debajo del punto SAR como una tendencia bajista.
  • Construcción de puntuación
    • MACD produce 100 (alcista) o 0 (bajista) puntos para el lado largo. Los valores inversos se usan para el lado corto.
    • El Parabolic SAR se comporta igual, proporcionando 100 puntos cuando la tendencia concuerda con la dirección respectiva.
    • Ambas puntuaciones se combinan mediante los pesos definidos por el usuario (MacdWeight y SarWeight). Con los pesos predeterminados (0.9 y 0.1), el MACD domina la decisión final igual que en la plantilla del asistente.
  • Reglas de entrada
    • Calcular la puntuación alcista: bullScore = macdBull * MacdWeight + sarBull * SarWeight.
    • Calcular la puntuación bajista: bearScore = macdBear * MacdWeight + sarBear * SarWeight.
    • Abrir una posición larga (o invertir desde corto) cuando bullScore >= OpenThreshold (predeterminado 20).
    • Abrir una posición corta (o invertir desde largo) cuando bearScore >= OpenThreshold.
  • Reglas de salida
    • Las posiciones largas se cierran cuando la puntuación bajista alcanza el nivel de confirmación fuerte CloseThreshold (predeterminado 100).
    • Las posiciones cortas se cierran cuando la puntuación alcista alcanza CloseThreshold.
    • Las señales de salida se evalúan antes que las señales de entrada para imitar el comportamiento del asesor experto original que prioriza el cierre de operaciones conflictivas.

Gestión de Riesgos

  • StopLossPoints y TakeProfitPoints replican la gestión del dinero basada en puntos del asistente. Ambos valores se convierten a unidades de precio usando el PriceStep del instrumento y luego se pasan a StartProtection.
  • Establezca cualquier parámetro en 0 para deshabilitar la orden de protección correspondiente.

Parámetros

Parámetro Descripción Predeterminado
MacdFastPeriod Período de EMA rápida para MACD. 12
MacdSlowPeriod Período de EMA lenta para MACD. 24
MacdSignalPeriod Período de SMA de señal para MACD. 9
MacdWeight Peso de la puntuación MACD (0..1). 0.9
SarWeight Peso de la puntuación del Parabolic SAR (0..1). 0.1
OpenThreshold Puntuación mínima para abrir/invertir posiciones. 20
CloseThreshold Puntuación opuesta mínima para salir de posiciones. 100
SarStep Paso de aceleración del Parabolic SAR. 0.02
SarMax Aceleración máxima del Parabolic SAR. 0.2
StopLossPoints Distancia del stop-loss en puntos de precio. 50
TakeProfitPoints Distancia del take-profit en puntos de precio. 115
CandleType Fuente de datos de velas para cálculos de indicadores. Marco temporal de 15 minutos

Notas de Uso

  • Los parámetros predeterminados reflejan la plantilla .mq5, por lo que la estrategia se comporta de manera consistente con el asesor experto original generado por el asistente.
  • Ajuste MacdWeight, SarWeight y los umbrales para cambiar la sensibilidad de las entradas y salidas. Por ejemplo, aumentar OpenThreshold requerirá una confirmación más fuerte antes de abrir nuevas operaciones.
  • Los campos internos _lastBullScore y _lastBearScore se actualizan en cada barra y pueden registrarse o exponerse si necesita monitorear cómo evoluciona la puntuación combinada a lo largo del tiempo.
  • Debido a que la estrategia depende de velas terminadas, asegúrese de que su feed de datos proporcione actualizaciones completas de velas para el CandleType seleccionado.
  • La gestión del dinero se expresa en puntos; asegúrese de que el instrumento elegido use el paso de precio esperado para que las órdenes de protección se alineen con las distancias previstas.
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>
/// Strategy that replicates the MetaTrader "MACD + Parabolic SAR" expert built with the MQL5 Wizard.
/// Combines the trend direction from Parabolic SAR with MACD momentum scores and uses weighted thresholds for decisions.
/// </summary>
public class MacdParabolicSarWizardStrategy : Strategy
{
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _macdSignalPeriod;
	private readonly StrategyParam<decimal> _macdWeight;
	private readonly StrategyParam<decimal> _sarWeight;
	private readonly StrategyParam<decimal> _openThreshold;
	private readonly StrategyParam<decimal> _closeThreshold;
	private readonly StrategyParam<decimal> _sarStep;
	private readonly StrategyParam<decimal> _sarMax;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergenceSignal _macd = null!;
	private ParabolicSar _parabolicSar = null!;
	private decimal _lastBullScore;
	private decimal _lastBearScore;

	/// <summary>
	/// Fast EMA period for MACD.
	/// </summary>
	public int MacdFastPeriod
	{
		get => _macdFastPeriod.Value;
		set => _macdFastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period for MACD.
	/// </summary>
	public int MacdSlowPeriod
	{
		get => _macdSlowPeriod.Value;
		set => _macdSlowPeriod.Value = value;
	}

	/// <summary>
	/// Signal line period for MACD.
	/// </summary>
	public int MacdSignalPeriod
	{
		get => _macdSignalPeriod.Value;
		set => _macdSignalPeriod.Value = value;
	}

	/// <summary>
	/// Weight of the MACD signal in the combined score (0..1).
	/// </summary>
	public decimal MacdWeight
	{
		get => _macdWeight.Value;
		set => _macdWeight.Value = value;
	}

	/// <summary>
	/// Weight of the Parabolic SAR signal in the combined score (0..1).
	/// </summary>
	public decimal SarWeight
	{
		get => _sarWeight.Value;
		set => _sarWeight.Value = value;
	}

	/// <summary>
	/// Minimum combined bullish or bearish score required to open a position.
	/// </summary>
	public decimal OpenThreshold
	{
		get => _openThreshold.Value;
		set => _openThreshold.Value = value;
	}

	/// <summary>
	/// Minimum opposite score required to exit the current position.
	/// </summary>
	public decimal CloseThreshold
	{
		get => _closeThreshold.Value;
		set => _closeThreshold.Value = value;
	}

	/// <summary>
	/// Acceleration step for Parabolic SAR.
	/// </summary>
	public decimal SarStep
	{
		get => _sarStep.Value;
		set => _sarStep.Value = value;
	}

	/// <summary>
	/// Maximum acceleration factor for Parabolic SAR.
	/// </summary>
	public decimal SarMax
	{
		get => _sarMax.Value;
		set => _sarMax.Value = value;
	}

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

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

	/// <summary>
	/// Type of candles used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize <see cref="MacdParabolicSarWizardStrategy"/>.
	/// </summary>
	public MacdParabolicSarWizardStrategy()
	{
		_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
		.SetDisplay("MACD Fast", "Fast EMA period for MACD", "MACD")
		;

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 24)
		.SetDisplay("MACD Slow", "Slow EMA period for MACD", "MACD")
		;

		_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
		.SetDisplay("MACD Signal", "Signal SMA period for MACD", "MACD")
		;

		_macdWeight = Param(nameof(MacdWeight), 0.9m)
		.SetDisplay("MACD Weight", "Relative weight of MACD in scoring", "Scoring")
		;

		_sarWeight = Param(nameof(SarWeight), 0.1m)
		.SetDisplay("SAR Weight", "Relative weight of SAR in scoring", "Scoring")
		;

		_openThreshold = Param(nameof(OpenThreshold), 90m)
		.SetDisplay("Open Threshold", "Score required to open trades", "Scoring")
		;

		_closeThreshold = Param(nameof(CloseThreshold), 90m)
		.SetDisplay("Close Threshold", "Score required to exit trades", "Scoring")
		;

		_sarStep = Param(nameof(SarStep), 0.02m)
		.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Parabolic SAR")
		;

		_sarMax = Param(nameof(SarMax), 0.2m)
		.SetDisplay("SAR Max", "Maximum acceleration for Parabolic SAR", "Parabolic SAR")
		;

		_stopLossPoints = Param(nameof(StopLossPoints), 50m)
		.SetDisplay("Stop Loss (pts)", "Stop-loss distance in points", "Risk")
		;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 115m)
		.SetDisplay("Take Profit (pts)", "Take-profit distance in points", "Risk")
		;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Primary candle source", "General");
	}

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

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

		_lastBullScore = 0m;
		_lastBearScore = 0m;
	}

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

		// Configure MACD indicator replicating the wizard defaults.
		_macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFastPeriod },
				LongMa = { Length = MacdSlowPeriod },
			},
			SignalMa = { Length = MacdSignalPeriod }
		};

		// Configure Parabolic SAR indicator.
		_parabolicSar = new ParabolicSar
		{
			AccelerationStep = SarStep,
			AccelerationMax = SarMax,
		};

		// Subscribe to candles and bind indicators.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_macd, _parabolicSar, ProcessCandle)
			.Start();

		// Configure risk management using point-based distances.
		var step = Security?.PriceStep ?? 1m;
		var takeProfit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints * step, UnitTypes.Absolute) : new Unit();
		var stopLoss = StopLossPoints > 0m ? new Unit(StopLossPoints * step, UnitTypes.Absolute) : new Unit();

		StartProtection(takeProfit, stopLoss, useMarketOrders: true);

		// Prepare chart visualization if the environment supports it.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _parabolicSar);
			DrawIndicator(area, _macd);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue sarValue)
	{
		// Process only finished candles to avoid premature trades.
		if (candle.State != CandleStates.Finished)
			return;

		if (!macdValue.IsFinal || !sarValue.IsFinal)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
		if (macdTyped.Macd is not decimal macdLine || macdTyped.Signal is not decimal signalLine)
			return;

		var sar = sarValue.ToDecimal();

		// Translate indicator states into normalized scores (0..100).
		var macdBull = macdLine > signalLine ? 100m : 0m;
		var macdBear = macdLine < signalLine ? 100m : 0m;
		var sarBull = candle.ClosePrice > sar ? 100m : 0m;
		var sarBear = candle.ClosePrice < sar ? 100m : 0m;

		var bullScore = macdBull * MacdWeight + sarBull * SarWeight;
		var bearScore = macdBear * MacdWeight + sarBear * SarWeight;

		_lastBullScore = bullScore;
		_lastBearScore = bearScore;

		// Exit conditions take priority to mirror the wizard behaviour.
		if (Position > 0 && bearScore >= CloseThreshold)
		{
			SellMarket();
			return;
		}

		if (Position < 0 && bullScore >= CloseThreshold)
		{
			BuyMarket();
			return;
		}

		// Entry rules: open when the weighted score exceeds the open threshold.
		if (Position <= 0 && bullScore >= OpenThreshold)
		{
			BuyMarket();
			return;
		}

		if (Position >= 0 && bearScore >= OpenThreshold)
		{
			SellMarket();
		}
	}
}