Ver en GitHub

Estrategia MACD de Muestra

Esta estrategia replica el clásico experto MACD Sample de MetaTrader. Utiliza un cruce de MACD combinado con un filtro de tendencia EMA, niveles individuales de take-profit y stop-loss para operaciones largas y cortas, y un trailing stop opcional. El trading solo está permitido dentro de una ventana de tiempo configurable.

Detalles

  • Criterios de entrada:
    • Largo: La línea MACD está por debajo de cero y cruza hacia arriba la línea de señal mientras la EMA sube.
    • Corto: La línea MACD está por encima de cero y cruza hacia abajo la línea de señal mientras la EMA cae.
  • Criterios de salida:
    • Cruce MACD inverso.
    • Alcanzar los objetivos individuales de take-profit o stop-loss.
    • Activación del trailing stop.
  • Largo/Corto: Ambos.
  • Valores predeterminados:
    • EMA Period = 26
    • MACD Open Level = 3
    • MACD Close Level = 2
    • Take Profit Long = 50
    • Take Profit Short = 75
    • Stop Loss Long = 80
    • Stop Loss Short = 50
    • Trailing Stop = 30
    • Horario de trading: 4 a 19 UTC
  • Indicadores: MACD, EMA
  • Marco temporal: Velas de 1 hora por defecto
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>
/// MACD sample strategy with EMA trend filter.
/// Buys on MACD crossover up when above EMA, sells on crossover down when below EMA.
/// </summary>
public class MacdSampleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maTrendPeriod;

	private decimal _prevMacd;
	private decimal _prevSignal;
	private bool _hasPrev;
	private decimal _emaValue;
	private bool _hasEma;

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

	public MacdSampleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");

		_maTrendPeriod = Param(nameof(MaTrendPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMacd = 0;
		_prevSignal = 0;
		_hasPrev = false;
		_emaValue = 0;
		_hasEma = false;
	}

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

		var macdSignal = new MovingAverageConvergenceDivergenceSignal();
		var ema = new ExponentialMovingAverage { Length = MaTrendPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, ProcessEma);
		subscription
			.BindEx(macdSignal, ProcessMacd)
			.Start();
	}

	private void ProcessEma(ICandleMessage candle, decimal emaVal)
	{
		if (candle.State != CandleStates.Finished)
			return;
		_emaValue = emaVal;
		_hasEma = true;
	}

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

		if (!_hasEma)
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
		if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
			return;

		if (!_hasPrev)
		{
			_prevMacd = macd;
			_prevSignal = signal;
			_hasPrev = true;
			return;
		}

		var close = candle.ClosePrice;

		// Buy: MACD crosses above signal, price above EMA
		if (_prevMacd <= _prevSignal && macd > signal && close > _emaValue)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Sell: MACD crosses below signal, price below EMA
		else if (_prevMacd >= _prevSignal && macd < signal && close < _emaValue)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevMacd = macd;
		_prevSignal = signal;
	}
}