Ver en GitHub

STO M5xM15xM30 Strategy

Overview

This strategy is a faithful C# conversion of the MetaTrader 4 expert advisor "STO_m5xm15xm30". It uses three stochastic oscillators computed on M5, M15, and M30 timeframes to identify synchronized momentum shifts. The StockSharp implementation keeps the original entry/exit structure, replaces manual order management with the high-level API, and exposes every key constant as a configurable StrategyParam.

Trading Logic

  1. Multi-timeframe confirmation
    • The primary (default M5) stochastic must show a bullish crossover (%K crosses above %D).
    • The middle (default M15) and slow (default M30) stochastic values must already be bullish (%K above %D).
    • A bearish setup requires the mirrored conditions (%K below %D).
  2. Shift filter
    • The primary stochastic also checks the state ShiftBars candles earlier. A buy signal requires the historical %K to be below %D, ensuring a fresh crossover. Sell signals require the opposite.
  3. Price momentum filter
    • The latest close must be higher (for buys) or lower (for sells) than the previous completed candle close. This mirrors the Close[0] > Close[1] rule from the MT4 script.
  4. Entry rules
    • If no position is open and the bullish criteria are met, the strategy opens a long market order with the configured TradeVolume.
    • If a short position exists when a bullish signal arrives, it is flattened first and a long position is opened afterwards. The inverse is true for bearish signals.
  5. Exit rules
    • A dedicated M5 stochastic with period ExitKPeriod checks the previous candle (shift = 1). A long position is closed when %K falls below %D; a short is closed when %K rises above %D.
    • After an exit is triggered, the strategy skips immediate re-entry on the same candle, replicating the MT4 order loop behaviour.

Indicators and Data Subscriptions

  • Primary candles: default 5-minute timeframe (CandleType).
  • Middle confirmation candles: default 15-minute timeframe (MiddleCandleType).
  • Slow confirmation candles: default 30-minute timeframe (SlowCandleType).
  • Stochastic oscillators: all use %K smoothing = 3 and %D smoothing = 3, matching the original parameters.

Parameters

Name Default Description
CandleType 5-minute candles Working timeframe for entries and exits.
MiddleCandleType 15-minute candles Confirmation timeframe #1.
SlowCandleType 30-minute candles Confirmation timeframe #2.
FastKPeriod 5 %K period for the primary stochastic.
MiddleKPeriod 5 %K period for the middle stochastic.
SlowKPeriod 5 %K period for the slow stochastic.
ExitKPeriod 5 %K period for the exit stochastic that operates on the previous bar.
ShiftBars 3 Number of bars between the reference crossover and the current bar.
TakeProfitPoints 30 Protective take-profit distance (points).
StopLossPoints 10 Protective stop-loss distance (points).
TradeVolume 0.1 Order volume used for new entries.

All parameters are exposed through StrategyParam<T>, making them available for optimization inside StockSharp Designer.

Risk Management

StartProtection() translates the MT4 TP and SL inputs into StockSharp protective orders. Both can be disabled by setting the corresponding parameter to zero.

Implementation Notes

  • Indicator values are obtained exclusively through SubscribeCandles(...).BindEx(...), complying with the high-level API guidelines and avoiding manual indicator collections.
  • The StochasticShiftBuffer helper mimics the MT4 shift argument without calling GetValue, keeping only the necessary bar history.
  • Entry processing happens once per completed candle. Exit evaluation occurs before entry logic, matching the original EA's processing order.
  • Inline comments explain each processing step and clarify how the MQL logic maps to the StockSharp code.

Usage

  1. Add the strategy to a StockSharp scheme or designer project.
  2. Configure the desired symbol and ensure historical data for M5, M15, and M30 candles is available.
  3. Adjust the parameters to suit the target market or optimization scenario.
  4. Start the strategy; protective stop-loss/take-profit levels are registered automatically for every position.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// STO M5xM15xM30: RSI momentum with dual EMA confirmation.
/// Uses RSI crossover of 50 level with fast/slow EMA alignment.
/// </summary>
public class StoM5xM15xM30Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public StoM5xM15xM30Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevRsi = 0;
		_entryPrice = 0;
	}

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

		_prevRsi = 0;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, fast, slow, atr, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevRsi == 0 || atrVal <= 0)
		{
			_prevRsi = rsiVal;
			return;
		}

		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (rsiVal < 40 && fastVal < slowVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (rsiVal > 60 && fastVal > slowVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: RSI crosses 50 with EMA alignment
		if (Position == 0)
		{
			if (_prevRsi <= 50 && rsiVal > 50 && fastVal > slowVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevRsi >= 50 && rsiVal < 50 && fastVal < slowVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
	}
}