RSI MA on RSI Filling Step — порт на StockSharp советника MetaTrader RSI_MAonRSI_Filling Step EA.mq5. В оригинале импульс измеряется индикатором RSI, а затем дополнительно сглаживается скользящей средней. Сделки открываются в момент пересечения RSI и его средней, если оба значения находятся по одну сторону от середины (уровня 50). При конверсии сохранены гибкая фильтрация направлений торговли, опция реверса сигналов и временное ограничение сессии, при этом вся математика реализована через высокоуровневый API StockSharp.
Логика торговли
Подписка на выбранный тип свечей и расчёт двух индикаторов на каждой завершённой свече: RelativeStrengthIndex с периодом RsiPeriod и MovingAverage (MaType, MaPeriod), применённой к последовательности значений RSI.
Обработка только закрытых свечей, что повторяет защиту оригинального советника от многократных входов внутри бара.
Покупка формируется, если предыдущий RSI был ниже своей скользящей средней, а текущее значение закрылось выше средней, причём оба индикатора находятся ниже MiddleLevel (по умолчанию 50). Продажа — зеркальная ситуация выше середины.
Параметр Mode ограничивает торговлю только длинными, только короткими или обоими направлениями. Дополнительно можно закрывать противоположные позиции перед входом и запрещать новые ордера при уже открытой позиции.
Временной фильтр (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 или внешние надстройки.
Рекомендации по использованию
Держите MiddleLevel около 50, чтобы сохранить исходный характер торговли от границ диапазона. Смещение уровня изменит стратегию в сторону пробоев.
Включайте OnlyOnePosition, если нужно строгое чередование «flat → позиция». Отключите параметр для пирамидинга.
Используйте временное окно вместе с биржевым расписанием, чтобы исключить ночные периоды без ликвидности.
Подбирайте 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_ma_on_rsi_filling_step_strategy(Strategy):
def __init__(self):
super(rsi_ma_on_rsi_filling_step_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._ma_period = self.Param("MaPeriod", 20)
self._rsi_history = []
self._prev_rsi = 0.0
self._prev_signal = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
def OnReseted(self):
super(rsi_ma_on_rsi_filling_step_strategy, self).OnReseted()
self._rsi_history = []
self._prev_rsi = 0.0
self._prev_signal = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(rsi_ma_on_rsi_filling_step_strategy, self).OnStarted2(time)
self._rsi_history = []
self._prev_rsi = 0.0
self._prev_signal = 0.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
# Accumulate RSI values for moving average
self._rsi_history.append(rsi_val)
ma_len = self.MaPeriod
while len(self._rsi_history) > ma_len:
self._rsi_history.pop(0)
# Need enough RSI values to compute the MA
if len(self._rsi_history) < ma_len:
self._prev_rsi = rsi_val
return
# Calculate SMA of RSI
signal_val = sum(self._rsi_history) / ma_len
if self._has_prev:
cross_up = self._prev_rsi < self._prev_signal and rsi_val > signal_val
cross_down = self._prev_rsi > self._prev_signal and rsi_val < signal_val
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi_val
self._prev_signal = signal_val
self._has_prev = True
def CreateClone(self):
return rsi_ma_on_rsi_filling_step_strategy()