GitHub で見る

Triple SMA Crossover Strategy

Overview

The Triple SMA Crossover strategy replicates the original MQL expert advisor 3sma.mq4. The system analyses three simple moving averages (SMA) calculated on the closing price and trades when the short-term trend aligns with the medium- and long-term averages. The conversion keeps the original trading rules while adapting them to the StockSharp high-level strategy API.

Trading Logic

  1. Calculate three SMAs with configurable periods.
  2. Exit existing long positions when the fast SMA drops below the medium SMA.
  3. Exit existing short positions when the fast SMA rises above the medium SMA.
  4. Enter a new long position when:
    • Fast SMA is above the medium SMA by at least the configured spread.
    • Medium SMA is above the slow SMA by at least the configured spread.
    • No long position is currently open.
  5. Enter a new short position when:
    • Fast SMA is below the medium SMA by at least the configured spread.
    • Medium SMA is below the slow SMA by at least the configured spread.
    • No short position is currently open.

Parameters

  • Candle Type – Primary timeframe used to compute the moving averages.
  • Fast SMA Length – Period for the fast SMA (MQL input SMA1).
  • Medium SMA Length – Period for the medium SMA (MQL input SMA2).
  • Slow SMA Length – Period for the slow SMA (MQL input SMA3).
  • SMA Spread Steps – Additional filter requiring SMAs to diverge by a number of price steps (MQL input SMAspread).
  • Trade Volume – Order volume used when opening positions (MQL input lots).

Notes

  • Stop loss handling from the MQL version is omitted because it was disabled in the source script.
  • All exits are market orders to align with the straightforward behaviour of the original expert.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Triple SMA crossover strategy.
/// Goes long when fast > medium > slow, short when fast less than medium less than slow.
/// Exits when fast crosses medium in opposite direction.
/// </summary>
public class TripleSmaCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _mediumPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevMed;
	private bool _hasPrev;

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

	public TripleSmaCrossoverStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_mediumPeriod = Param(nameof(MediumPeriod), 10)
			.SetDisplay("Medium SMA", "Medium SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 20)
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevMed = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var medium = new SimpleMovingAverage { Length = MediumPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, medium, slow, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal med, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevMed = med;
			_hasPrev = true;
			return;
		}

		// Bullish alignment: fast > medium > slow
		if (fast > med && med > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Bearish alignment: fast < medium < slow
		else if (fast < med && med < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
		// Exit long when fast crosses below medium
		else if (Position > 0 && _prevFast >= _prevMed && fast < med)
		{
			SellMarket();
		}
		// Exit short when fast crosses above medium
		else if (Position < 0 && _prevFast <= _prevMed && fast > med)
		{
			BuyMarket();
		}

		_prevFast = fast;
		_prevMed = med;
	}
}