Ver en GitHub

MACD Stochastic 2 Strategy

This strategy reproduces the MetaTrader "MACD Stochastic 2" expert logic with StockSharp's high-level API. It combines a three-bar MACD swing filter with a stochastic oscillator to look for momentum reversals near oversold and overbought regions. Risk is controlled through direction-specific stops, take-profits, and an optional trailing stop that operates in pip units.

Overview

  • Works on any instrument and time frame provided through the CandleType parameter.
  • Uses the MACD main line to confirm local troughs/peaks while the MACD histogram and signal line remain available for visualization.
  • Confirms entries with a stochastic %K reading below 20 for longs and above 80 for shorts.
  • Adapts MetaTrader pip handling by deriving pip size from the instrument's price step, multiplying by 10 when the symbol has 3 or 5 decimal places.

Trading Logic

Long Entry

  1. MACD main line values of the current and previous two finished candles are all below zero.
  2. The current MACD value is greater than the previous value, while the previous value is less than the value two bars ago (local trough).
  3. Stochastic %K is below 20 (oversold).
  4. No existing long position is open (Position <= 0). Any short position is flattened before entering the new long.

Short Entry

  1. MACD main line values of the current and previous two finished candles are all above zero.
  2. The current MACD value is less than the previous value, while the previous value is greater than the value two bars ago (local peak).
  3. Stochastic %K is above 80 (overbought).
  4. No existing short position is open (Position >= 0). Any long position is closed before entering the new short.

Risk Management & Exits

  • Hard Stop / Take Profit: Each direction has independent pip-based stop-loss and take-profit distances. Pips are converted to absolute price offsets using the computed pip size.
  • Trailing Stop: When enabled, the trailing stop activates after price advances beyond the trailing distance. The stop is raised/lowered only when the move exceeds the configured trailing step to avoid excessive order churn.
  • Opposite Signals: Entering an opposite signal first flat-tens the existing position, then opens the new one with the configured trade volume.

Parameters

Parameter Default Description
TradeVolume 1 Order volume sent with each new trade.
StopLossBuyPips 50 Pip distance for long stop-loss. Set to 0 to disable.
StopLossSellPips 50 Pip distance for short stop-loss. Set to 0 to disable.
TakeProfitBuyPips 50 Pip distance for long take-profit. Set to 0 to disable.
TakeProfitSellPips 50 Pip distance for short take-profit. Set to 0 to disable.
TrailingStopPips 0 Trailing stop distance in pips. 0 disables trailing.
TrailingStepPips 5 Minimum pip gain before updating the trailing stop. Must stay positive when trailing is enabled.
MacdFastPeriod 12 Fast EMA length for MACD.
MacdSlowPeriod 26 Slow EMA length for MACD.
MacdSignalPeriod 9 Signal smoothing length for MACD.
StochasticKPeriod 5 Lookback period for stochastic %K.
StochasticDPeriod 3 Smoothing period for stochastic %D.
StochasticSlowing 3 Additional smoothing applied to stochastic %K.
CandleType 1h time frame Candle type (time frame) used for indicator calculations.

Notes

  • The pip size calculation mirrors the original MetaTrader expert: pip = PriceStep and is multiplied by 10 when the instrument is quoted with 3 or 5 decimals.
  • Stochastic thresholds (20/80) remain constants as in the original script. Adjust them directly in code if custom levels are needed.
  • The strategy operates on fully finished candles only, ensuring consistency with MetaTrader's bar-close execution.

Usage

  1. Configure the desired instrument, CandleType, and volume before starting the strategy.
  2. Tune stop, take-profit, and trailing parameters to match the instrument's volatility.
  3. Optionally optimize MACD and stochastic lengths using StockSharp's optimizer thanks to the exposed parameters.
  4. Monitor the chart objects (candles, MACD, stochastic, own trades) added automatically when a chart area is available.
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);
		}
	}
}