GitHub で見る

RSI Histogram Strategy

This strategy uses the Relative Strength Index (RSI) histogram to detect reversals when the oscillator leaves extreme zones. The histogram colors the RSI value based on two thresholds: a high level marking the overbought area and a low level marking the oversold area. When the color changes from green (overbought) to gray or red, the strategy closes short positions and opens a long position. When the color changes from red (oversold) to gray or green, it closes long positions and opens a short position.

The implementation is built with the high-level StockSharp API and subscribes to candle data of a selected timeframe. An RSI indicator processes the candles and generates signals whenever its value exits the defined zones. Optional parameters allow enabling or disabling entries and exits for each side separately.

The strategy is meant for educational purposes and demonstrates how to convert an MQL expert advisor to the StockSharp framework.

Details

  • Entry Criteria:
    • Long: Previous bar was above the high level and the last bar moved below it.
    • Short: Previous bar was below the low level and the last bar moved above it.
  • Long/Short: Both sides.
  • Exit Criteria:
    • Opposite signal closes the current position if allowed.
  • Stops: No built-in stops; the StartProtection framework is prepared for adding them.
  • Default Values:
    • RSI period = 14
    • High level = 60
    • Low level = 40
    • Timeframe = 4 hours
  • Filters:
    • Category: Mean reversion
    • Direction: Both
    • Indicators: Single
    • Stops: Optional
    • Complexity: Simple
    • Timeframe: Medium-term
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: Moderate
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 based on RSI histogram color changes.
/// Opens long when RSI leaves the overbought zone.
/// Opens short when RSI leaves the oversold zone.
/// </summary>
public class RsiHistogramStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _highLevel;
	private readonly StrategyParam<decimal> _lowLevel;
	private readonly StrategyParam<bool> _buyPosOpen;
	private readonly StrategyParam<bool> _sellPosOpen;
	private readonly StrategyParam<bool> _buyPosClose;
	private readonly StrategyParam<bool> _sellPosClose;
	private readonly StrategyParam<DataType> _candleType;

	private int _prevClass = -1;
	private int _prevPrevClass = -1;

	/// <summary>
	/// RSI period length.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// Overbought threshold.
	/// </summary>
	public decimal HighLevel
	{
		get => _highLevel.Value;
		set => _highLevel.Value = value;
	}

	/// <summary>
	/// Oversold threshold.
	/// </summary>
	public decimal LowLevel
	{
		get => _lowLevel.Value;
		set => _lowLevel.Value = value;
	}

	/// <summary>
	/// Allow opening long positions.
	/// </summary>
	public bool BuyPosOpen
	{
		get => _buyPosOpen.Value;
		set => _buyPosOpen.Value = value;
	}

	/// <summary>
	/// Allow opening short positions.
	/// </summary>
	public bool SellPosOpen
	{
		get => _sellPosOpen.Value;
		set => _sellPosOpen.Value = value;
	}

	/// <summary>
	/// Allow closing long positions on opposite signal.
	/// </summary>
	public bool BuyPosClose
	{
		get => _buyPosClose.Value;
		set => _buyPosClose.Value = value;
	}

	/// <summary>
	/// Allow closing short positions on opposite signal.
	/// </summary>
	public bool SellPosClose
	{
		get => _sellPosClose.Value;
		set => _sellPosClose.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public RsiHistogramStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "Length of the RSI indicator", "RSI")
			.SetGreaterThanZero()
			
			.SetOptimize(5, 30, 1);

		_highLevel = Param(nameof(HighLevel), 60m)
			.SetDisplay("High Level", "Overbought threshold", "RSI")
			
			.SetOptimize(55m, 70m, 5m);

		_lowLevel = Param(nameof(LowLevel), 40m)
			.SetDisplay("Low Level", "Oversold threshold", "RSI")
			
			.SetOptimize(30m, 45m, 5m);

		_buyPosOpen = Param(nameof(BuyPosOpen), true)
			.SetDisplay("Enable Buy Entry", "Allow long entries when signal appears", "Trading");

		_sellPosOpen = Param(nameof(SellPosOpen), true)
			.SetDisplay("Enable Sell Entry", "Allow short entries when signal appears", "Trading");

		_buyPosClose = Param(nameof(BuyPosClose), true)
			.SetDisplay("Close Buy Positions", "Allow closing longs on opposite signal", "Trading");

		_sellPosClose = Param(nameof(SellPosClose), true)
			.SetDisplay("Close Sell Positions", "Allow closing shorts on opposite signal", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for RSI calculation", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClass = -1;
		_prevPrevClass = -1;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription.Bind(rsi, (candle, rsiValue) =>
		{
			if (candle.State != CandleStates.Finished)
				return;

			var currentClass = rsiValue > HighLevel ? 0 : rsiValue < LowLevel ? 2 : 1;

			if (!IsFormedAndOnlineAndAllowTrading())
			{
				_prevPrevClass = _prevClass;
				_prevClass = currentClass;
				return;
			}

			if (_prevPrevClass == 0 && _prevClass > 0)
			{
				if (SellPosClose && Position < 0)
					BuyMarket();

				if (BuyPosOpen && Position <= 0)
					BuyMarket();
			}
			else if (_prevPrevClass == 2 && _prevClass < 2)
			{
				if (BuyPosClose && Position > 0)
					SellMarket();

				if (SellPosOpen && Position >= 0)
					SellMarket();
			}

			_prevPrevClass = _prevClass;
			_prevClass = currentClass;
		})
		.Start();

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