Открыть на GitHub

Стратегия RSI MA on RSI Filling Step

Обзор

RSI MA on RSI Filling Step — порт на StockSharp советника MetaTrader RSI_MAonRSI_Filling Step EA.mq5. В оригинале импульс измеряется индикатором RSI, а затем дополнительно сглаживается скользящей средней. Сделки открываются в момент пересечения RSI и его средней, если оба значения находятся по одну сторону от середины (уровня 50). При конверсии сохранены гибкая фильтрация направлений торговли, опция реверса сигналов и временное ограничение сессии, при этом вся математика реализована через высокоуровневый API StockSharp.

Логика торговли

  1. Подписка на выбранный тип свечей и расчёт двух индикаторов на каждой завершённой свече: RelativeStrengthIndex с периодом RsiPeriod и MovingAverage (MaType, MaPeriod), применённой к последовательности значений RSI.
  2. Обработка только закрытых свечей, что повторяет защиту оригинального советника от многократных входов внутри бара.
  3. Покупка формируется, если предыдущий RSI был ниже своей скользящей средней, а текущее значение закрылось выше средней, причём оба индикатора находятся ниже MiddleLevel (по умолчанию 50). Продажа — зеркальная ситуация выше середины.
  4. Параметр ReverseSignals меняет направление интерпретации: бычий сигнал открывает шорт, медвежий — лонг.
  5. Параметр Mode ограничивает торговлю только длинными, только короткими или обоими направлениями. Дополнительно можно закрывать противоположные позиции перед входом и запрещать новые ордера при уже открытой позиции.
  6. Временной фильтр (UseTimeWindow, SessionStart, SessionEnd) воспроизводит функцию TimeControlHourMinute из MQL, включая сценарии с переходом через полночь.

Параметры

  • CandleType — тип свечей, используемых в расчётах (по умолчанию часовые).
  • RsiPeriod — период RSI (по умолчанию 14).
  • MaPeriod — длина скользящей средней по RSI (по умолчанию 21).
  • MaType — тип скользящей средней (по умолчанию Simple).
  • MiddleLevel — центральный уровень RSI, подтверждающий сигналы (по умолчанию 50).
  • ReverseSignals — инвертировать логику входов (по умолчанию false).
  • Mode — режим торговли (BuyOnly, SellOnly, Both).
  • CloseOppositePositions — закрывать встречные позиции перед входом (по умолчанию false).
  • OnlyOnePosition — не открывать новые сделки при наличии позиции (по умолчанию false).
  • UseTimeWindow — включить фильтр торговой сессии (по умолчанию false).
  • SessionStart / SessionEnd — время начала и окончания разрешённой сессии.

Особенности реализации

  • Индикаторы подключаются через метод Bind, что избавляет от ручного копирования буферов, как в MQL (CopyBuffer).
  • Предыдущие значения RSI и его средней кешируются, чтобы воспроизвести обращение RSI[m_bar_current+1] из оригинала. Поле _lastSignalBarTime гарантирует только одно решение на бар, аналогично m_last_deal_buy_in и m_last_deal_sell_in.
  • Для входов используются BuyMarket() и SellMarket(), что повторяет рыночное исполнение советника. При активной опции закрытия противоположных позиций вызывается ClosePosition() до подачи нового ордера.
  • Временное окно полностью копирует логику TimeControlHourMinute, включая ночные сессии, когда время начала больше времени окончания.
  • В визуализации добавляется область с RSI и его средней, что облегчает проверку пересечений в тестах.

Отличия от эксперта MetaTrader

  • Не перенесены варианты управления объёмом (ENUM_LOT_OR_RISK), трейлинг и проверки на Freeze Level. В StockSharp их можно реализовать отдельными модулями защиты.
  • Очередь заявок, проверка магического номера и обработка транзакций из оригинала не требуются: жизненный цикл ордеров контролируется самим движком стратегий.
  • Stop Loss и Take Profit не устанавливаются автоматически — при необходимости используйте StartProtection или внешние надстройки.

Рекомендации по использованию

  1. Держите MiddleLevel около 50, чтобы сохранить исходный характер торговли от границ диапазона. Смещение уровня изменит стратегию в сторону пробоев.
  2. Включайте OnlyOnePosition, если нужно строгое чередование «flat → позиция». Отключите параметр для пирамидинга.
  3. Используйте временное окно вместе с биржевым расписанием, чтобы исключить ночные периоды без ликвидности.
  4. Подбирайте RsiPeriod, MaPeriod и MiddleLevel совместно при адаптации к другим инструментам.

Документация описывает ключевые особенности и поможет быстро внедрить стратегию в инфраструктуру StockSharp.

namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

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

/// <summary>
/// RSI crossing its own moving average with trade signals.
/// Converted from the MetaTrader expert advisor "RSI_MAonRSI_Filling Step EA.mq5".
/// </summary>
public class RsiMaOnRsiFillingStepStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _maPeriod;

	private RelativeStrengthIndex _rsi;
	private readonly Queue<decimal> _rsiHistory = new();
	private decimal? _previousRsi;
	private decimal? _previousSignal;

	/// <summary>
	/// Initializes a new instance of <see cref="RsiMaOnRsiFillingStepStrategy"/>.
	/// </summary>
	public RsiMaOnRsiFillingStepStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("Candle Type", "Timeframe used for RSI calculations.", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
		.SetGreaterThanZero()
		.SetDisplay("RSI Period", "Number of bars for the RSI smoothing window.", "Indicators");

		_maPeriod = Param(nameof(MaPeriod), 20)
		.SetGreaterThanZero()
		.SetDisplay("MA Period", "Length of the moving average applied to the RSI.", "Indicators");
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// RSI averaging period.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// Moving average period applied to the RSI output.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousRsi = null;
		_previousSignal = null;
		_rsiHistory.Clear();
		_rsi = null!;
	}

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

		_previousRsi = null;
		_previousSignal = null;
		_rsiHistory.Clear();

		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

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

		// Accumulate RSI values for moving average calculation
		_rsiHistory.Enqueue(rsiValue);
		while (_rsiHistory.Count > MaPeriod)
			_rsiHistory.Dequeue();

		if (!_rsi.IsFormed)
			return;

		// Need enough RSI values to compute the MA
		if (_rsiHistory.Count < MaPeriod)
		{
			_previousRsi = rsiValue;
			return;
		}

		// Calculate simple moving average of RSI
		var sum = 0m;
		var history = _rsiHistory.ToArray();
		foreach (var v in history)
			sum += v;
		var signalValue = sum / MaPeriod;

		if (_previousRsi is null || _previousSignal is null)
		{
			_previousRsi = rsiValue;
			_previousSignal = signalValue;
			return;
		}

		var crossUp = _previousRsi < _previousSignal && rsiValue > signalValue;
		var crossDown = _previousRsi > _previousSignal && rsiValue < signalValue;

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

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

		_previousRsi = rsiValue;
		_previousSignal = signalValue;
	}
}