Ver no GitHub

MACD Exemplo de estratégia clássica

A estratégia reproduz o consultor especialista MetaTrader 4 "MACD Exemplo" usando o API de alto nível de StockSharp. Ele negocia em ambas as direções em um único instrumento e reflete a lógica original: faça negociações quando a linha MACD cruza sua linha de sinal no lado correto de zero enquanto uma tendência EMA confirma a direção. As ordens de proteção são convertidas para o gerenciador de risco integrado do StockSharp com trailing stops opcionais.

Lógica de negociação

  1. Aguarde pelo menos 100 velas concluídas para que MACD e EMA contenham histórico suficiente.
  2. Calcule um padrão MACD (12, 26, 9) junto com sua linha de sinal e uma média móvel exponencial de 26 períodos que atua como um filtro direcional.
  3. Entrada longa – permitida apenas quando não existe posição. O MACD deve estar abaixo de zero, mas cruzando acima da linha de sinal, o valor anterior de MACD estava abaixo de seu sinal, o valor absoluto de MACD excede o limite configurável de MacdOpenLevel (em faixas de preço) e a tendência EMA está subindo.
  4. Entrada curta – a configuração simétrica: MACD acima de zero cruzando abaixo de seu sinal, o MACD anterior estava acima do sinal, o valor atual excede o limite MacdOpenLevel e a tendência EMA está caindo.
  5. Saída longa – quando MACD cruza novamente abaixo do sinal no lado positivo de zero e o valor está acima de MacdCloseLevel. A posição também pode ser fechada mais cedo pelo trailing stop ou take-profit gerenciado por StartProtection.
  6. Saída curta – quando MACD cruza de volta o sinal no lado negativo e o valor absoluto de MACD excede MacdCloseLevel, ou pelos módulos de proteção.

A estratégia nunca mantém mais de uma posição por vez. Cada entrada usa ordens de mercado dimensionadas pela propriedade Volume. A lógica de proteção depende do controlador de risco de StockSharp para que as distâncias de lucro e os trailing stops permaneçam sincronizados com o tamanho do tick do instrumento.

Parâmetros

Nome Descrição Padrão Notas
FastEmaPeriod Período EMA rápido usado por MACD. 12 Faixa otimizável 6…18.
SlowEmaPeriod Período EMA lento usado por MACD. 26 Faixa otimizável 20…32.
SignalPeriod Período de sinal EMA dentro de MACD. 9 Faixa otimizável 5…13.
TrendMaPeriod Comprimento EMA para o filtro direcional. 26 Faixa otimizável 20…40.
MacdOpenLevel Limite de entrada expresso em MACD pontos (etapas de preço). 3 Equivalente a MACDOpenLevel no código MT4.
MacdCloseLevel Limite de saída expresso em MACD pontos. 2 Equivalente a MACDCloseLevel.
TakeProfitPoints Obtenha lucro em faixas de preço (multiplicado pelo tamanho do tick do instrumento). 50 Defina como 0 para desativar o lucro.
TrailingStopPoints Trailing stop em faixas de preço. 30 Defina como 0 para desativar o trailing stop.
CandleType Série de velas usada para atualizações de indicadores. Período de 5 minutos Suporta qualquer tipo de vela StockSharp.

Notas de implementação

  • Os indicadores MACD e EMA estão vinculados à assinatura da vela por meio de BindEx/Bind, permitindo que StockSharp alimente valores prontos para uso sem armazenamento em cache manual.
  • As posições são abertas somente quando a plataforma reporta IsFormedAndOnlineAndAllowTrading(), evitando negociações enquanto os dados históricos ainda estão sendo carregados ou a conexão está offline.
  • Todos os limites que se referem a "pontos" são automaticamente dimensionados pela etapa de preço do instrumento, imitando a constante Point de MetaTrader.
  • StartProtection converte o take-profit fixo e o trailing stop de MetaTrader em ordens de proteção do lado da bolsa. Habilite ou desabilite cada módulo alterando o parâmetro correspondente.
  • O registro extensivo (LogInfo) documenta cada decisão comercial, simplificando a comparação com o consultor especialista original durante a validação da migração.

Dicas de uso

  • O EA original tem como alvo as principais empresas de Forex em prazos intradiários. Comece com símbolos semelhantes e ajuste os parâmetros se o instrumento usar um tamanho de tick diferente.
  • Ao testar símbolos com valores de tick exóticos, verifique se Security.PriceStep está configurado; caso contrário, o padrão 1.0 será usado.
  • Combine com os recursos de proteção de portfólio do StockSharp se você precisar de gerenciamento de dinheiro no nível da conta além dos limites por posição.

Etiquetas

  • Acompanhamento de tendências
  • Momento
  • MACD cruzamento
  • Intradiário (padrão 5 minutos)
  • Trailing Stop + Take Profit
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>
/// MACD strategy that replicates the original MetaTrader "MACD Sample" expert advisor behaviour.
/// </summary>
public class MacdSampleClassicStrategy : Strategy
{

	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<int> _trendMaPeriod;
	private readonly StrategyParam<decimal> _macdOpenLevel;
	private readonly StrategyParam<decimal> _macdCloseLevel;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _trailingStopPoints;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _minimumHistoryCandles;

	private decimal _pointSize;
	private decimal? _prevMacd;
	private decimal? _prevSignal;
	private decimal? _trendMaCurrent;
	private decimal? _trendMaPrevious;
	private int _finishedCandles;
	private DateTimeOffset? _lastProcessedTime;

	/// <summary>
	/// Fast EMA period for the MACD indicator.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period for the MACD indicator.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

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

	/// <summary>
	/// Period of the trend EMA used as direction filter.
	/// </summary>
	public int TrendMaPeriod
	{
		get => _trendMaPeriod.Value;
		set => _trendMaPeriod.Value = value;
	}

	/// <summary>
	/// Threshold for MACD entries expressed in points (price steps).
	/// </summary>
	public decimal MacdOpenLevel
	{
		get => _macdOpenLevel.Value;
		set => _macdOpenLevel.Value = value;
	}

	/// <summary>
	/// Threshold for MACD exits expressed in points (price steps).
	/// </summary>
	public decimal MacdCloseLevel
	{
		get => _macdCloseLevel.Value;
		set => _macdCloseLevel.Value = value;
	}

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

	/// <summary>
	/// Trailing stop distance measured in price points.
	/// </summary>
	public decimal TrailingStopPoints
	{
		get => _trailingStopPoints.Value;
		set => _trailingStopPoints.Value = value;
	}

	/// <summary>
	/// Candle type used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}
	/// <summary>
	/// Number of finished candles required before the strategy begins trading.
	/// </summary>
	public int MinimumHistoryCandles
	{
		get => _minimumHistoryCandles.Value;
		set => _minimumHistoryCandles.Value = value;
	}

	/// <summary>
	/// Initialize default parameters for the MACD Sample strategy.
	/// </summary>
	public MacdSampleClassicStrategy()
	{
		Volume = 1;

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
		.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators")
		
		.SetOptimize(6, 18, 2);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
		.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators")
		
		.SetOptimize(20, 32, 2);

		_signalPeriod = Param(nameof(SignalPeriod), 9)
		.SetDisplay("Signal EMA", "Signal EMA period for MACD", "Indicators")
		
		.SetOptimize(5, 13, 2);

		_trendMaPeriod = Param(nameof(TrendMaPeriod), 26)
		.SetDisplay("Trend EMA", "EMA period used for directional filter", "Indicators")
		
		.SetOptimize(20, 40, 2);

		_macdOpenLevel = Param(nameof(MacdOpenLevel), 0m)
		.SetDisplay("MACD Open", "Entry threshold in MACD points", "Signals")
		
		.SetOptimize(1m, 5m, 1m);

		_macdCloseLevel = Param(nameof(MacdCloseLevel), 0m)
		.SetDisplay("MACD Close", "Exit threshold in MACD points", "Signals")
		
		.SetOptimize(1m, 4m, 1m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 50m)
		.SetDisplay("Take Profit", "Take profit distance in price points", "Risk")
		
		.SetOptimize(20m, 100m, 10m);

		_trailingStopPoints = Param(nameof(TrailingStopPoints), 30m)
		.SetDisplay("Trailing Stop", "Trailing stop distance in price points", "Risk")
		
		.SetOptimize(10m, 60m, 10m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
		.SetDisplay("Candle Type", "Candle type used for analysis", "General");
		_minimumHistoryCandles = Param(nameof(MinimumHistoryCandles), 30)
			.SetDisplay("Warm-up candles", "Number of finished candles required before trading starts", "General")
			.SetGreaterThanZero();
	}

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

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

		_pointSize = 0m;
		_prevMacd = null;
		_prevSignal = null;
		_trendMaCurrent = null;
		_trendMaPrevious = null;
		_finishedCandles = 0;
		_lastProcessedTime = null;
	}

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

		_pointSize = Security?.PriceStep ?? 1m;

		// Configure indicators exactly as in the original expert advisor.
		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastEmaPeriod },
				LongMa = { Length = SlowEmaPeriod },
			},
			SignalMa = { Length = SignalPeriod }
		};

		var trendMa = new ExponentialMovingAverage { Length = TrendMaPeriod };

		// Subscribe to candles and bind indicators for automatic updates.
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(macd, ProcessMacdValues);
		subscription.Bind(trendMa, ProcessTrendMaValue);
		subscription.Start();

		// Visualize price, indicators and trades when a chart is available.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, macd);
			DrawIndicator(area, trendMa);
			DrawOwnTrades(area);
		}

		var takeProfitDistance = TakeProfitPoints * _pointSize;
		var trailingDistance = TrailingStopPoints * _pointSize;

		if (takeProfitDistance > 0m || trailingDistance > 0m)
		{
			StartProtection(
			takeProfitDistance > 0m ? new Unit(takeProfitDistance, UnitTypes.Absolute) : null,
			trailingDistance > 0m ? new Unit(trailingDistance, UnitTypes.Absolute) : null,
			isStopTrailing: trailingDistance > 0m);
		}
	}

	private void ProcessTrendMaValue(ICandleMessage candle, decimal maValue)
	{
		if (candle.State != CandleStates.Finished)
		return;

		// Keep current and previous EMA values for the directional filter.
		_trendMaPrevious = _trendMaCurrent;
		_trendMaCurrent = maValue;
	}

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

		// Avoid duplicate processing for the same candle.
		if (_lastProcessedTime != candle.OpenTime)
		{
			_lastProcessedTime = candle.OpenTime;
			_finishedCandles++;
		}

		// indicators checked below

		if (_finishedCandles < MinimumHistoryCandles)
		return;

		var macdSignal = (MovingAverageConvergenceDivergenceSignalValue)macdValue;

		if (macdSignal.Macd is not decimal macdCurrent ||
		macdSignal.Signal is not decimal signalCurrent)
		{
			return;
		}

		if (_prevMacd is not decimal macdPrevious ||
		_prevSignal is not decimal signalPrevious ||
		_trendMaCurrent is not decimal trendMaCurrent ||
		_trendMaPrevious is not decimal trendMaPrevious)
		{
			_prevMacd = macdCurrent;
			_prevSignal = signalCurrent;
			return;
		}

		var macdOpenThreshold = MacdOpenLevel * _pointSize;
		var macdCloseThreshold = MacdCloseLevel * _pointSize;

		// Determine trend direction using EMA slope.
		var isTrendUp = trendMaCurrent > trendMaPrevious;
		var isTrendDown = trendMaCurrent < trendMaPrevious;

		var buySignal = macdCurrent < 0m &&
		macdCurrent > signalCurrent &&
		macdPrevious < signalPrevious &&
		Math.Abs(macdCurrent) > macdOpenThreshold &&
		isTrendUp;

		var sellSignal = macdCurrent > 0m &&
		macdCurrent < signalCurrent &&
		macdPrevious > signalPrevious &&
		macdCurrent > macdOpenThreshold &&
		isTrendDown;

		var exitLongSignal = macdCurrent > 0m &&
		macdCurrent < signalCurrent &&
		macdPrevious > signalPrevious &&
		macdCurrent > macdCloseThreshold;

		var exitShortSignal = macdCurrent < 0m &&
		macdCurrent > signalCurrent &&
		macdPrevious < signalPrevious &&
		Math.Abs(macdCurrent) > macdCloseThreshold;

		if (buySignal && Position == 0m)
		{
			// MACD crossed up in negative territory and EMA confirms uptrend.
			BuyMarket();
			LogInfo($"Open long: MACD {macdCurrent:F5} above signal {signalCurrent:F5}.");
		}
		else if (sellSignal && Position == 0m)
		{
			// MACD crossed down in positive territory and EMA confirms downtrend.
			SellMarket();
			LogInfo($"Open short: MACD {macdCurrent:F5} below signal {signalCurrent:F5}.");
		}
		else if (exitLongSignal && Position > 0m)
		{
			// MACD crossed back below the signal line in positive zone - close long.
			SellMarket();
			LogInfo($"Close long: MACD {macdCurrent:F5} dropped under signal {signalCurrent:F5}.");
		}
		else if (exitShortSignal && Position < 0m)
		{
			// MACD crossed back above the signal line in negative zone - close short.
			BuyMarket();
			LogInfo($"Close short: MACD {macdCurrent:F5} rose above signal {signalCurrent:F5}.");
		}

		_prevMacd = macdCurrent;
		_prevSignal = signalCurrent;
	}
}