Ver no GitHub

MACD Estratégia PSAR corrigida

Visão geral

Esta estratégia é uma versão C# do consultor especialista MetaTrader EA_MACD_FixedPSAR. Ele negocia reversões de tendência combinando um filtro cruzado MACD com uma verificação de tendência EMA. O gerenciamento de riscos reflete a implementação original e suporta um trailing stop de distância fixa e um modo de trailing estilo Parabolic SAR. Todas as distâncias são configuradas em pips e convertidas internamente em unidades de preço com base no tamanho do tick do instrumento.

Indicadores

  • MovingAverageConvergenceDivergenceSignal (12, 26, 9) fornece MACD e linhas de sinal.
  • ExponentialMovingAverage (padrão 26) confirma a direção da tendência de curto prazo.

Lógica de negociação

  1. Condições de entrada
    • Longo: MACD cruza acima de sua linha de sinal enquanto permanece abaixo de zero, o valor absoluto de MACD excede o MACD nível aberto e o EMA está subindo em comparação com a vela anterior.
    • Venda: MACD cruza abaixo de sua linha de sinal enquanto permanece acima de zero, o valor absoluto de MACD excede o MACD nível aberto e o EMA está caindo em comparação com a vela anterior.
  2. Exit Conditions
    • MACD reversão que excede o MACD Nível de Fechamento na direção oposta.
    • Níveis configuráveis de take-profit e stop-loss, ambos medidos em pips.
    • Comportamento de parada móvel opcional:
      • Fixo: mantém uma distância constante do último fechamento.
      • Corrigido PSAR: emula o ajuste incremental Parabolic SAR usado pela versão MQL.

Parâmetros

Nome Descrição
Volume Volume de negociação utilizado para ordens de mercado.
TakeProfitPips Distância de lucro em pips.
StopLossPips Distância de stop-loss em pips.
TrailMode Lógica de parada final (None, Fixed, FixedPsar).
TrailingStopPips Distância para o modo de rastreamento fixo.
PsarStep Fator de aceleração inicial para o modo de trilha PSAR.
PsarMaximum Fator máximo de aceleração para o modo de trilha PSAR.
MacdOpenLevelPips Magnitude mínima de MACD (em pips) necessária para abrir uma posição.
MacdCloseLevelPips Magnitude mínima de MACD (em pips) necessária para fechar uma posição.
TrendPeriod Período EMA usado para confirmação de tendência.
CandleType Tipo de série de velas para cálculos de indicadores.

Notas

  • Todos os limites são armazenados em pips e traduzidos em unidades de preço usando o tamanho do tick do instrumento (com cinco ou três correções decimais emulando o ajuste MetaTrader).
  • A lógica de trailing stop é atualizada apenas em velas totalmente formadas para evitar saídas prematuras.
  • A estratégia desenha velas, indicadores e marcas registradas na área padrão do gráfico, quando disponível.
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;

/// <summary>
/// Simplified from "EA_MACD_FixedPSAR" MetaTrader expert.
/// Combines MACD histogram crossover with EMA trend filter.
/// Buys when MACD histogram goes positive and price above EMA.
/// Sells when MACD histogram goes negative and price below EMA.
/// </summary>
public class MacdFixedPsarStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<int> _trendPeriod;

	private MovingAverageConvergenceDivergence _macd;
	private ExponentialMovingAverage _trendEma;
	private readonly Queue<decimal> _macdHistory = new();
	private decimal? _prevHistogram;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	public int TrendPeriod
	{
		get => _trendPeriod.Value;
		set => _trendPeriod.Value = value;
	}

	public MacdFixedPsarStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for MACD calculations", "General");

		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period for MACD", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period for MACD", "Indicators");

		_signalPeriod = Param(nameof(SignalPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Signal Period", "Signal line smoothing period", "Indicators");

		_trendPeriod = Param(nameof(TrendPeriod), 60)
			.SetGreaterThanZero()
			.SetDisplay("Trend EMA", "Trend filter EMA period (computed as SMA)", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevHistogram = null;
		_macdHistory.Clear();

		_macd = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = FastPeriod },
			LongMa = { Length = SlowPeriod },
		};
		_trendEma = new ExponentialMovingAverage { Length = TrendPeriod };

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

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

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

		if (!_macd.IsFormed || !_trendEma.IsFormed)
			return;

		var close = candle.ClosePrice;

		// Compute signal line manually
		_macdHistory.Enqueue(macdValue);
		if (_macdHistory.Count > SignalPeriod)
			_macdHistory.Dequeue();

		if (_macdHistory.Count < SignalPeriod)
			return;

		decimal signalSum = 0;
		var history = _macdHistory.ToArray();
		foreach (var v in history)
			signalSum += v;
		var signal = signalSum / history.Length;

		var histogram = macdValue - signal;

		if (_prevHistogram is null)
		{
			_prevHistogram = histogram;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		var crossUp = _prevHistogram.Value <= 0 && histogram > 0;
		var crossDown = _prevHistogram.Value >= 0 && histogram < 0;

		if (crossUp && close > trendValue)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossDown && close < trendValue)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevHistogram = histogram;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_macd = null;
		_trendEma = null;
		_prevHistogram = null;
		_macdHistory.Clear();

		base.OnReseted();
	}
}