在 GitHub 上查看

RSI Expert 突破策略

概述

  • 源自 MetaTrader 5 平台的「RSI_Expert」策略,核心思想是交易 RSI 阈值的突破与回归。
  • 仅使用一个 RSI 指标,在超卖 / 超买区域附近寻找动量反转信号。
  • 保留原策略中以点(pip)为单位的固定止盈、止损与跟踪止损机制。

策略逻辑

  1. 在选定的 K 线周期上计算 RSI(默认周期 14)。
  2. 记录最近两个已经完成的 RSI 数值。
  3. 当 RSI 重新上穿下限阈值(默认 20)且之前曾位于阈值下方时,开多单。
  4. 当 RSI 再次跌破上限阈值(默认 60)且之前曾位于阈值上方时,开空单。
  5. 在建立新仓位前先平掉反向仓位,保证净头寸方向一致。
  6. 使用可选的止损、止盈和跟踪止损(均以点为单位)管理持仓。

参数

名称 说明 默认值
CandleType 使用的 K 线周期。 1 小时
TradeVolume 下单手数。 0.1
RsiPeriod RSI 计算周期。 14
RsiUpperLevel 触发做空的 RSI 上限阈值。 60
RsiLowerLevel 触发做多的 RSI 下限阈值。 20
TakeProfitPips 止盈距离(点),0 表示关闭。 60
StopLossPips 止损距离(点),0 表示关闭。 0
TrailingStopPips 跟踪止损距离(点),0 表示关闭。 15
TrailingStepPips 每次移动跟踪止损所需的最小价格改善。 5

关于点值: 在 StockSharp 版本中,一个「点」等于 Security.PriceStep。对于带有小数报价的外汇品种,请确认价格步长与交易所使用的点值一致,必要时手动调整参数。

风险控制

  • 每根收盘 K 线都会根据当前持仓均价检查止盈与止损条件。
  • 当盈利超过 TrailingStopPips + TrailingStepPips 时启动跟踪止损,并按 TrailingStopPips 的距离跟随价格。
  • 通过比较 K 线的最高价 / 最低价模拟盘中触发,一旦触及相应价位即市价平仓。

移植说明

  • 采用高层 API(SubscribeCandles + Bind),直接在回调中获取 RSI 数值,无需手动维护指标缓冲区。
  • 跟踪止损的实现严格复刻 MQL 版本的条件,包括再次移动前所需的最小价格步长。
  • 每当仓位关闭或方向切换时都会重置跟踪状态,避免沿用旧的止损价格。
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>
/// RSI Expert Breakout strategy. Trades RSI threshold crossovers.
/// </summary>
public class RsiExpertBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiUpper;
	private readonly StrategyParam<decimal> _rsiLower;

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

	public decimal RsiLower
	{
		get => _rsiLower.Value;
		set => _rsiLower.Value = value;
	}

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

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

		_rsiUpper = Param(nameof(RsiUpper), 70m)
			.SetDisplay("RSI Upper", "Overbought threshold", "Indicators");

		_rsiLower = Param(nameof(RsiLower), 30m)
			.SetDisplay("RSI Lower", "Oversold threshold", "Indicators");
	}

	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);
			var rsiArea = CreateChartArea();
			if (rsiArea != null)
				DrawIndicator(rsiArea, rsi);
			DrawOwnTrades(area);
		}
	}

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

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

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

		// RSI crosses above lower level from below → buy
		if (_prevRsi.Value < RsiLower && rsiVal >= RsiLower && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// RSI crosses below upper level from above → sell
		else if (_prevRsi.Value > RsiUpper && rsiVal <= RsiUpper && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevRsi = rsiVal;
	}
}