Открыть на GitHub

Стратегия RRS Impulse

RRS Impulse — это перенос эксперта MetaTrader «RRS Impulse» на высокоуровневый API StockSharp. В оригинале робот сочетал фильтры RSI, стохастика и полос Боллинджера, переключал режимы силы сигналов и использовал виртуальные стопы с трейлингом. В версии на C# сохранена та же логика, но реализация целиком построена на высокоуровневых вызовах: свечные подписки питают индикаторы, а заявки выставляются методами BuyMarket, SellMarket и ClosePosition.

Торговая логика

  1. Режимы индикаторов — четыре варианта поведения:
    • Rsi: торгуем, когда RSI выходит из зоны перекупленности/перепроданности.
    • Stochastic: одновременно требуются уровни %K и %D выше/ниже заданных порогов.
    • BollingerBands: реакция на закрытие свечи выше верхней или ниже нижней полосы.
    • RsiStochasticBollinger: сигнал возникает только при единогласном подтверждении всех трёх фильтров.
  2. Направление торговлиTrend следует за показаниями индикатора (перекупленность → продажи), а CounterTrend отыгрывает разворот (перекупленность → покупки).
  3. Сила сигнала — определяет, сколько таймфреймов должны совпасть:
    • SingleTimeFrame: используется только базовый таймфрейм CandleType.
    • MultiTimeFrame: требуется подтверждение на M1, M5, M15, M30, H1 и H4.
    • Strong: концентрируется на внутридневном импульсе (M1, M5, M15, M30).
    • VeryStrong: использует всю лестницу M1…H4. При комбинированном режиме индикаторов каждый таймфрейм должен выполнить условия всех трёх фильтров.
  4. Риск-менеджмент — для каждой позиции отслеживаются три виртуальных условия выхода:
    • фиксированный стоп-лосс в пунктах;
    • фиксированный тейк-профит в пунктах;
    • трейлинг-стоп, активируемый после достижения 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();
		}
	}
}