在 GitHub 上查看
RSI Alert 策略
概述
RSI Alert Strategy 将 MetaTrader 5 中的 "RSI Alert" 智能交易系统移植到 StockSharp 框架。原始 EA 在每根新 K 线结束时计算相对强弱指标(RSI),当 RSI 进入深度超卖(≤20)或超买(≥80)区域时发送提醒并立即执行市价交易。移植版本延续了这种事件驱动的思路:订阅蜡烛数据、评估完成的 RSI 数值,并在触发阈值时通过市价单自动翻转仓位。
交易逻辑
- 订阅指定的蜡烛序列(默认 1 分钟)并将收盘价输入
RelativeStrengthIndex 指标。
- 忽略未完成的蜡烛,只在新 K 线闭合后检查条件,与 MQL 实现保持一致。
- 生成交易信号:
- 买入信号 – RSI ≤
OversoldLevel。策略平掉现有空头并按设定手数开多。
- 卖出信号 – RSI ≥
OverboughtLevel。策略平掉现有多头并按设定手数开空。
- 全部使用
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.Volume 与 OrderVolume 参数保持同步,确保用户在运行时调整手数后反手逻辑仍然正确。
- 代码中的注释与 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();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_alert_strategy(Strategy):
def __init__(self):
super(rsi_alert_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._overbought_level = self.Param("OverboughtLevel", 70.0)
self._oversold_level = self.Param("OversoldLevel", 30.0)
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def OverboughtLevel(self):
return self._overbought_level.Value
@OverboughtLevel.setter
def OverboughtLevel(self, value):
self._overbought_level.Value = value
@property
def OversoldLevel(self):
return self._oversold_level.Value
@OversoldLevel.setter
def OversoldLevel(self, value):
self._oversold_level.Value = value
def OnReseted(self):
super(rsi_alert_strategy, self).OnReseted()
def OnStarted2(self, time):
super(rsi_alert_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent))
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
buy_signal = rsi_val <= float(self.OversoldLevel)
sell_signal = rsi_val >= float(self.OverboughtLevel)
if buy_signal and self.Position == 0:
self.BuyMarket()
elif sell_signal and self.Position == 0:
self.SellMarket()
def CreateClone(self):
return rsi_alert_strategy()