在 GitHub 上查看

RSI 趋势交易策略

摘要

该策略移植自 MetaTrader 的“RSI trader”专家顾问,通过价格移动均线与 RSI 平滑均线的双重趋势过滤来寻找方向一致的行情。当两个过滤器给出相反信号时视为盘整,并平掉现有仓位。默认使用与原策略相同的 1 小时 K 线,也可选择其他任意烛图数据。

工作流程

  1. RSI Period(默认 14)计算 RSI。
  2. 使用两个简单移动平均线对 RSI 值进行平滑,分别对应 Short RSI MALong RSI MA
  3. 对收盘价分别应用简单移动平均(Short Price MA)和线性加权平均(Long Price MA)。
  4. 仅在 K 线收盘后生成交易信号:
    • 做多:价格短均线和 RSI 短均线同时位于各自长均线上方。
    • 做空:价格短均线和 RSI 短均线同时位于各自长均线下方。
    • 盘整:两个模块的方向相互矛盾,此时若存在持仓则立即市价平仓。
  5. 通过 BuyMarket / SellMarket 下单。在建立新方向之前,策略会先平掉相反头寸。

参数

名称 说明 默认值 可优化范围
RSI Period RSI 的计算周期。 14 是(7~28,步长 1)
Short Price MA 价格短期简单移动平均长度。 9 是(5~20,步长 1)
Long Price MA 价格长期线性加权平均长度。 45 是(30~90,步长 5)
Short RSI MA RSI 短期平滑均线长度。 9 是(5~20,步长 1)
Long RSI MA RSI 长期平滑均线长度。 45 是(30~90,步长 5)
Candle Type 使用的烛图数据类型,默认为 1 小时。 H1

备注

  • 仅在所有指标准备就绪时才会发出交易指令。
  • 原始 EA 使用手数与滑点参数;在 StockSharp 中,订单数量来自策略的 Volume 属性,滑点由交易适配器处理。
  • 策略本身不设置固定止损/止盈,离场完全依赖盘整检测,可结合外部风险控制。
  • 如果连接了图表服务,界面会绘制价格及 RSI 的均线以便观察信号。
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 Trader strategy combining price SMA crossover with RSI trend confirmation.
/// Buy when short SMA crosses above long SMA with RSI above 50.
/// Sell when short SMA crosses below long SMA with RSI below 50.
/// </summary>
public class RsiTraderAlignedAveragesStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _shortMaPeriod;
	private readonly StrategyParam<int> _longMaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevShort;
	private decimal _prevLong;
	private bool _hasPrev;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int ShortMaPeriod { get => _shortMaPeriod.Value; set => _shortMaPeriod.Value = value; }
	public int LongMaPeriod { get => _longMaPeriod.Value; set => _longMaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

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

		_shortMaPeriod = Param(nameof(ShortMaPeriod), 9)
			.SetDisplay("Short MA", "Short moving average period", "Indicators");

		_longMaPeriod = Param(nameof(LongMaPeriod), 26)
			.SetDisplay("Long MA", "Long moving average period", "Indicators");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevShort = 0m;
		_prevLong = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var shortMa = new SimpleMovingAverage { Length = ShortMaPeriod };
		var longMa = new SimpleMovingAverage { Length = LongMaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, shortMa, longMa, ProcessCandle)
			.Start();
	}

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

		if (!_hasPrev)
		{
			_prevShort = shortMa;
			_prevLong = longMa;
			_hasPrev = true;
			return;
		}

		var bullCross = _prevShort <= _prevLong && shortMa > longMa;
		var bearCross = _prevShort >= _prevLong && shortMa < longMa;

		if (Position <= 0 && bullCross && rsiValue > 50)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (Position >= 0 && bearCross && rsiValue < 50)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevShort = shortMa;
		_prevLong = longMa;
	}
}