Ver en GitHub

Estrategia MACD Stochastic 2

Esta estrategia reproduce la lógica del experto MetaTrader "MACD Stochastic 2" con la API de alto nivel de StockSharp. Combina un filtro de oscilación MACD de tres barras con un oscilador estocástico para buscar reversiones de momentum cerca de regiones de sobrecompra y sobreventa. El riesgo se controla mediante stops y take-profits específicos por dirección y un trailing stop opcional que opera en unidades de pips.

Descripción general

  • Funciona en cualquier instrumento y marco temporal proporcionado mediante el parámetro CandleType.
  • Usa la línea principal de MACD para confirmar mínimos/máximos locales, mientras que el histograma y la línea de señal del MACD permanecen disponibles para visualización.
  • Confirma entradas con una lectura de %K del estocástico por debajo de 20 para largos y por encima de 80 para cortos.
  • Adapta el manejo de pips de MetaTrader derivando el tamaño del pip del step de precio del instrumento, multiplicando por 10 cuando el símbolo tiene 3 o 5 decimales.

Lógica de negociación

Entrada larga

  1. Los valores de la línea principal de MACD de la vela actual y las dos anteriores completadas están todos por debajo de cero.
  2. El valor actual de MACD es mayor que el anterior, mientras que el anterior es menor que el de hace dos barras (mínimo local).
  3. %K del estocástico está por debajo de 20 (sobrevendido).
  4. No hay posición larga existente abierta (Position <= 0). Cualquier posición corta se cierra antes de entrar en el nuevo largo.

Entrada corta

  1. Los valores de la línea principal de MACD de la vela actual y las dos anteriores completadas están todos por encima de cero.
  2. El valor actual de MACD es menor que el anterior, mientras que el anterior es mayor que el de hace dos barras (máximo local).
  3. %K del estocástico está por encima de 80 (sobrecomprado).
  4. No hay posición corta existente abierta (Position >= 0). Cualquier posición larga se cierra antes de entrar en el nuevo corto.

Gestión del riesgo y salidas

  • Stop fijo / Take Profit: Cada dirección tiene distancias de stop-loss y take-profit independientes basadas en pips. Los pips se convierten a offsets de precio absolutos usando el tamaño del pip calculado.
  • Trailing Stop: Cuando está habilitado, el trailing stop se activa después de que el precio avanza más allá de la distancia de trailing. El stop solo se sube/baja cuando el movimiento supera el paso de trailing configurado para evitar excesiva rotación de órdenes.
  • Señales opuestas: Entrar en una señal opuesta primero cierra la posición existente y luego abre la nueva con el volumen de negociación configurado.

Parámetros

Parámetro Predeterminado Descripción
TradeVolume 1 Volumen de orden enviado con cada nueva operación.
StopLossBuyPips 50 Distancia en pips del stop-loss para largos. Establezca en 0 para desactivar.
StopLossSellPips 50 Distancia en pips del stop-loss para cortos. Establezca en 0 para desactivar.
TakeProfitBuyPips 50 Distancia en pips del take-profit para largos. Establezca en 0 para desactivar.
TakeProfitSellPips 50 Distancia en pips del take-profit para cortos. Establezca en 0 para desactivar.
TrailingStopPips 0 Distancia del trailing stop en pips. 0 desactiva el trailing.
TrailingStepPips 5 Ganancia mínima en pips antes de actualizar el trailing stop. Debe mantenerse positivo cuando el trailing está activo.
MacdFastPeriod 12 Longitud de la EMA rápida para MACD.
MacdSlowPeriod 26 Longitud de la EMA lenta para MACD.
MacdSignalPeriod 9 Longitud del suavizado de señal para MACD.
StochasticKPeriod 5 Período de lookback para el %K estocástico.
StochasticDPeriod 3 Período de suavizado para el %D estocástico.
StochasticSlowing 3 Suavizado adicional aplicado al %K estocástico.
CandleType marco temporal de 1h Tipo de vela (marco temporal) utilizado para los cálculos de indicadores.

Notas

  • El cálculo del tamaño del pip refleja el experto original de MetaTrader: pip = PriceStep y se multiplica por 10 cuando el instrumento cotiza con 3 o 5 decimales.
  • Los umbrales del estocástico (20/80) permanecen como constantes al igual que en el script original. Ajústalos directamente en el código si se necesitan niveles personalizados.
  • La estrategia opera solo en velas completamente cerradas, garantizando consistencia con la ejecución al cierre de barra de MetaTrader.

Uso

  1. Configura el instrumento deseado, CandleType y el volumen antes de iniciar la estrategia.
  2. Ajusta los parámetros de stop, take-profit y trailing para adaptarlos a la volatilidad del instrumento.
  3. Opcionalmente optimiza las longitudes de MACD y estocástico usando el optimizador de StockSharp gracias a los parámetros expuestos.
  4. Monitorea los objetos del gráfico (velas, MACD, estocástico, operaciones propias) añadidos automáticamente cuando hay un área de gráfico disponible.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MACD and stochastic based swing strategy.
/// Buys when MACD turns up in negative zone + stochastic is oversold.
/// Sells when MACD turns down in positive zone + stochastic is overbought.
/// </summary>
public class MacdStochastic2Strategy : Strategy
{
	private readonly StrategyParam<decimal> _oversoldThreshold;
	private readonly StrategyParam<decimal> _overboughtThreshold;
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _stochasticKPeriod;
	private readonly StrategyParam<int> _stochasticDPeriod;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _macdPrev1;
	private decimal _macdPrev2;
	private int _macdCount;

	public decimal OversoldThreshold { get => _oversoldThreshold.Value; set => _oversoldThreshold.Value = value; }
	public decimal OverboughtThreshold { get => _overboughtThreshold.Value; set => _overboughtThreshold.Value = value; }
	public int MacdFastPeriod { get => _macdFastPeriod.Value; set => _macdFastPeriod.Value = value; }
	public int MacdSlowPeriod { get => _macdSlowPeriod.Value; set => _macdSlowPeriod.Value = value; }
	public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
	public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
	public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
	public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public MacdStochastic2Strategy()
	{
		_oversoldThreshold = Param(nameof(OversoldThreshold), 20m)
			.SetDisplay("Oversold", "Stochastic oversold threshold", "Stochastic");

		_overboughtThreshold = Param(nameof(OverboughtThreshold), 80m)
			.SetDisplay("Overbought", "Stochastic overbought threshold", "Stochastic");

		_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("MACD Fast", "Fast EMA length for MACD", "MACD");

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("MACD Slow", "Slow EMA length for MACD", "MACD");

		_stochasticKPeriod = Param(nameof(StochasticKPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic K", "Lookback for %K", "Stochastic");

		_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic D", "Smoothing for %D", "Stochastic");

		_stopLoss = Param(nameof(StopLoss), 1000m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 2000m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Take profit in price units", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for calculations", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_macdPrev1 = 0m;
		_macdPrev2 = 0m;
		_macdCount = 0;
	}

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

		var macd = new MovingAverageConvergenceDivergence(
			new ExponentialMovingAverage { Length = MacdSlowPeriod },
			new ExponentialMovingAverage { Length = MacdFastPeriod });

		var rsi = new RelativeStrengthIndex { Length = StochasticKPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, (candle, rsiValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				// Process MACD manually
				var macdResult = macd.Process(candle.ClosePrice, candle.CloseTime, true);
				if (!macd.IsFormed)
					return;

				var macdValue = macdResult.ToDecimal();

				_macdCount++;
				if (_macdCount < 3)
				{
					_macdPrev2 = _macdPrev1;
					_macdPrev1 = macdValue;
					return;
				}

				var macd0 = macdValue;
				var macd1 = _macdPrev1;
				var macd2 = _macdPrev2;

				// Buy: MACD in negative zone turning up + stochastic oversold
				var longSignal = macd0 < 0m && macd1 < 0m && macd2 < 0m &&
					macd0 > macd1 && macd1 < macd2 &&
					rsiValue < OversoldThreshold;

				// Sell: MACD in positive zone turning down + stochastic overbought
				var shortSignal = macd0 > 0m && macd1 > 0m && macd2 > 0m &&
					macd0 < macd1 && macd1 > macd2 &&
					rsiValue > OverboughtThreshold;

				if (longSignal && Position <= 0)
					BuyMarket();
				else if (shortSignal && Position >= 0)
					SellMarket();

				_macdPrev2 = _macdPrev1;
				_macdPrev1 = macdValue;
			})
			.Start();

		StartProtection(
			new Unit(TakeProfit, UnitTypes.Absolute),
			new Unit(StopLoss, UnitTypes.Absolute));

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