Ver en GitHub

Sidus EMA RSI Strategy

This strategy is a StockSharp port of the MetaTrader 4 expert advisor Exp_Sidus.mq4. It reproduces the original logic that combines a fast/slow EMA crossover with a 50-level RSI filter. Signals are evaluated on completed candles only and each candle can spawn at most one order, matching the timing discipline of the source robot.

Trading Logic

  • Indicator stack
    • Fast Exponential Moving Average (default period 5)
    • Slow Exponential Moving Average (default period 12)
    • Relative Strength Index (default period 21)
  • Bullish setup
    1. The fast EMA was below or equal to the slow EMA on the previous signal candle.
    2. The fast EMA is above the slow EMA on the current signal candle.
    3. RSI on the same candle is strictly greater than 50.
  • Bearish setup
    1. The fast EMA was above or equal to the slow EMA on the previous signal candle.
    2. The fast EMA is below the slow EMA on the current signal candle.
    3. RSI on the same candle is strictly smaller than 50.
  • Signal shift — the SignalShift parameter (default 1) defines which closed candle is considered the "current" signal bar. A value of 1 uses the last closed candle, 0 uses the just-closed candle, 2 looks two bars back, and so on. The previous candle for crossover detection is calculated automatically as SignalShift + 1.
  • Duplicate protection — the strategy stores the open time of the signal candle and never opens another position tied to the same bar, faithfully mimicking the LastTime check in the original EA.

Position Management

  • Only one position exists at any time.
  • When an opposite signal appears while a position is open, the strategy first closes the existing position and then waits for the next processing pass to open a trade in the new direction, exactly as the MQL version does.
  • StartProtection attaches optional take-profit and stop-loss brackets expressed in price points (price steps). Distances are derived from the inputs of the original EA: default take-profit 80 points and stop-loss 20 points.

Parameters

Name Description Default Notes
TakeProfitPoints Take-profit distance in price steps. 80 Set 0 to disable the target.
StopLossPoints Stop-loss distance in price steps. 20 Set 0 to disable protection.
TradeVolume Order volume (lots/contracts). 0.1 Assigned to the base Volume property on start.
FastPeriod Fast EMA length. 5 Optimizable.
SlowPeriod Slow EMA length. 12 Optimizable.
RsiPeriod RSI length. 21 Optimizable.
SignalShift Number of closed candles used for signal calculations. 1 Mirrors the shif input of the MT4 EA.
CandleType Candle source for the subscription. 1h time frame Can be set to any DataType supported by the environment.

Implementation Notes

  • Candle data is subscribed via SubscribeCandles(CandleType) and processed inside ProcessCandle only after the candle reaches the Finished state.
  • Indicator values are cached in a short queue so the strategy can access the current and previous bars specified by SignalShift without calling indicator methods like GetValue, complying with the repository guidelines.
  • Trade execution uses BuyMarket/SellMarket once the strategy is flat; when a position in the opposite direction exists, ClosePosition is issued first, keeping order flow identical to the original robot.
  • All runtime logs are written in English to maintain a clear audit trail.

Conversion Notes

  • The take-profit and stop-loss distances multiply the instrument PriceStep, replicating the MetaTrader Point behaviour.
  • Volume defaults to 0.1, the same as the Lots input in the MQL source.
  • RSI thresholds are hard-coded at 50 to mirror the original implementation.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Sidus EMA + RSI strategy: fast EMA crosses slow EMA confirmed by RSI above/below 50.
/// </summary>
public class SidusEmaRsiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _rsiPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;

	public SidusEmaRsiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 12)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 21)
			.SetDisplay("RSI Period", "RSI period.", "Indicators");
	}

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, rsi, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			return;
		}

		var bullishCross = _prevFast <= _prevSlow && fastValue > slowValue;
		var bearishCross = _prevFast >= _prevSlow && fastValue < slowValue;

		// Exit existing positions on opposite cross
		if (Position > 0 && bearishCross)
		{
			SellMarket();
		}
		else if (Position < 0 && bullishCross)
		{
			BuyMarket();
		}

		// Entry on crossover confirmed by RSI
		if (Position == 0)
		{
			if (bullishCross && rsiValue > 50)
			{
				BuyMarket();
			}
			else if (bearishCross && rsiValue < 50)
			{
				SellMarket();
			}
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}