Ver en GitHub

MACD Estrategia PSAR fija

Descripción general

Esta estrategia es una adaptación de C# del asesor experto MetaTrader EA_MACD_FixedPSAR. Intercambia cambios de tendencia combinando un filtro cruzado MACD con una verificación de tendencia EMA. La gestión de riesgos refleja la implementación original y admite tanto un trailing stop de distancia fija como un modo de seguimiento estilo Parabolic SAR. Todas las distancias se configuran en pips y se convierten internamente a unidades de precio según el tamaño del tick del instrumento.

Indicadores

  • MovingAverageConvergenceDivergenceSignal (12, 26, 9) entrega MACD y líneas de señal.
  • ExponentialMovingAverage (predeterminado 26) confirma la dirección de la tendencia a corto plazo.

Lógica de trading

  1. Condiciones de entrada
    • Largo: MACD cruza por encima de su línea de señal mientras permanece por debajo de cero, el valor absoluto de MACD excede el MACD nivel de apertura y el EMA está aumentando en comparación con la vela anterior.
    • Corto: MACD cruza por debajo de su línea de señal mientras permanece por encima de cero, el valor absoluto de MACD excede el MACD nivel de apertura y el EMA está cayendo en comparación con la vela anterior.
  2. Condiciones de salida
    • MACD reversión que excede el MACD Nivel de Cierre en la dirección opuesta.
    • Niveles configurables de toma de ganancias y límite de pérdidas, ambos medidos en pips.
    • Comportamiento de trailing stop opcional:
      • Fijo: mantiene una distancia constante desde el último cierre.
      • Fijo PSAR: emula el ajuste incremental Parabolic SAR utilizado por la versión MQL.

Parámetros

Nombre Descripción
Volume Volumen de negociación utilizado para órdenes de mercado.
TakeProfitPips Distancia de toma de ganancias en pips.
StopLossPips Distancia de stop-loss en pips.
TrailMode Lógica de trailing stop (None, Fixed, FixedPsar).
TrailingStopPips Distancia para el modo de seguimiento fijo.
PsarStep Factor de aceleración inicial para el modo de seguimiento PSAR.
PsarMaximum Factor de aceleración máximo para el modo de seguimiento PSAR.
MacdOpenLevelPips Magnitud mínima de MACD (en pips) necesaria para abrir una posición.
MacdCloseLevelPips Magnitud mínima MACD (en pips) necesaria para cerrar una posición.
TrendPeriod EMA período utilizado para la confirmación de tendencias.
CandleType Tipo de serie de velas para cálculos de indicadores.

Notas

  • Todos los umbrales se almacenan en pips y se traducen a unidades de precio utilizando el tamaño del tick del instrumento (con cinco o tres correcciones decimales que emulan el ajuste MetaTrader.
  • La lógica de trailing stop se actualiza solo en velas completamente formadas para evitar salidas prematuras.
  • La estrategia dibuja velas, ambos indicadores y marcas comerciales en el área del gráfico predeterminado cuando están disponibles.
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();
	}
}