Открыть на GitHub

Стратегия RSI EA v2

Стратегия представляет собой порт советника MetaTrader 5 «RSI EA v2» для платформы StockSharp. Она автоматизирует торговлю на основе пересечений уровней индикатора RSI и сохраняет управление капиталом, трейлинг-стоп и фильтр торговых часов оригинального советника. По умолчанию используется минутный таймфрейм, но тип свечей можно выбрать через параметры.

Логика торговли

  • Входы
    • Покупка открывается, когда RSI поднимается выше уровня покупки, а на предыдущей завершённой свече RSI находился ниже этого уровня, и торговый интервал разрешает сделки.
    • Продажа открывается, когда RSI опускается ниже уровня продажи, а ранее располагался выше, при условии, что торговое окно активно.
    • Если уже есть позиция противоположного направления, объём рыночной заявки подбирается так, чтобы закрыть существующую позицию и открыть новую в требуемую сторону (используется неттинг).
  • Выходы
    • При появлении новой позиции сразу выставляются уровни стоп-лосса и тейк-профита, заданные в пипсах.
    • Трейлинг-стоп повторяет логику оригинала: активируется после прохождения цены на величину TrailingStop + TrailingStep и передвигается не менее чем на trailing step.
    • Опциональное закрытие по сигналу ликвидирует лонги при обратном пересечении уровня продажи и шорты при возврате RSI выше уровня покупки.
    • Все условия проверяются только на закрытых свечах, что обеспечивает повторяемость в тестах.

Управление рисками и позицией

  • Стоп-лосс / Тейк-профит – задаются в пипсах и переводятся в цену с учётом точности инструмента (поддерживаются 3- и 5-знаковые форекс-котировки).
  • Трейлинг-стоп – отключается нулевым значением; при ненулевом расстоянии требуется положительный шаг.
  • Размер позиции – фиксированный объём либо расчёт из процента риска и величины стопа. Режим риска требует данных по стоимости шага цены и текущей стоимости портфеля.
  • Торговое окно – дневной фильтр с указанием начального (включительно) и конечного (исключительно) часа. Если значения совпадают, торговля запрещена.

Параметры

Параметр Описание
OpenBuy / OpenSell Включают/отключают сделки в сторону покупки и продажи.
CloseBySignal Закрывать ли позицию по обратному сигналу RSI.
StopLossPips Размер стоп-лосса в пипсах (0 — без стопа).
TakeProfitPips Размер тейк-профита в пипсах (0 — без цели).
TrailingStopPips Дистанция трейлинг-стопа в пипсах.
TrailingStepPips Дополнительное улучшение цены, необходимое для переноса трейлинга. При активном трейлинге должно быть > 0.
RsiPeriod Период расчёта RSI.
RsiBuyLevel / RsiSellLevel Пороговые уровни для входа и сигналов выхода.
UseRiskSizing Переключение на расчёт объёма по проценту риска.
FixedVolume Фиксированный объём сделки и запасное значение для риск-режима.
RiskPercent Доля капитала, рискуемая в сделке. Используется только при включённом риск-режиме и положительном стопе.
UseTimeControl Включает фильтр торговых часов.
StartHour / EndHour Начало (включительно) и конец (исключительно) торгового окна в часах 0–23.
CandleType Тип свечей, на которых рассчитывается RSI.

Особенности реализации

  • Используется высокоуровневое связывание подписки на свечи с индикатором RSI.
  • Пересчёт пипсов опирается на PriceStep и Decimals, чтобы соответствовать шкале цен MetaTrader.
  • Объём заявок нормализуется по шагу объёма и границам (минимум/максимум) инструмента.
  • Трейлинг-стоп хранится во внутренних переменных; закрытие при достижении уровня выполняется рыночной заявкой.
  • Для лонгов и шортов ведётся раздельное состояние, чтобы сохранять уровни защиты и трейлинга между свечами.

Как использовать

  1. Подключите стратегию к коннектору StockSharp с доступными данными по инструменту и портфелю.
  2. Настройте уровни RSI, дистанции стопов/тейков и при необходимости ограничения по времени.
  3. При наличии данных по стоимости портфеля можно активировать режим расчёта объёма от процента риска, иначе используйте фиксированный объём.
  4. Запустите стратегию — она будет ждать закрытия свечей, реагировать на сигналы RSI и управлять открытыми позициями согласно заданным параметрам.
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>
/// RSI EA v2 strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class RsiEaV2Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public RsiEaV2Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}