Ver en GitHub

Estrategia MACD 1 MIN SCALPER

Esta estrategia es un port en C# del asesor experto de MetaTrader "MACD 1 MIN SCALPER". Combina medias móviles ponderadas con confirmaciones de MACD en múltiples marcos temporales y un filtro de momentum antes de abrir operaciones. El objetivo es operar en la dirección de la tendencia cuando los indicadores de marcos temporales inferior y superior están alineados y el momentum del precio es suficientemente fuerte.

Lógica de trading

  1. Marco temporal base – configurable (por defecto M1). Dos medias móviles ponderadas (WMA) con períodos 50 y 200, calculadas sobre el precio típico (Máximo + Mínimo + Cierre) / 3, definen la tendencia a corto plazo.
  2. Filtro de tendencia de marco temporal superior – se calculan WMAs con los mismos períodos en el marco temporal H1. Las configuraciones largas requieren que ambas WMAs rápidas estén por encima de sus homólogas lentas, las cortas requieren lo opuesto. Si el marco temporal operativo ya es H1, las WMAs base se reutilizan.
  3. Confirmaciones MACD – el MACD (12, 26, 9) debe tener su línea principal por encima de la línea de señal en el marco temporal base, el marco temporal H1 y un marco temporal mensual (aprox. 43200 minutos). Las entradas cortas requieren que los tres MACDs estén por debajo de sus señales.
  4. Filtro de momentum – un indicador de momentum con período 14 opera en un marco temporal superior derivado del período base de MetaTrader (M1→M15, M5→M30, …). La desviación absoluta desde 100 debe superar un umbral configurable en al menos una de las últimas tres barras completadas.
  5. Reglas de entrada – se abre una posición larga cuando se cumplen todas las condiciones alcistas y la estrategia actualmente no tiene exposición larga. Una posición corta requiere las condiciones bajistas reflejadas. Si hay una posición opuesta abierta, el tamaño de la orden incluye automáticamente la cantidad necesaria para cerrarla.
  6. Gestión de riesgo – las distancias opcionales de stop-loss y take-profit se especifican en pips y se convierten a puntos del instrumento al inicio. Las funciones de trailing, breakeven y gestión monetaria del script original se omiten intencionalmente en este port de alto nivel.

Parámetros

Parámetro Descripción
CandleType Marco temporal operativo para los indicadores base.
OrderVolume Volumen enviado con cada entrada de mercado. También se usa para cerrar/voltear posiciones.
FastMaPeriod / SlowMaPeriod Longitudes de las medias móviles ponderadas rápida y lenta.
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod Períodos EMA usados por el indicador MACD.
MomentumPeriod Longitud del indicador de momentum en el marco temporal de confirmación.
MomentumThreshold Desviación absoluta mínima desde 100 requerida para aceptar el momentum.
TakeProfitPips / StopLossPips Niveles protectores opcionales especificados en pips.

Notas de implementación

  • La estrategia depende de las suscripciones de velas de alto nivel de StockSharp (SubscribeCandles) y la vinculación de indicadores (Bind / BindEx). No se usan cálculos de indicadores manuales o buffers históricos.
  • El marco temporal de momentum se deriva del mapeo de MetaTrader: [1,5,15,30,60,240,1440,10080,43200]. Si un valor cae fuera de esta lista, se usa un multiplicador 4× del marco temporal base como respaldo.
  • StartProtection se lanza solo cuando al menos uno de los parámetros de riesgo es mayor que cero. No hay implementación de trailing stop en este port.
  • La representación en gráficos está habilitada para las velas base, ambas WMAs y el MACD para facilitar la inspección visual durante la depuración o el trading en vivo.

Consejos de uso

  • Establezca el parámetro OrderVolume según el tamaño mínimo de lote del instrumento. El ayudante ajusta automáticamente el volumen enviado para que coincida con el paso y las restricciones de mínimo/máximo del símbolo.
  • Asegúrese de que los datos de marcos temporales superiores (H1 y mensual) estén disponibles en el feed de datos. Sin estas velas, la estrategia no abrirá posiciones porque las señales de confirmación permanecen incompletas.
  • El filtrado de momentum es sensible al umbral elegido. Los valores más altos exigen impulsos de momentum más fuertes mientras que los valores más bajos resultan en operaciones más frecuentes.
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;

public class Macd1MinScalperStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public Macd1MinScalperStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}