在 GitHub 上查看

RSI Alert 策略

概述

RSI Alert Strategy 将 MetaTrader 5 中的 "RSI Alert" 智能交易系统移植到 StockSharp 框架。原始 EA 在每根新 K 线结束时计算相对强弱指标(RSI),当 RSI 进入深度超卖(≤20)或超买(≥80)区域时发送提醒并立即执行市价交易。移植版本延续了这种事件驱动的思路:订阅蜡烛数据、评估完成的 RSI 数值,并在触发阈值时通过市价单自动翻转仓位。

交易逻辑

  1. 订阅指定的蜡烛序列(默认 1 分钟)并将收盘价输入 RelativeStrengthIndex 指标。
  2. 忽略未完成的蜡烛,只在新 K 线闭合后检查条件,与 MQL 实现保持一致。
  3. 生成交易信号:
    • 买入信号 – RSI ≤ OversoldLevel。策略平掉现有空头并按设定手数开多。
    • 卖出信号 – RSI ≥ OverboughtLevel。策略平掉现有多头并按设定手数开空。
  4. 全部使用 BuyMarket/SellMarket 市价单执行,不会挂出挂单、止损或止盈。原 EA 提供了可选的 SL/TP 输入,但默认依赖人工管理;本移植版本专注于“提醒即交易”的逻辑,风险控制可交由外部模块(如 StartProtection() 或组合风控)处理。

当出现反向信号时,策略会在下单时自动补足仓位差额,先平仓再反手,与原脚本的行为完全一致。

参数

参数 默认值 说明
OrderVolume 0.01 每次市价交易的基础手数。反向开仓时会额外补齐当前仓位的绝对量。
RsiPeriod 30 RSI 计算周期,必须为正整数。
OverboughtLevel 80 触发卖出信号的 RSI 阈值,可用于优化。
OversoldLevel 20 触发买入信号的 RSI 阈值。
CandleType 1 分钟 TimeFrameCandle 用于计算 RSI 的蜡烛类型,可根据需要改成更高周期。

所有设置都通过 StrategyParam<T> 暴露,可在 StockSharp 设计器中配置、保存到预设文件并参与参数优化。

实现说明

  • 全面使用 StockSharp 的高级 API:通过 SubscribeCandles() 获取数据,并利用 subscription.Bind(indicator, callback) 传递指标结果,无需手动复制指标缓冲区。
  • Strategy.VolumeOrderVolume 参数保持同步,确保用户在运行时调整手数后反手逻辑仍然正确。
  • 代码中的注释与 XML 文档均采用英文,符合仓库的贡献要求。
  • 在设计器中运行时,会自动绘制价格蜡烛、成交记录以及 RSI 曲线,便于视觉分析。

使用建议

  • 如需自动风控,可结合外部的止损、止盈或移动保护模块。
  • 针对不同市场波动性优化 RSI 阈值,以匹配交易节奏。
  • 根据交易风格调整蜡烛周期:默认的 1 分钟适合提醒式短线交易,更长周期适用于波段策略。
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>
/// RSI alert strategy converted from the original MetaTrader expert advisor.
/// Buys when RSI drops below the oversold threshold and sells when RSI rises above the overbought threshold.
/// </summary>
public class RsiAlertStrategy : Strategy
{
	private readonly StrategyParam<decimal> _orderVolume;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _overboughtLevel;
	private readonly StrategyParam<decimal> _oversoldLevel;
	private readonly StrategyParam<DataType> _candleType;

	private RelativeStrengthIndex _rsi;

	/// <summary>
	/// Order volume used for market trades.
	/// </summary>
	public decimal OrderVolume
	{
		get => _orderVolume.Value;
		set
		{
			_orderVolume.Value = value;
			Volume = value; // Keep the base strategy volume aligned with the parameter value.
		}
	}

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

	/// <summary>
	/// RSI level that triggers short trades.
	/// </summary>
	public decimal OverboughtLevel
	{
		get => _overboughtLevel.Value;
		set => _overboughtLevel.Value = value;
	}

	/// <summary>
	/// RSI level that triggers long trades.
	/// </summary>
	public decimal OversoldLevel
	{
		get => _oversoldLevel.Value;
		set => _oversoldLevel.Value = value;
	}

	/// <summary>
	/// Candle type supplying prices to the RSI indicator.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="RsiAlertStrategy"/> with default parameters.
	/// </summary>
	public RsiAlertStrategy()
	{
		_orderVolume = Param(nameof(OrderVolume), 0.01m)
			.SetGreaterThanZero()
			.SetDisplay("Order Volume", "Order size used for market trades", "Trading")
			;

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "Number of bars for RSI calculation", "Indicator")
			;

		_overboughtLevel = Param(nameof(OverboughtLevel), 70m)
			.SetDisplay("Overbought Level", "RSI threshold that triggers short signals", "Indicator")
			;

		_oversoldLevel = Param(nameof(OversoldLevel), 30m)
			.SetDisplay("Oversold Level", "RSI threshold that triggers long signals", "Indicator")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles that feed the RSI", "General");

		Volume = OrderVolume;
	}

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

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

		_rsi = null;
	}

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

		Volume = OrderVolume; // Ensure the strategy uses the configured volume on each start.

		_rsi = new RelativeStrengthIndex
		{
			Length = RsiPeriod
		};

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(_rsi, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent));

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

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

		var buySignal = rsiValue <= OversoldLevel;
		var sellSignal = rsiValue >= OverboughtLevel;

		if (buySignal && Position == 0)
		{
			BuyMarket();
		}
		else if (sellSignal && Position == 0)
		{
			SellMarket();
		}
	}
}