Открыть на GitHub

Стратегия RSI Expert Breakout

Обзор

  • Порт оригинальной стратегии MetaTrader 5 «RSI_Expert», торгующей пробои уровней RSI.
  • Использует единственный индикатор RSI для поиска разворотов импульса возле зон перепроданности и перекупленности.
  • Сохраняет механику фиксированных тейк-профитов, стоп-лоссов и трейлинг-стопа, заданных в пунктах.

Логика стратегии

  1. Строим RSI по выбранному таймфрейму свечей (по умолчанию период 14).
  2. Отслеживаем два последних завершённых значения RSI.
  3. Открываем длинную позицию, когда RSI возвращается выше нижнего порога (20 по умолчанию) после нахождения ниже него.
  4. Открываем короткую позицию, когда RSI уходит ниже верхнего порога (60 по умолчанию) после нахождения выше него.
  5. Перед открытием новой позиции закрываем встречную, чтобы оставаться направленными только в одну сторону.
  6. Управляем сделками при помощи опциональных стоп-лоссов, тейк-профитов и трейлинг-стопа, измеряемых в пунктах.

Параметры

Имя Описание Значение по умолчанию
CandleType Таймфрейм свечей. Часовые свечи
TradeVolume Объём заявки. 0.1
RsiPeriod Период расчёта RSI. 14
RsiUpperLevel Верхний уровень RSI для сигнала на продажу. 60
RsiLowerLevel Нижний уровень RSI для сигнала на покупку. 20
TakeProfitPips Дистанция тейк-профита в пунктах (0 — без тейка). 60
StopLossPips Дистанция стоп-лосса в пунктах (0 — без стопа). 0
TrailingStopPips Дистанция трейлинг-стопа в пунктах (0 — без трейлинга). 15
TrailingStepPips Минимальное улучшение цены перед переносом трейлинг-стопа. 5

Пояснение по пунктам: в версии StockSharp «пункт» равен одному Security.PriceStep. Для инструментов с дробным котированием убедитесь, что шаг цены соответствует понятию «пункт», либо подберите значения параметров вручную.

Управление рисками

  • Тейк-профит и стоп-лосс проверяются на каждой закрывшейся свече с использованием средней цены позиции.
  • Трейлинг-стоп активируется только после движения больше, чем TrailingStopPips + TrailingStepPips, и затем сопровождает цену на расстоянии TrailingStopPips.
  • Для имитации внутрисвечных срабатываний сравниваются максимумы/минимумы свечей; при достижении уровня позиция закрывается по рынку.

Особенности портирования

  • Используется высокоуровневый API (SubscribeCandles + Bind), индикаторные значения берутся напрямую из колбэка без ручных буферов.
  • Логика трейлинг-стопа повторяет условия MQL, включая требуемый шаг смещения перед каждым переносом.
  • Состояние трейлинга сбрасывается при закрытии или развороте позиции, чтобы исключить наследование старых уровней.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RSI Expert Breakout strategy. Trades RSI threshold crossovers.
/// </summary>
public class RsiExpertBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpper;
	private readonly StrategyParam<decimal> _rsiLower;

	private decimal? _prevRsi;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public decimal RsiUpper
	{
		get => _rsiUpper.Value;
		set => _rsiUpper.Value = value;
	}

	public decimal RsiLower
	{
		get => _rsiLower.Value;
		set => _rsiLower.Value = value;
	}

	public RsiExpertBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI lookback period", "Indicators");

		_rsiUpper = Param(nameof(RsiUpper), 70m)
			.SetDisplay("RSI Upper", "Overbought threshold", "Indicators");

		_rsiLower = Param(nameof(RsiLower), 30m)
			.SetDisplay("RSI Lower", "Oversold threshold", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevRsi = null;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			var rsiArea = CreateChartArea();
			if (rsiArea != null)
				DrawIndicator(rsiArea, rsi);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevRsi = rsiVal;
			return;
		}

		if (_prevRsi == null)
		{
			_prevRsi = rsiVal;
			return;
		}

		// RSI crosses above lower level from below → buy
		if (_prevRsi.Value < RsiLower && rsiVal >= RsiLower && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// RSI crosses below upper level from above → sell
		else if (_prevRsi.Value > RsiUpper && rsiVal <= RsiUpper && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevRsi = rsiVal;
	}
}