Ver no GitHub

Estratégia Assistente MACD Parabolic SAR

Visão Geral

Esta estratégia é uma conversão do StockSharp do Consultor Especializado do MetaTrader gerado pelo Assistente MQL5 que combina o momentum do MACD com a direção de tendência do Parabolic SAR. A lógica reproduz o mecanismo de pontuação do assistente atribuindo uma pontuação normalizada (0..100) a cada indicador e depois ponderando as contribuições antes de tomar decisões de trading.

Lógica de Trading

  • Indicadores
    • MACD (12, 24, 9): o sinal do histograma define se o momentum altista (histograma > 0) ou o momentum baixista (histograma < 0) está ativo.
    • Parabolic SAR (0.02, 0.2): o preço de fechamento acima do ponto SAR é interpretado como tendência de alta, e abaixo do ponto SAR como tendência de baixa.
  • Construção de pontuação
    • MACD produz 100 (altista) ou 0 (baixista) pontos para o lado comprado. Os valores inversos são usados para o lado vendido.
    • O Parabolic SAR se comporta da mesma forma, fornecendo 100 pontos quando a tendência concorda com a direção respectiva.
    • Ambas as pontuações são combinadas por meio dos pesos definidos pelo usuário (MacdWeight e SarWeight). Com os pesos padrão (0.9 e 0.1), o MACD domina a decisão final assim como no modelo do assistente.
  • Regras de entrada
    • Calcular a pontuação altista: bullScore = macdBull * MacdWeight + sarBull * SarWeight.
    • Calcular a pontuação baixista: bearScore = macdBear * MacdWeight + sarBear * SarWeight.
    • Abrir uma posição comprada (ou reverter do vendido) quando bullScore >= OpenThreshold (padrão 20).
    • Abrir uma posição vendida (ou reverter do comprado) quando bearScore >= OpenThreshold.
  • Regras de saída
    • Posições compradas são fechadas quando a pontuação baixista atinge o nível de confirmação forte CloseThreshold (padrão 100).
    • Posições vendidas são fechadas quando a pontuação altista atinge CloseThreshold.
    • Os sinais de saída são avaliados antes dos sinais de entrada para imitar o comportamento do consultor especializado original que prioriza o fechamento de trades conflitantes.

Gestão de Risco

  • StopLossPoints e TakeProfitPoints replicam a gestão do dinheiro baseada em pontos do assistente. Ambos os valores são convertidos para unidades de preço usando o PriceStep do instrumento e então passados para StartProtection.
  • Defina qualquer parâmetro como 0 para desabilitar a ordem de proteção correspondente.

Parâmetros

Parâmetro Descrição Padrão
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 sinal para MACD. 9
MacdWeight Peso da pontuação MACD (0..1). 0.9
SarWeight Peso da pontuação do Parabolic SAR (0..1). 0.1
OpenThreshold Pontuação mínima para abrir/reverter posições. 20
CloseThreshold Pontuação oposta mínima para sair de posições. 100
SarStep Passo de aceleração do Parabolic SAR. 0.02
SarMax Aceleração máxima do Parabolic SAR. 0.2
StopLossPoints Distância do stop-loss em pontos de preço. 50
TakeProfitPoints Distância do take-profit em pontos de preço. 115
CandleType Fonte de dados de velas para cálculos de indicadores. Período de 15 minutos

Notas de Uso

  • Os parâmetros padrão espelham o template .mq5, portanto a estratégia se comporta de forma consistente com o consultor especializado original gerado pelo assistente.
  • Ajuste MacdWeight, SarWeight e os limiares para mudar a sensibilidade das entradas e saídas. Por exemplo, aumentar OpenThreshold exigirá confirmação mais forte antes de abrir novos trades.
  • Os campos internos _lastBullScore e _lastBearScore são atualizados a cada barra e podem ser registrados ou expostos se você precisar monitorar como a pontuação combinada evolui ao longo do tempo.
  • Como a estratégia depende de velas concluídas, certifique-se de que seu feed de dados fornece atualizações completas de velas para o CandleType selecionado.
  • A gestão do dinheiro é expressa em pontos; certifique-se de que o instrumento escolhido usa o passo de preço esperado para que as ordens de proteção se alinhem com as distâncias pretendidas.
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();
		}
	}
}