在 GitHub 上查看

RSI 阈值策略

将 MetaTrader 的 Exp_RSI 专家顾问转换为 StockSharp。 当 RSI 指标穿越设定的超买和超卖水平时,策略开仓或平仓。

细节

  • 入场条件:
    • 多头: RSI 上穿 RSI Low Level
    • 空头: RSI 下穿 RSI High Level
  • 多空方向: 双向。
  • 出场条件:
    • 反向信号或止损/止盈。
  • 止损: 以绝对价格单位设置的止盈和止损。
  • 默认值:
    • RSI Period = 14
    • RSI High Level = 60
    • RSI Low Level = 40
    • Stop Loss = 1000
    • Take Profit = 2000
  • 筛选:
    • 类别: 振荡器
    • 方向: 双向
    • 指标: 单一
    • 止损: 有
    • 复杂度: 初级
    • 时间框架: H4
    • 季节性: 无
    • 神经网络: 无
    • 背离: 无
    • 风险等级: 中等
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>
/// Simple RSI threshold strategy converted from MQL/16855.
/// Buys or sells when RSI crosses predefined levels.
/// </summary>
public class RsiThresholdStrategy : Strategy
{
	public enum TrendModes
	{
		/// <summary>
		/// Trade in the direction of RSI crossings.
		/// Buy when RSI crosses above the low level, sell when it crosses below the high level.
		/// </summary>
		Direct,
		/// <summary>
		/// Trade against the direction of RSI crossings.
		/// Sell when RSI crosses above the low level, buy when it crosses below the high level.
		/// </summary>
		Reverse
	}


	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _highLevel;
	private readonly StrategyParam<decimal> _lowLevel;
	private readonly StrategyParam<TrendModes> _trend;
	private readonly StrategyParam<bool> _buyOpen;
	private readonly StrategyParam<bool> _sellOpen;
	private readonly StrategyParam<bool> _buyClose;
	private readonly StrategyParam<bool> _sellClose;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;
	
	private decimal? _prevRsi;
	
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
	public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
	public TrendModes Trend { get => _trend.Value; set => _trend.Value = value; }
	public bool BuyOpen { get => _buyOpen.Value; set => _buyOpen.Value = value; }
	public bool SellOpen { get => _sellOpen.Value; set => _sellOpen.Value = value; }
	public bool BuyClose { get => _buyClose.Value; set => _buyClose.Value = value; }
	public bool SellClose { get => _sellClose.Value; set => _sellClose.Value = value; }
	public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
	public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	
	public RsiThresholdStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
		.SetGreaterThanZero()
		.SetDisplay("RSI Period", "Period for RSI calculation", "Indicator")
		
		.SetOptimize(10, 30, 2);
		
		_highLevel = Param(nameof(HighLevel), 70m)
		.SetDisplay("RSI High Level", "Overbought level", "Signal")
		
		.SetOptimize(50m, 80m, 5m);
		
		_lowLevel = Param(nameof(LowLevel), 30m)
		.SetDisplay("RSI Low Level", "Oversold level", "Signal")
		
		.SetOptimize(20m, 50m, 5m);
		
		_trend = Param(nameof(Trend), TrendModes.Direct)
		.SetDisplay("Trend Mode", "Trading direction relative to RSI crossings", "General");
		
		_buyOpen = Param(nameof(BuyOpen), true)
		.SetDisplay("Enable Buy Entry", "Allow opening long positions", "General");
		
		_sellOpen = Param(nameof(SellOpen), true)
		.SetDisplay("Enable Sell Entry", "Allow opening short positions", "General");
		
		_buyClose = Param(nameof(BuyClose), true)
		.SetDisplay("Enable Buy Exit", "Allow closing long positions", "General");
		
		_sellClose = Param(nameof(SellClose), true)
		.SetDisplay("Enable Sell Exit", "Allow closing short positions", "General");
		
		_stopLoss = Param(nameof(StopLoss), 1000m)
		.SetGreaterThanZero()
		.SetDisplay("Stop Loss", "Stop loss in price units", "Risk Management");
		
		_takeProfit = Param(nameof(TakeProfit), 2000m)
		.SetGreaterThanZero()
		.SetDisplay("Take Profit", "Take profit in price units", "Risk Management");
		
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles to use", "General");
	}
	
	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

	/// <inheritdoc />
	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();
		
		StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
	}
	
	private void ProcessCandle(ICandleMessage candle, decimal rsi)
	{
		if (candle.State != CandleStates.Finished) // only process completed candles
		return;
		
		if (_prevRsi is null)
		{
			// store first RSI value to detect crossings
			_prevRsi = rsi;
			return;
		}
		
		if (Trend == TrendModes.Direct)
		{
			if (_prevRsi <= LowLevel && rsi > LowLevel)
			// RSI crossed above oversold level
			{
				if (SellClose && Position < 0) // close short positions
				BuyMarket();
				
				if (BuyOpen && Position <= 0) // open long
				BuyMarket();
			}
			
			if (_prevRsi >= HighLevel && rsi < HighLevel)
			// RSI crossed below overbought level
			{
				if (BuyClose && Position > 0) // close long positions
				SellMarket();
				
				if (SellOpen && Position >= 0) // open short
				SellMarket();
			}
		}
else
	{
		if (_prevRsi <= LowLevel && rsi > LowLevel)
			// RSI crossed above oversold level
		{
			if (BuyClose && Position > 0) // close long positions
			SellMarket();
			
			if (SellOpen && Position >= 0) // open short
			SellMarket();
		}
		
		if (_prevRsi >= HighLevel && rsi < HighLevel)
			// RSI crossed below overbought level
		{
			if (SellClose && Position < 0) // close short positions
			BuyMarket();
			
			if (BuyOpen && Position <= 0) // open long
			BuyMarket();
		}
	}
	
	_prevRsi = rsi; // remember last RSI value
}
}