View on GitHub

RSI Trader Aligned Averages Strategy

Summary

This strategy reproduces the "RSI trader" MetaTrader Expert Advisor. It aligns two trend filters – price moving averages and smoothed RSI averages – to enter in the direction of the dominant trend and exits when the filters diverge (sideways regime). The StockSharp port works on any instrument with candle data support and defaults to hourly candles as in the original description.

How it works

  1. Calculate RSI with the period specified by RSI Period (default 14).
  2. Smooth the RSI stream with two simple moving averages: a short one (Short RSI MA) and a long one (Long RSI MA).
  3. Smooth closing prices with two moving averages: a short simple MA (Short Price MA) and a long linear-weighted MA (Long Price MA).
  4. Generate signals on finished candles only:
    • Long – both short averages (price and RSI) are above their long counterparts.
    • Short – both short averages are below their long counterparts.
    • Sideways – the averages disagree (one indicates uptrend and the other downtrend). When this occurs any open position is closed.
  5. Orders are issued with BuyMarket / SellMarket. Opposite positions are flattened before entering a new direction.

Parameters

Name Description Default Optimizable
RSI Period RSI calculation length. 14 Yes (7…28, step 1)
Short Price MA Length of the short simple moving average of price. 9 Yes (5…20, step 1)
Long Price MA Length of the long linear-weighted moving average of price. 45 Yes (30…90, step 5)
Short RSI MA Length of the short smoothing average applied to RSI. 9 Yes (5…20, step 1)
Long RSI MA Length of the long smoothing average applied to RSI. 45 Yes (30…90, step 5)
Candle Type Data type used for candles. Defaults to 1-hour timeframe. H1 No

Notes

  • Trading is only performed when all indicators are formed.
  • The original EA used lots and slippage settings. StockSharp uses the strategy Volume property for order size and leaves execution slippage management to the trading adapter.
  • No built-in stop-loss or take-profit is defined; exits depend on sideways detection. Additional risk management can be added externally.
  • Charts draw both price and RSI moving averages when the charting service is available.
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>
/// RSI Trader strategy combining price SMA crossover with RSI trend confirmation.
/// Buy when short SMA crosses above long SMA with RSI above 50.
/// Sell when short SMA crosses below long SMA with RSI below 50.
/// </summary>
public class RsiTraderAlignedAveragesStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _shortMaPeriod;
	private readonly StrategyParam<int> _longMaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevShort;
	private decimal _prevLong;
	private bool _hasPrev;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int ShortMaPeriod { get => _shortMaPeriod.Value; set => _shortMaPeriod.Value = value; }
	public int LongMaPeriod { get => _longMaPeriod.Value; set => _longMaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public RsiTraderAlignedAveragesStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI calculation period", "Indicators");

		_shortMaPeriod = Param(nameof(ShortMaPeriod), 9)
			.SetDisplay("Short MA", "Short moving average period", "Indicators");

		_longMaPeriod = Param(nameof(LongMaPeriod), 26)
			.SetDisplay("Long MA", "Long moving average 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();

		_prevShort = 0m;
		_prevLong = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var shortMa = new SimpleMovingAverage { Length = ShortMaPeriod };
		var longMa = new SimpleMovingAverage { Length = LongMaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, shortMa, longMa, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevShort = shortMa;
			_prevLong = longMa;
			_hasPrev = true;
			return;
		}

		var bullCross = _prevShort <= _prevLong && shortMa > longMa;
		var bearCross = _prevShort >= _prevLong && shortMa < longMa;

		if (Position <= 0 && bullCross && rsiValue > 50)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (Position >= 0 && bearCross && rsiValue < 50)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevShort = shortMa;
		_prevLong = longMa;
	}
}