Стратегия RRS Impulse
RRS Impulse — это перенос эксперта MetaTrader «RRS Impulse» на высокоуровневый API StockSharp. В оригинале робот сочетал
фильтры RSI, стохастика и полос Боллинджера, переключал режимы силы сигналов и использовал виртуальные стопы с трейлингом. В
версии на C# сохранена та же логика, но реализация целиком построена на высокоуровневых вызовах: свечные подписки питают
индикаторы, а заявки выставляются методами BuyMarket, SellMarket и ClosePosition.
Торговая логика
- Режимы индикаторов — четыре варианта поведения:
Rsi: торгуем, когда RSI выходит из зоны перекупленности/перепроданности.
Stochastic: одновременно требуются уровни %K и %D выше/ниже заданных порогов.
BollingerBands: реакция на закрытие свечи выше верхней или ниже нижней полосы.
RsiStochasticBollinger: сигнал возникает только при единогласном подтверждении всех трёх фильтров.
- Направление торговли —
Trend следует за показаниями индикатора (перекупленность → продажи), а CounterTrend
отыгрывает разворот (перекупленность → покупки).
- Сила сигнала — определяет, сколько таймфреймов должны совпасть:
SingleTimeFrame: используется только базовый таймфрейм CandleType.
MultiTimeFrame: требуется подтверждение на M1, M5, M15, M30, H1 и H4.
Strong: концентрируется на внутридневном импульсе (M1, M5, M15, M30).
VeryStrong: использует всю лестницу M1…H4. При комбинированном режиме индикаторов каждый таймфрейм должен выполнить
условия всех трёх фильтров.
- Риск-менеджмент — для каждой позиции отслеживаются три виртуальных условия выхода:
- фиксированный стоп-лосс в пунктах;
- фиксированный тейк-профит в пунктах;
- трейлинг-стоп, активируемый после достижения
TrailingStartPips и сопровождаемый отступом TrailingGapPips.
При смене направления стратегия сначала вызывает ClosePosition(), чтобы закрыть текущую позицию, и только на следующей
проверке при наличии подтверждения открывает противоположную сделку.
Параметры
| Группа |
Параметр |
Описание |
| Data |
CandleType |
Базовая свечная серия для расчётов. |
| Orders |
TradeVolume |
Объём рыночных заявок. |
| Risk |
StopLossPips, TakeProfitPips, TrailingStartPips, TrailingGapPips |
Виртуальные защитные расстояния в пунктах. |
| Signals |
IndicatorMode, TradeDirection, SignalStrength |
Переключатели поведения, повторяющие входные параметры MQL. |
| RSI |
RsiPeriod, RsiUpperLevel, RsiLowerLevel |
Настройки RSI для выявления перекупленности/перепроданности. |
| Stochastic |
StochasticKPeriod, StochasticDPeriod, StochasticSlowing, StochasticUpperLevel, StochasticLowerLevel |
Параметры медленного стохастика. |
| Bollinger |
BollingerPeriod, BollingerDeviation |
Длина расчёта и множитель отклонения полос Боллинджера. |
Диапазоны оптимизации сохранены там, где они присутствовали в оригинальном советнике (стопы, тейки, уровни осцилляторов).
Требования к данным
Стратегии нужны минутные свечи для лестницы подтверждений. Если выбран более строгий режим SignalStrength, нужные таймфреймы
подписываются автоматически (метод GetWorkingSecurities сообщает о них движку). Потоки Level1 не используются: решения
принимаются только по закрытым свечам, что полностью повторяет «виртуальные» стопы и тейки MQL-версии.
Особенности портирования
- Случайный перебор символов из MetaTrader убран: стратегии StockSharp работают с одним
Security, поэтому акцент сделан на
точном повторении логики фильтров и выходов, а выбор инструментов остаётся за пользователем.
- Управление позициями реализовано через рыночные команды — при смене направления или срабатывании защитного условия вызывается
ClosePosition(), что соответствует циклам OrderClose в исходном коде.
- Все комментарии в коде написаны на английском языке, а отступы выполнены табуляцией в соответствии с требованиями репозитория.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// RRS Impulse strategy.
/// Combines RSI, Stochastic and Bollinger Bands for counter-trend entries.
/// </summary>
public class RrsImpulseStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiUpperLevel;
private readonly StrategyParam<decimal> _rsiLowerLevel;
private readonly StrategyParam<int> _stochasticKPeriod;
private readonly StrategyParam<int> _stochasticDPeriod;
private readonly StrategyParam<decimal> _stochasticUpperLevel;
private readonly StrategyParam<decimal> _stochasticLowerLevel;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiUpperLevel { get => _rsiUpperLevel.Value; set => _rsiUpperLevel.Value = value; }
public decimal RsiLowerLevel { get => _rsiLowerLevel.Value; set => _rsiLowerLevel.Value = value; }
public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
public decimal StochasticUpperLevel { get => _stochasticUpperLevel.Value; set => _stochasticUpperLevel.Value = value; }
public decimal StochasticLowerLevel { get => _stochasticLowerLevel.Value; set => _stochasticLowerLevel.Value = value; }
public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
public decimal BollingerDeviation { get => _bollingerDeviation.Value; set => _bollingerDeviation.Value = value; }
public RrsImpulseStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI length", "RSI");
_rsiUpperLevel = Param(nameof(RsiUpperLevel), 65m)
.SetDisplay("RSI Upper", "Overbought", "RSI");
_rsiLowerLevel = Param(nameof(RsiLowerLevel), 35m)
.SetDisplay("RSI Lower", "Oversold", "RSI");
_stochasticKPeriod = Param(nameof(StochasticKPeriod), 10)
.SetDisplay("Stochastic %K", "%K period", "Stochastic");
_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
.SetDisplay("Stochastic %D", "%D period", "Stochastic");
_stochasticUpperLevel = Param(nameof(StochasticUpperLevel), 70m)
.SetDisplay("Stochastic Upper", "Overbought", "Stochastic");
_stochasticLowerLevel = Param(nameof(StochasticLowerLevel), 30m)
.SetDisplay("Stochastic Lower", "Oversold", "Stochastic");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetDisplay("Bollinger Period", "BB length", "Bollinger");
_bollingerDeviation = Param(nameof(BollingerDeviation), 2m)
.SetDisplay("Bollinger Deviation", "BB deviation", "Bollinger");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochasticKPeriod;
stochastic.D.Length = StochasticDPeriod;
var bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerDeviation };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, stochastic, bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiVal, IIndicatorValue stochVal, IIndicatorValue bbVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!rsiVal.IsFinal || !stochVal.IsFinal || !bbVal.IsFinal)
return;
if (!rsiVal.IsFormed || !stochVal.IsFormed || !bbVal.IsFormed)
return;
var rsi = rsiVal.GetValue<decimal>();
var stoch = (StochasticOscillatorValue)stochVal;
var stochK = stoch.K ?? 50m;
var bb = (BollingerBandsValue)bbVal;
var bbUpper = bb.UpBand ?? candle.ClosePrice;
var bbLower = bb.LowBand ?? candle.ClosePrice;
var close = candle.ClosePrice;
// Count how many indicators signal overbought/oversold
var obSignals = 0;
var osSignals = 0;
if (rsi >= RsiUpperLevel) obSignals++;
if (rsi <= RsiLowerLevel) osSignals++;
if (stochK >= StochasticUpperLevel) obSignals++;
if (stochK <= StochasticLowerLevel) osSignals++;
if (close >= bbUpper) obSignals++;
if (close <= bbLower) osSignals++;
// Counter-trend: need at least 2 of 3 indicators confirming
if (osSignals >= 2 && Position <= 0)
{
BuyMarket();
}
else if (obSignals >= 2 && Position >= 0)
{
SellMarket();
}
// Exit long when RSI and stoch neutralize
else if (Position > 0 && rsi > 50m && stochK > 50m)
{
SellMarket();
}
// Exit short when RSI and stoch neutralize
else if (Position < 0 && rsi < 50m && stochK < 50m)
{
BuyMarket();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, RelativeStrengthIndex, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class rrs_impulse_strategy(Strategy):
"""RSI + Stochastic + Bollinger Bands counter-trend strategy."""
def __init__(self):
super(rrs_impulse_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI length", "RSI")
self._rsi_upper_level = self.Param("RsiUpperLevel", 65.0) \
.SetDisplay("RSI Upper", "Overbought", "RSI")
self._rsi_lower_level = self.Param("RsiLowerLevel", 35.0) \
.SetDisplay("RSI Lower", "Oversold", "RSI")
self._stochastic_k_period = self.Param("StochasticKPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %K", "%K period", "Stochastic")
self._stochastic_d_period = self.Param("StochasticDPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %D", "%D period", "Stochastic")
self._stochastic_upper_level = self.Param("StochasticUpperLevel", 70.0) \
.SetDisplay("Stochastic Upper", "Overbought", "Stochastic")
self._stochastic_lower_level = self.Param("StochasticLowerLevel", 30.0) \
.SetDisplay("Stochastic Lower", "Oversold", "Stochastic")
self._bollinger_period = self.Param("BollingerPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Period", "BB length", "Bollinger")
self._bollinger_deviation = self.Param("BollingerDeviation", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Deviation", "BB deviation", "Bollinger")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(rrs_impulse_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
stochastic = StochasticOscillator()
stochastic.K.Length = self._stochastic_k_period.Value
stochastic.D.Length = self._stochastic_d_period.Value
bollinger = BollingerBands()
bollinger.Length = self._bollinger_period.Value
bollinger.Width = self._bollinger_deviation.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(rsi, stochastic, bollinger, self._process_candle).Start()
def _process_candle(self, candle, rsi_val, stoch_val, bb_val):
if candle.State != CandleStates.Finished:
return
if not rsi_val.IsFinal or not stoch_val.IsFinal or not bb_val.IsFinal:
return
if not rsi_val.IsFormed or not stoch_val.IsFormed or not bb_val.IsFormed:
return
rsi = float(rsi_val)
stoch_k = float(stoch_val.K) if stoch_val.K is not None else 50.0
bb_upper = bb_val.UpBand
bb_lower = bb_val.LowBand
upper = float(bb_upper) if bb_upper is not None else float(candle.ClosePrice)
lower = float(bb_lower) if bb_lower is not None else float(candle.ClosePrice)
close = float(candle.ClosePrice)
ob_signals = 0
os_signals = 0
if rsi >= float(self._rsi_upper_level.Value):
ob_signals += 1
if rsi <= float(self._rsi_lower_level.Value):
os_signals += 1
if stoch_k >= float(self._stochastic_upper_level.Value):
ob_signals += 1
if stoch_k <= float(self._stochastic_lower_level.Value):
os_signals += 1
if close >= upper:
ob_signals += 1
if close <= lower:
os_signals += 1
if os_signals >= 2 and self.Position <= 0:
self.BuyMarket()
elif ob_signals >= 2 and self.Position >= 0:
self.SellMarket()
elif self.Position > 0 and rsi > 50.0 and stoch_k > 50.0:
self.SellMarket()
elif self.Position < 0 and rsi < 50.0 and stoch_k < 50.0:
self.BuyMarket()
def CreateClone(self):
return rrs_impulse_strategy()