Открыть на GitHub

Стратегия JBrainUltraRSI

Пример стратегии объединяет индикатор относительной силы (RSI) и стохастический осциллятор для генерации торговых сигналов. Идея взята из оригинального советника MetaTrader, в котором использовались индикаторы JBrainTrendSig1 и UltraRSI. В данной адаптации стохастик выполняет роль фильтра тренда, а RSI подает сигналы входа.

Как это работает

  1. Индикаторы
    • RSI — измеряет импульс, сравнивая недавние прибыли и убытки. Пересечение уровня 50 снизу вверх указывает на бычий импульс, сверху вниз — на медвежий.
    • Стохастический осциллятор — оценивает положение цены закрытия относительно диапазона. Пересечения линий %K и %D подтверждают направление тренда.
  2. Режимы
    • JBrainSig1Filter — сигналы формирует RSI, стохастик подтверждает направление.
    • UltraRsiFilter — сигналы формирует стохастик, RSI выступает фильтром.
    • Composition — сделки открываются только при совпадении сигналов обоих индикаторов.
  3. Правила торговли
    • Длинная позиция открывается при появлении сигнала на покупку и отсутствии короткой позиции.
    • Короткая позиция открывается при сигнале на продажу и отсутствии длинной позиции.
    • Обратные сигналы закрывают текущие позиции при соответствующем разрешении.

Параметры

Параметр Описание
RsiPeriod Период расчёта RSI.
StochLength Период %K стохастика.
SignalLength Период %D стохастика.
Mode Режим комбинирования индикаторов.
AllowLongEntry / AllowShortEntry Разрешения на открытие длинных/коротких позиций.
AllowLongExit / AllowShortExit Разрешения на закрытие длинных/коротких позиций.
CandleType Таймфрейм свечей для работы.

Примечания

  • Стратегия использует высокоуровневый API StockSharp с Bind / BindEx для обработки индикаторов.
  • Защитные уровни задаются методом StartProtection().
  • При наличии области графика стратегия отображает свечи, индикаторы и собственные сделки.

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy combining RSI and Stochastic oscillator signals.
/// </summary>
public class JBrainUltraRsiStrategy : Strategy
{
        /// <summary>
        /// Combination modes for indicators.
        /// </summary>
        public enum AlgorithmModes
        {
                JBrainSig1Filter,
                UltraRsiFilter,
                Composition
        }

	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _stochLength;
	private readonly StrategyParam<int> _signalLength;
	private readonly StrategyParam<AlgorithmModes> _mode;
	private readonly StrategyParam<bool> _allowLongEntry;
	private readonly StrategyParam<bool> _allowShortEntry;
	private readonly StrategyParam<bool> _allowLongExit;
	private readonly StrategyParam<bool> _allowShortExit;
	private readonly StrategyParam<DataType> _candleType;
	
	private RelativeStrengthIndex _rsi;
	private StochasticOscillator _stochastic;
	private decimal? _prevRsi;
	private decimal? _prevK;
	private decimal? _prevD;
	
	/// <summary>
	/// RSI calculation period.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}
	
	/// <summary>
	/// Stochastic %K period.
	/// </summary>
	public int StochLength
	{
		get => _stochLength.Value;
		set => _stochLength.Value = value;
	}
	
	/// <summary>
	/// Stochastic %D period.
	/// </summary>
	public int SignalLength
	{
		get => _signalLength.Value;
		set => _signalLength.Value = value;
	}
	
	/// <summary>
	/// Mode combining indicators.
	/// </summary>
	public AlgorithmModes Mode
	{
		get => _mode.Value;
		set => _mode.Value = value;
	}
	
	/// <summary>
	/// Permission to open long positions.
	/// </summary>
	public bool AllowLongEntry
	{
		get => _allowLongEntry.Value;
		set => _allowLongEntry.Value = value;
	}
	
	/// <summary>
	/// Permission to open short positions.
	/// </summary>
	public bool AllowShortEntry
	{
		get => _allowShortEntry.Value;
		set => _allowShortEntry.Value = value;
	}
	
	/// <summary>
	/// Permission to close long positions.
	/// </summary>
	public bool AllowLongExit
	{
		get => _allowLongExit.Value;
		set => _allowLongExit.Value = value;
	}
	
	/// <summary>
	/// Permission to close short positions.
	/// </summary>
	public bool AllowShortExit
	{
		get => _allowShortExit.Value;
		set => _allowShortExit.Value = value;
	}
	
	/// <summary>
	/// Candle type to process.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}
	
	/// <summary>
	/// Initializes a new instance of <see cref="JBrainUltraRsiStrategy"/>.
	/// </summary>
	public JBrainUltraRsiStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 13)
		.SetGreaterThanZero()
		.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
		;
		
		_stochLength = Param(nameof(StochLength), 9)
		.SetGreaterThanZero()
		.SetDisplay("Stochastic %K", "Period for %K line", "Indicators")
		;
		
		_signalLength = Param(nameof(SignalLength), 3)
		.SetGreaterThanZero()
		.SetDisplay("Stochastic %D", "Period for %D line", "Indicators")
		;
		
		_mode = Param(nameof(Mode), AlgorithmModes.Composition)
		.SetDisplay("Mode", "Algorithm to enter the market", "General");
		
		_allowLongEntry = Param(nameof(AllowLongEntry), true)
		.SetDisplay("Allow Long Entry", "Permission to open long positions", "Trading");
		
		_allowShortEntry = Param(nameof(AllowShortEntry), true)
		.SetDisplay("Allow Short Entry", "Permission to open short positions", "Trading");
		
		_allowLongExit = Param(nameof(AllowLongExit), true)
		.SetDisplay("Allow Long Exit", "Permission to close long positions", "Trading");
		
		_allowShortExit = Param(nameof(AllowShortExit), true)
		.SetDisplay("Allow Short Exit", "Permission to close short positions", "Trading");
		
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles", "General");
	}
	
	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}
	
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		
		_rsi = default;
		_stochastic = default;
		_prevRsi = default;
		_prevK = default;
		_prevD = default;
	}
	
	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		
		StartProtection(null, null);
		
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_stochastic = new StochasticOscillator
		{
			K = { Length = StochLength },
			D = { Length = SignalLength },
		};
		
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_stochastic, ProcessCandle)
			.Start();

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

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

		// process RSI manually
		var rsiResult = _rsi.Process(candle.ClosePrice, candle.OpenTime, true);
		if (!rsiResult.IsFormed)
			return;

		var rsi = rsiResult.ToDecimal();

		var stoch = (StochasticOscillatorValue)stochValue;
		if (stoch.K is not decimal k || stoch.D is not decimal d)
		{
			_prevRsi = rsi;
			return;
		}

		var rsiUp = _prevRsi is decimal pr && pr <= 50m && rsi > 50m;
		var rsiDown = _prevRsi is decimal pr2 && pr2 >= 50m && rsi < 50m;
		var stochUp = _prevK is decimal pk && _prevD is decimal pd && pk <= pd && k > d;
		var stochDown = _prevK is decimal pk2 && _prevD is decimal pd2 && pk2 >= pd2 && k < d;

		var buySignal = false;
		var sellSignal = false;

		switch (Mode)
		{
			case AlgorithmModes.JBrainSig1Filter:
				buySignal = rsiUp && k > d;
				sellSignal = rsiDown && k < d;
				break;
			case AlgorithmModes.UltraRsiFilter:
				buySignal = stochUp && rsi > 50m;
				sellSignal = stochDown && rsi < 50m;
				break;
			case AlgorithmModes.Composition:
				buySignal = rsiUp && stochUp;
				sellSignal = rsiDown && stochDown;
				break;
		}

		if (buySignal)
		{
			if (Position < 0 && AllowShortExit)
				BuyMarket();
			if (AllowLongEntry && Position <= 0)
				BuyMarket();
		}
		else if (sellSignal)
		{
			if (Position > 0 && AllowLongExit)
				SellMarket();
			if (AllowShortEntry && Position >= 0)
				SellMarket();
		}

		_prevRsi = rsi;
		_prevK = k;
		_prevD = d;
	}
}