Ver en GitHub

Estrategia Macd Pattern Trader DoubleTop

Descripción general

Puerto del asesor experto MetaTrader 4 MacdPatternTraderv04cb. La estrategia escanea una línea principal MACD configurable para Patrones de doble techo bajista y doble fondo alcista. Cuando el segundo swing no logra superar al primero mientras el MACD permanece más allá de un nivel de activación positivo o negativo, la estrategia abre una posición de mercado en la dirección del reversión anticipada. Las órdenes de protección reproducen las distancias fijas originales de stop-loss de 100 pips y de toma de ganancias de 300 pips.

Reglas comerciales

  1. Suscríbase a la serie de velas seleccionada (predeterminado: período de tiempo de 30 minutos) y calcule la línea principal MACD con el configurados períodos rápido, lento y de señal (predeterminados: 5, 13 y 1).
  2. Realice un seguimiento de los últimos tres valores MACD terminados. Una configuración bajista se arma una vez que el MACD se mantiene por encima del TriggerLevel, forma un máximo local y luego desciende. La configuración se valida cuando el siguiente máximo MACD es inferior al almacenado previamente alto mientras el MACD todavía está por encima del disparador. En ese momento se envía una venta de mercado.
  3. Refleje la misma lógica bajo cero. Cuando el MACD permanece por debajo de -TriggerLevel, forma una depresión y la siguiente depresión es mayor que el anterior, la estrategia abre una compra de mercado.
  4. Restablezca los picos y valles almacenados cada vez que la línea MACD vuelva a cruzar dentro del [-TriggerLevel, TriggerLevel] rango. Esto coincide con el comportamiento original EA que cancela la búsqueda de patrones cuando el impulso pierde fuerza.
  5. Los tamaños de posición comienzan desde el TradeVolume configurado. Al cambiar de dirección, la estrategia agrega suficiente volumen para aplanar la exposición opuesta antes de establecer la nueva operación.
  6. Llame a StartProtection una vez al inicio para que tanto el stop-loss de 100 pips como el take-profit de 300 pips sean gestionados por el plataforma incluso después de reiniciar.

Parámetros

Nombre Descripción
FastPeriod Longitud rápida de EMA utilizada por MACD.
SlowPeriod Longitud lenta de EMA utilizada por MACD.
SignalPeriod Longitud de suavizado de la línea de señal para MACD.
TriggerLevel Nivel absoluto de MACD requerido para activar la detección de doble techo/doble fondo.
StopLossPips Distancia del tope de protección en pips (por defecto 100).
TakeProfitPips Distancia de la toma de ganancias en pips (por defecto 300).
TradeVolume Volumen base de pedidos para nuevas posiciones.
CandleType Serie de velas utilizadas para los cálculos de indicadores.

Notas

  • El stop-loss y el take-profit se convierten de pips en pasos de instrumentos antes de pasarlos a StartProtection, manteniendo el comportamiento idéntico al experto MQL4 original.
  • Todos los indicadores y comentarios comerciales dentro del código fuente de C# están escritos en inglés, según lo exige el repositorio. directrices.
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>
/// Port of the MetaTrader 4 expert advisor MacdPatternTraderv04cb.
/// Detects bearish and bullish double-top/double-bottom patterns on the MACD main line.
/// The second swing needs to be weaker than the first one before a market order is sent.
/// Fixed protective orders replicate the original 100/300 pip stop-loss and take-profit distances.
/// </summary>
public class MacdPatternTraderDoubleTopStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _triggerLevel;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergenceSignal _macd = null!;
	private decimal? _previousMacd;
	private decimal? _previousMacd2;
	private decimal? _firstPeak;
	private decimal? _firstTrough;
	private bool _sellPatternArmed;
	private bool _buyPatternArmed;

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public MacdPatternTraderDoubleTopStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
		.SetGreaterThanZero()
		.SetDisplay("Fast EMA", "Fast moving average length used by MACD", "MACD")
		
		.SetOptimize(3, 15, 1);

		_slowPeriod = Param(nameof(SlowPeriod), 13)
		.SetGreaterThanZero()
		.SetDisplay("Slow EMA", "Slow moving average length used by MACD", "MACD")
		
		.SetOptimize(10, 30, 1);

		_signalPeriod = Param(nameof(SignalPeriod), 1)
		.SetGreaterThanZero()
		.SetDisplay("Signal EMA", "Signal smoothing period for MACD", "MACD")
		
		.SetOptimize(1, 9, 1);

		_triggerLevel = Param(nameof(TriggerLevel), 50m)
		.SetNotNegative()
		.SetDisplay("Trigger Level", "Absolute MACD level that arms the pattern logic", "MACD");

		_stopLossPips = Param(nameof(StopLossPips), 100m)
		.SetNotNegative()
		.SetDisplay("Stop-Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management")
		
		.SetOptimize(50m, 200m, 10m);

		_takeProfitPips = Param(nameof(TakeProfitPips), 300m)
		.SetNotNegative()
		.SetDisplay("Take-Profit (pips)", "Take-profit distance expressed in pips", "Risk Management")
		
		.SetOptimize(150m, 500m, 10m);

		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
		.SetGreaterThanZero()
		.SetDisplay("Trade Volume", "Order volume used for new entries", "Trading")
		
		.SetOptimize(0.05m, 1m, 0.05m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
		.SetDisplay("Candle Type", "Timeframe used for MACD calculations", "General");
	}

/// <summary>
/// Fast EMA length used by MACD.
/// </summary>
public int FastPeriod
{
	get => _fastPeriod.Value;
	set => _fastPeriod.Value = value;
}

/// <summary>
/// Slow EMA length used by MACD.
/// </summary>
public int SlowPeriod
{
	get => _slowPeriod.Value;
	set => _slowPeriod.Value = value;
}

/// <summary>
/// Signal EMA length used by MACD.
/// </summary>
public int SignalPeriod
{
	get => _signalPeriod.Value;
	set => _signalPeriod.Value = value;
}

/// <summary>
/// Absolute MACD level that enables pattern tracking.
/// </summary>
public decimal TriggerLevel
{
	get => _triggerLevel.Value;
	set => _triggerLevel.Value = value;
}

/// <summary>
/// Stop-loss distance expressed in pips.
/// </summary>
public decimal StopLossPips
{
	get => _stopLossPips.Value;
	set => _stopLossPips.Value = value;
}

/// <summary>
/// Take-profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
	get => _takeProfitPips.Value;
	set => _takeProfitPips.Value = value;
}

/// <summary>
/// Trading volume used for new market orders.
/// </summary>
public decimal TradeVolume
{
	get => _tradeVolume.Value;
	set => _tradeVolume.Value = value;
}

/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
	get => _candleType.Value;
	set => _candleType.Value = value;
}

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

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

	_previousMacd = null;
	_previousMacd2 = null;
	_firstPeak = null;
	_firstTrough = null;
	_sellPatternArmed = false;
	_buyPatternArmed = false;
}

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

	Volume = TradeVolume;

	_macd = new MovingAverageConvergenceDivergenceSignal
	{
		Macd =
		{
			ShortMa = { Length = FastPeriod },
			LongMa = { Length = SlowPeriod },
		},
	SignalMa = { Length = SignalPeriod },
};

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

var pipSize = Security?.PriceStep ?? 0.01m;
if (pipSize <= 0m) pipSize = 0.01m;

StartProtection(
	takeProfit: TakeProfitPips > 0m ? new Unit(TakeProfitPips * pipSize, UnitTypes.Absolute) : null,
	stopLoss: StopLossPips > 0m ? new Unit(StopLossPips * pipSize, UnitTypes.Absolute) : null,
	useMarketOrders: true);
}

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

	if (!macdValue.IsFinal || macdValue is not MovingAverageConvergenceDivergenceSignalValue typed)
	return;

	if (typed.Macd is not decimal macdLine)
	return;

	var previous = _previousMacd;
	var previous2 = _previousMacd2;

	if (previous.HasValue && previous2.HasValue)
	{
		ProcessSellPattern(macdLine, previous.Value, previous2.Value);
		ProcessBuyPattern(macdLine, previous.Value, previous2.Value);
	}

_previousMacd2 = previous;
_previousMacd = macdLine;
}

private void ProcessSellPattern(decimal current, decimal previous, decimal previous2)
{
	if (current > TriggerLevel && current < previous && previous > previous2)
	{
		if (!_sellPatternArmed)
		{
			_firstPeak = previous;
			_sellPatternArmed = true;
		}
	else if (_firstPeak is decimal referencePeak && previous < referencePeak)
	{
		EnterShort();
		ResetSellPattern();
	}
}
else if (current < TriggerLevel)
{
	ResetSellPattern();
}
}

private void ProcessBuyPattern(decimal current, decimal previous, decimal previous2)
{
	var negativeTrigger = -TriggerLevel;

	if (current < negativeTrigger && current > previous && previous < previous2)
	{
		if (!_buyPatternArmed)
		{
			_firstTrough = previous;
			_buyPatternArmed = true;
		}
	else if (_firstTrough is decimal referenceTrough && previous > referenceTrough)
	{
		EnterLong();
		ResetBuyPattern();
	}
}
else if (current > negativeTrigger)
{
	ResetBuyPattern();
}
}

private void EnterShort()
{
	if (!IsFormedAndOnlineAndAllowTrading())
	return;

	var volume = Volume + Math.Max(0m, Position);
	if (volume <= 0m)
	return;

	SellMarket(volume);
}

private void EnterLong()
{
	if (!IsFormedAndOnlineAndAllowTrading())
	return;

	var volume = Volume + Math.Max(0m, -Position);
	if (volume <= 0m)
	return;

	BuyMarket(volume);
}

private void ResetSellPattern()
{
	_sellPatternArmed = false;
	_firstPeak = null;
}

private void ResetBuyPattern()
{
	_buyPatternArmed = false;
	_firstTrough = null;
}

}