Стратегия RSI MA on RSI Dual переносит эксперта MetaTrader "RSI_MAonRSI_Dual" в экосистему StockSharp. Используются две линии RSI с разными периодами, для каждой из которых рассчитывается простое скользящее среднее. Торговые решения принимаются, когда сглаженные линии RSI пересекаются, находясь при этом по одну сторону от заданного нейтрального уровня.
Реализация сохраняет ключевую логику оригинального робота и добавляет параметры для ограничения времени торговли, выбора направления и инверсии сигналов.
Индикаторы
Быстрый RSI — RSI с настраиваемым периодом.
Медленный RSI — RSI с собственным периодом.
Скользящее среднее по RSI — простое скользящее среднее, вычисляемое поверх каждой серии значений RSI.
Все индикаторы используют один и тот же тип цены (по умолчанию закрытие). Сглаженные линии RSI выводятся на отдельную область графика.
Правила входа
Дождитесь формирования обеих сглаженных линий RSI на завершившейся свече.
Покупка
Быстрая линия пересекает медленную снизу вверх (предыдущее значение ниже, текущее выше).
Обе сглаженные линии находятся ниже нейтрального уровня (по умолчанию 50).
Продажа
Быстрая линия пересекает медленную сверху вниз (предыдущее значение выше, текущее ниже).
Обе сглаженные линии находятся выше нейтрального уровня.
Параметр ReverseSignals позволяет поменять направления сигналов местами.
На одной свече открывается не более одной позиции — повторные сигналы игнорируются.
Управление позицией
AllowLong и AllowShort включают или отключают торговлю в соответствующем направлении.
CloseOpposite закрывает противоположную позицию перед разворотом.
OnlyOnePosition запрещает открывать новую сделку при наличии любой позиции.
Рыночные заявки отправляются с объёмом, указанным в параметре Volume стратегии.
Временной фильтр
Параметр UseTimeFilter включает фильтрацию по торговой сессии. При включенном фильтре сделки допускаются только между SessionStart и SessionEnd. Поддерживаются интервалы, пересекающие полуночь. Время берётся из таймстемпов свечей в часовом поясе торговой площадки.
Параметры
Параметр
Описание
CandleType
Тип свечей, используемых в анализе.
FastRsiPeriod
Период быстрого RSI.
SlowRsiPeriod
Период медленного RSI.
MaPeriod
Длина сглаживающего скользящего среднего.
AppliedPrice
Тип цены, подаваемой на расчёт RSI.
NeutralLevel
Нейтральный уровень, разделяющий бычью и медвежью зоны.
AllowLong / AllowShort
Разрешение торговли в длинную или короткую сторону.
ReverseSignals
Инверсия направлений сигналов.
CloseOpposite
Закрывать противоположную позицию перед входом.
OnlyOnePosition
Ограничение на одну открытую позицию.
UseTimeFilter
Включение фильтра по времени.
SessionStart / SessionEnd
Границы торгового окна.
Отличия от оригинального эксперта
Блоки управления капиталом, стоп-лоссы и трейлинг-стопы из версии MQL5 не перенесены. В StockSharp стратегия выставляет рыночные приказы фиксированным объёмом.
Удалены выводы в журнал и всплывающие сообщения — для диагностики следует использовать стандартные механизмы StockSharp.
Отслеживание сделок реализовано через событийную модель заявок StockSharp.
Несмотря на перечисленные отличия, логика генерации сигналов и фильтрация направлений полностью соответствуют исходному эксперту.
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;
}
}