Ver en GitHub

4 SMA Strategy

Overview

The 4 SMA strategy replicates the MetaTrader expert advisor 4 SMA.mq4. It works on 30-minute candles calculated with median prices and compares four simple moving averages (5, 20, 40, and 60 periods) to detect momentum breakouts. The StockSharp port keeps the single-position behaviour of the original code and uses high-level API helpers for market entries and risk management.

Trading Logic

  • Calculate the median price (high + low) / 2 for each finished candle and feed it into the four SMAs.
  • Long entry happens when the fast SMA is above the medium SMA, the medium SMA is above the slow SMA, the slow SMA is above the very slow SMA by at least one price step, and the previous slow SMA was below or equal to the very slow SMA. Only one long position can be active at a time.
  • Short entry is the mirror condition: the fast SMA is below the medium SMA, the medium SMA is below the slow SMA, the very slow SMA is above the slow SMA by at least one price step, and the previous slow SMA was above or equal to the very slow SMA. Only one short position can be active at a time.

Position Management

  • The strategy closes longs when the slow SMA crosses below the very slow SMA and closes shorts when the slow SMA crosses above the very slow SMA.
  • Protective levels are pre-computed after each entry. Stop-loss and take-profit distances follow the original point-based settings and rely on the security price step.
  • Trailing stops activate after price moves beyond the configured trailing distance. The stop is trailed candle by candle and never loosened.

Parameters

Name Description Default
CandleType Candle series used for calculations (30-minute by default). M30 time frame
TakeProfit Take-profit distance in points. 50
StopLoss Stop-loss distance in points. 50
TrailingStop Trailing stop distance in points. 11
FastLength Length of the fast SMA. 5
MediumLength Length of the medium SMA. 20
SlowLength Length of the slow SMA. 40
VerySlowLength Length of the very slow SMA. 60

All numeric parameters are exposed for optimisation via the StockSharp parameter UI.

Differences from the MQL Version

  • The original trailing stop manipulated MT4 orders directly; the port recalculates exit prices and issues market orders when levels are breached.
  • Price-step aware calculations let the strategy operate on instruments with non-forex tick sizes.
  • The StockSharp implementation relies on high-level SubscribeCandles bindings and strategy parameters, staying close to framework best practices.
using System;

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

namespace StockSharp.Samples.Strategies;

public class FourSmaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FourSmaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevFast = fast;
		_prevSlow = slow;
	}
}