GitHub で見る

RSI MA on RSI Dual Strategy

Overview

The RSI MA on RSI Dual strategy recreates the MetaTrader expert advisor "RSI_MAonRSI_Dual" inside StockSharp. It watches two relative strength indexes with different lookback periods and applies a common moving average on top of each RSI stream. Trading decisions are taken when the smoothed RSI lines cross one another while remaining on the same side of a configurable neutral level.

The conversion keeps the behaviour of the original robot, including time filtering and the ability to restrict trading direction or to reverse the signal logic.

Indicators

  • Fast RSI – Relative Strength Index with configurable period.
  • Slow RSI – Relative Strength Index with its own period.
  • Moving average on RSI – Simple moving average calculated on top of each RSI value stream. Both RSIs use the same smoothing length.

All three indicators share the same applied price (close price by default). The two smoothed RSI lines are drawn on a dedicated chart panel for monitoring.

Entry rules

  1. Wait for both smoothed RSI values to form on the current completed bar.
  2. Long setup
    • The fast smoothed RSI crosses above the slow smoothed RSI (current value above, previous value below).
    • Both smoothed RSIs are below the neutral level (50 by default).
  3. Short setup
    • The fast smoothed RSI crosses below the slow smoothed RSI (current value below, previous value above).
    • Both smoothed RSIs are above the neutral level.
  4. Optionally reverse the signal directions using the ReverseSignals parameter.
  5. Signals generated on the same bar are ignored (one entry per bar).

Position management

  • AllowLong and AllowShort control whether the strategy may open positions in each direction.
  • CloseOpposite closes an existing position before entering the opposite side, replicating the original EA logic.
  • OnlyOnePosition forbids opening a new position when any position is already active.
  • Market orders are issued with the strategy Volume.

Time filter

Enable or disable the trading session filter with UseTimeFilter. When enabled, trades are allowed only between SessionStart and SessionEnd. Sessions that cross midnight are supported. The timestamps are evaluated in the exchange time zone provided by the incoming candle messages.

Parameters

Name Description
CandleType Candle series analysed by the strategy.
FastRsiPeriod Period of the fast RSI.
SlowRsiPeriod Period of the slow RSI.
MaPeriod Moving-average length used to smooth both RSI streams.
AppliedPrice Price type forwarded into the RSI calculations.
NeutralLevel RSI threshold that separates bullish and bearish zones.
AllowLong / AllowShort Enable or disable trading direction.
ReverseSignals Swap long and short signals.
CloseOpposite Close the opposite position before entering a new one.
OnlyOnePosition Permit at most one open position.
UseTimeFilter Activate the trading session filter.
SessionStart / SessionEnd Trading window boundaries.

Differences from the original EA

  • Money management, stop-loss and trailing-stop blocks of the original MQL5 code are not reproduced. The StockSharp strategy places market orders using the fixed Volume configured on the strategy.
  • All logging and diagnostic alerts were removed; StockSharp logging should be used instead if required.
  • Platform-specific transaction tracking is replaced with StockSharp order-state events.

Despite these differences, the core entry logic and directional filters match the source expert advisor.

using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Dual moving averages calculated on top of RSI values.
/// Fast RSI MA crossing slow RSI MA generates entry signals.
/// </summary>
public class RsiMaOnRsiDualStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastRsiPeriod;
	private readonly StrategyParam<int> _slowRsiPeriod;
	private readonly StrategyParam<int> _maPeriod;

	private RelativeStrengthIndex _fastRsi;
	private RelativeStrengthIndex _slowRsi;
	private readonly Queue<decimal> _fastRsiHistory = new();
	private readonly Queue<decimal> _slowRsiHistory = new();

	private decimal? _previousFastMa;
	private decimal? _previousSlowMa;

	public RsiMaOnRsiDualStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle type", "Candles processed by the strategy.", "General");

		_fastRsiPeriod = Param(nameof(FastRsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Fast RSI period", "Length of the fast RSI smoothing window.", "Indicators");

		_slowRsiPeriod = Param(nameof(SlowRsiPeriod), 28)
			.SetGreaterThanZero()
			.SetDisplay("Slow RSI period", "Length of the slow RSI smoothing window.", "Indicators");

		_maPeriod = Param(nameof(MaPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("MA period", "Number of RSI values averaged by the smoothing moving average.", "Indicators");
	}

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

	public int FastRsiPeriod
	{
		get => _fastRsiPeriod.Value;
		set => _fastRsiPeriod.Value = value;
	}

	public int SlowRsiPeriod
	{
		get => _slowRsiPeriod.Value;
		set => _slowRsiPeriod.Value = value;
	}

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousFastMa = null;
		_previousSlowMa = null;
		_fastRsiHistory.Clear();
		_slowRsiHistory.Clear();
		_fastRsi = null!;
		_slowRsi = null!;
	}

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

		_previousFastMa = null;
		_previousSlowMa = null;
		_fastRsiHistory.Clear();
		_slowRsiHistory.Clear();

		_fastRsi = new RelativeStrengthIndex { Length = FastRsiPeriod };
		_slowRsi = new RelativeStrengthIndex { Length = SlowRsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastRsi, _slowRsi, ProcessCandle)
			.Start();

		var priceArea = CreateChartArea();
		if (priceArea != null)
		{
			DrawCandles(priceArea, subscription);
			DrawOwnTrades(priceArea);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastRsiValue, decimal slowRsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_fastRsiHistory.Enqueue(fastRsiValue);
		_slowRsiHistory.Enqueue(slowRsiValue);
		while (_fastRsiHistory.Count > MaPeriod)
			_fastRsiHistory.Dequeue();
		while (_slowRsiHistory.Count > MaPeriod)
			_slowRsiHistory.Dequeue();

		if (!_fastRsi.IsFormed || !_slowRsi.IsFormed)
			return;

		if (_fastRsiHistory.Count < MaPeriod || _slowRsiHistory.Count < MaPeriod)
			return;

		// Calculate SMA of each RSI
		var fastSum = 0m;
		var fastHistory = _fastRsiHistory.ToArray();
		foreach (var v in fastHistory)
			fastSum += v;
		var fastMa = fastSum / MaPeriod;

		var slowSum = 0m;
		var slowHistory = _slowRsiHistory.ToArray();
		foreach (var v in slowHistory)
			slowSum += v;
		var slowMa = slowSum / MaPeriod;

		if (_previousFastMa is null || _previousSlowMa is null)
		{
			_previousFastMa = fastMa;
			_previousSlowMa = slowMa;
			return;
		}

		var crossUp = _previousFastMa < _previousSlowMa && fastMa > slowMa;
		var crossDown = _previousFastMa > _previousSlowMa && fastMa < slowMa;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		if (crossUp)
		{
			if (Position <= 0)
				BuyMarket(volume);
		}
		else if (crossDown)
		{
			if (Position >= 0)
				SellMarket(volume);
		}

		_previousFastMa = fastMa;
		_previousSlowMa = slowMa;
	}
}