Basic RSI EA Template Strategy — конвертация советника MetaTrader 4 «Basic Rsi EA Template.mq4» (MQL/26750). Стратегия отслеживает индекс относительной силы (RSI) на выбранной серии свечей и реагирует, когда импульс достигает настраиваемых зон перекупленности или перепроданности. В StockSharp сохранены простая работа с одной позицией и защитные стоп/тейк уровни оригинального робота, при этом используется высокоуровневый API подписки.
Логика стратегии
Индикаторы
RSI с настраиваемым периодом, рассчитанный по выбранному типу свечей.
Условия входа
Покупка: RSI падает ниже OversoldLevel, открытых позиций нет — отправляется рыночная покупка объемом OrderVolume.
Продажа: RSI поднимается выше OverboughtLevel, открытых позиций нет — отправляется рыночная продажа объемом OrderVolume.
Алгоритм работает в неттинговом режиме: одновременно может существовать только одна позиция. Если активна длинная позиция, стратегия ждет её закрытия перед входом в шорт (и наоборот).
Условия выхода
Стоп-лосс: значение StopLossPips переводится в абсолютное расстояние по цене с учетом шага тикования инструмента. При откате цены на эту величину встроенная защита закрывает позицию.
Тейк-профит: TakeProfitPips обрабатывается аналогично — при движении цены в прибыль на указанное расстояние позиция закрывается.
Дополнительных трейлинг-стопов или выходов по сигналу нет. Стратегия полагается только на защитные расстояния или ручное вмешательство, повторяя минимализм исходного шаблона.
Риск и объем
OrderVolume задает фиксированный объем каждой рыночной сделки (по умолчанию 0.01 лота, как в MQL-примере).
Стратегия не наращивает и не хеджирует позиции. После закрытия стопом или тейком она возвращается в ноль и ждет следующего сигнала RSI.
Параметры
CandleType: серия свечей для генерации сигналов (по умолчанию минутный таймфрейм).
RsiPeriod: количество баров в расчете RSI (по умолчанию 14).
OverboughtLevel: порог RSI для разрешения коротких позиций (по умолчанию 70).
OversoldLevel: порог RSI для разрешения длинных позиций (по умолчанию 30).
StopLossPips: расстояние стоп-лосса в пунктах, пересчитанное в абсолютное значение (по умолчанию 30 пунктов).
TakeProfitPips: расстояние тейк-профита в пунктах, пересчитанное в абсолютное значение (по умолчанию 20 пунктов).
OrderVolume: фиксированный объем рыночных ордеров (по умолчанию 0.01).
Особенности реализации
Используется цепочка SubscribeCandles(...).Bind(rsi, ProcessCandle), чтобы значения индикатора сразу попадали в обработчик без ручного буфера.
Метод CreateProtectionUnit воспроизводит логику MQL по пересчету пунктов: для инструментов с 3 или 5 знаками после запятой применяется множитель 10.
Проверки сигнала выполняются только на закрытых свечах, чтобы избежать повторных ордеров на одном баре.
Конверсия рассчитана на неттинговый учет, в отличие от хеджевого режима MetaTrader. Поэтому противоположные сделки закрывают текущую позицию вместо открытия новой.
Все комментарии и сообщения в коде написаны на английском языке для удобства сопровождения.
Рекомендации по использованию
Подберите CandleType под инструмент и горизонт торговли (например, переключитесь на часовые свечи для свинг-подхода).
Настройте StopLossPips и TakeProfitPips под волатильность инструмента — защитные расстояния критичны для управления риском.
При необходимости расширенного мани-менеджмента подключите портфельные или рисковые модули StockSharp поверх данного шаблона.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Basic RSI template: buys when RSI is oversold, sells when RSI is overbought.
/// </summary>
public class BasicRsiEaTemplateStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<decimal> _oversoldLevel;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
public decimal OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
public BasicRsiEaTemplateStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "Indicators");
_overboughtLevel = Param(nameof(OverboughtLevel), 70m)
.SetDisplay("Overbought Level", "RSI overbought threshold", "Indicators");
_oversoldLevel = Param(nameof(OversoldLevel), 30m)
.SetDisplay("Oversold Level", "RSI oversold threshold", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
decimal? prevRsi = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, (candle, rsiValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevRsi.HasValue)
{
var crossBelowOversold = prevRsi.Value >= OversoldLevel && rsiValue < OversoldLevel;
var crossAboveOverbought = prevRsi.Value <= OverboughtLevel && rsiValue > OverboughtLevel;
if (crossBelowOversold && Position <= 0)
BuyMarket();
else if (crossAboveOverbought && Position >= 0)
SellMarket();
}
prevRsi = rsiValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
}
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 basic_rsi_ea_template_strategy(Strategy):
def __init__(self):
super(basic_rsi_ea_template_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._overbought_level = self.Param("OverboughtLevel", 70.0) \
.SetDisplay("Overbought Level", "RSI overbought threshold", "Indicators")
self._oversold_level = self.Param("OversoldLevel", 30.0) \
.SetDisplay("Oversold Level", "RSI oversold threshold", "Indicators")
self._rsi = None
self._prev_rsi = None
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def overbought_level(self):
return self._overbought_level.Value
@property
def oversold_level(self):
return self._oversold_level.Value
def OnReseted(self):
super(basic_rsi_ea_template_strategy, self).OnReseted()
self._rsi = None
self._prev_rsi = None
def OnStarted2(self, time):
super(basic_rsi_ea_template_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.Bind(self._rsi, self._process_candle)
subscription.Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._rsi.IsFormed:
return
rsi = float(rsi_value)
if self._prev_rsi is not None:
cross_below_oversold = self._prev_rsi >= self.oversold_level and rsi < self.oversold_level
cross_above_overbought = self._prev_rsi <= self.overbought_level and rsi > self.overbought_level
if cross_below_oversold and self.Position <= 0:
self.BuyMarket()
elif cross_above_overbought and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi
def CreateClone(self):
return basic_rsi_ea_template_strategy()