Открыть на GitHub

Стратегия Exp RSIOMA V2

Обзор

Exp RSIOMA V2 — конверсия оригинального советника MetaTrader 5 в инфраструктуру StockSharp. Стратегия использует осциллятор RSIOMA (Relative Strength Index of Moving Average): выбранная цена свечи сглаживается, из сглаженных значений рассчитывается импульс, после чего формируется RSI-подобная величина. Торговые решения принимаются, когда осциллятор меняет направление или выходит из ключевых зон.

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

  1. Предобработка цены. Цена свечи, заданная параметром AppliedPrice (по умолчанию закрытие), сглаживается одним из четырёх типов средних (простое, экспоненциальное, сглаженное или линейно взвешенное).
  2. Расчёт импульса. Сглаженное значение сравнивается с ценой MomentumPeriod свечей назад, что даёт текущий импульс.
  3. Вычисление RSIOMA. Положительные и отрицательные импульсы усредняются экспоненциальным фильтром длиной RsiomaLength, формируя значение от 0 до 100.
  4. Оценка сигналов. В зависимости от Mode анализируются последние закрытые свечи:
    • Breakdown — реагирует на выход RSIOMA за уровни MainTrendLong / MainTrendShort. Возврат из верхней зоны закрывает шорты и позволяет открыть лонг, выход из нижней зоны делает обратное.
    • Twist — ищет разворотные точки. Покупка происходит, когда наклон осциллятора меняется с нисходящего на восходящий, продажа — при обратном переходе.
    • CloudTwist — повторяет «облако» индикатора MT5: сделки открываются, когда RSIOMA возвращается из экстремумов внутрь канала, одновременно закрывая противоположные позиции.

Сигналы оцениваются на свече, заданной SignalBar (по умолчанию предыдущая полностью закрытая свеча).

Параметры

Параметр Описание Значение по умолчанию
OrderVolume Базовый объём заявки. 1
CandleType Тип свечей, используемый стратегией. 4 часа
EnableLongEntries / EnableShortEntries Разрешение на открытие длинных / коротких позиций. true
EnableLongExits / EnableShortExits Разрешение на закрытие длинных / коротких позиций. true
Mode Режим генерации сигналов (Breakdown, Twist, CloudTwist). Breakdown
PriceSmoothing Тип усреднения, применяемый к цене перед расчётом RSIOMA. Exponential
RsiomaLength Длина усреднения RSIOMA. 14
MomentumPeriod Запаздывание при расчёте импульса. 1
AppliedPrice Используемая цена свечи (close, open, median, DeMark и т.д.). Close
MainTrendLong / MainTrendShort Границы зон перекупленности и перепроданности. 60 / 40
SignalBar Количество закрытых свечей назад для анализа. 1

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

  • Реализованы только типы сглаживания, доступные в StockSharp (SMA, EMA, SMMA, WMA). Продвинутые варианты из MT5 (JJMA, VIDYA, AMA и др.) не поддерживаются.
  • Для инициализации RSI используется первая серия импульсов длиной RsiomaLength, затем применяется стандартное экспоненциальное обновление, как и в оригинальном советнике.
  • Перед открытием противоположной позиции стратегия всегда закрывает текущую. Параметры EnableLongEntries / EnableShortEntries и EnableLongExits / EnableShortExits позволяют гибко ограничивать направления.
  • Значение SignalBar = 0 позволяет реагировать на текущую закрытую свечу, большие значения имитируют задержку, доступную в MT5.

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

  1. Добавьте стратегию в проект StockSharp и назначьте инструмент для торговли.
  2. Настройте подписку на свечи через CandleType (по умолчанию 4-часовые) и при необходимости скорректируйте пороговые уровни под волатильность инструмента.
  3. Выберите режим сигналов: пробой уровней (Breakdown), разворот (Twist) или изменение «облака» (CloudTwist).
  4. Запустите стратегию. Она подпишется на выбранные свечи, рассчитает цепочку RSIOMA и отправит рыночные заявки при выполнении условий.
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>
/// RSIOMA strategy using RSI with overbought/oversold levels.
/// Buys when RSI crosses above oversold, sells when crosses below overbought.
/// </summary>
public class ExpRsiomaV2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<decimal> _oversold;

	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 Overbought
	{
		get => _overbought.Value;
		set => _overbought.Value = value;
	}

	public decimal Oversold
	{
		get => _oversold.Value;
		set => _oversold.Value = value;
	}

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

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

		_overbought = Param(nameof(Overbought), 65m)
			.SetDisplay("Overbought", "Overbought RSI level", "Levels");

		_oversold = Param(nameof(Oversold), 35m)
			.SetDisplay("Oversold", "Oversold RSI level", "Levels");
	}

	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);
			DrawOwnTrades(area);
		}

		var indArea = CreateChartArea();
		if (indArea != null)
		{
			DrawIndicator(indArea, rsi);
		}
	}

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

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

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

		// RSI crosses above oversold → buy signal
		if (_prevRsi.Value <= Oversold && rsiValue > Oversold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// RSI crosses below overbought → sell signal
		else if (_prevRsi.Value >= Overbought && rsiValue < Overbought && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevRsi = rsiValue;
	}
}