在 GitHub 上查看
Alerting System 阈值策略
Alerting System Threshold Strategy 是 MetaTrader 5 专家顾问 “AlertingSystem”(MQL 目录 31843)的 StockSharp 版本。原始 EA 在图表上绘制两条水平线,当买价突破上方水平线或卖价跌破下方水平线时播放提示音。C# 版本继承相同的预警思想,并使用 StockSharp 的高级 API 和日志系统来交付通知。
核心思路
- 订阅 Level 1 数据流(最优买价和卖价)。
- 当最优买价大于或等于上方阈值时触发一次性提示。
- 当最优卖价小于或等于下方阈值时触发一次性提示。
- 当价格返回区间内部时自动复位,以便下次突破继续生效。
与 MQL 版本在每个 tick 都播放声音不同,StockSharp 实现通过 AddInfoLog 输出一条信息日志来表示突破事件,从而避免重复提示导致的噪音。
参数
| 参数 |
说明 |
默认值 |
UpperPrice |
激活多头提示的买价阈值,设为 0 表示关闭。 |
0 |
LowerPrice |
激活空头提示的卖价阈值,设为 0 表示关闭。 |
0 |
两个参数都由 StrategyParam<decimal> 创建,支持运行中调整和优化。实际使用时可以像在 MetaTrader 中拖动水平线那样随时修改阈值。
执行流程
- 启动后调用
SubscribeLevel1().Bind(ProcessLevel1).Start() 订阅 Level 1 数据。
- 每条
Level1ChangeMessage 更新缓存的最优买价和卖价。
- 更新后检查上下提示条件:
- 上方提示:当
BestBid >= UpperPrice 且之前价格低于该阈值时触发一次。
- 下方提示:当
BestAsk <= LowerPrice 且之前价格高于该阈值时触发一次。
- 当市场回到区间内时,提示标志会重置,等待下一次突破。
日志与扩展
突破信息通过 AddInfoLog 写入,包含当前买价/卖价以及目标阈值。如果需要声音、短信或其他通知方式,可以在宿主应用程序中订阅策略的日志事件并执行自定义动作。
使用建议
- 只需设置感兴趣的一侧阈值,另一侧保持
0 即可禁用。
- 如果想恢复声音提示,可以在宿主程序里监听
Info 日志并调用自定义播放逻辑。
- 策略不进行下单或持仓管理,无需调用
StartProtection()。
与原版的差异
- 使用 Level 1 行情数据,而不是在图表上创建水平线对象。
- 每次突破只发送一次日志,避免信息泛滥。
- 报警条件与参数与 MQL 原版保持一致。
文件结构
CS/AlertingSystemStrategy.cs — 策略源码。
README.md — 英文说明。
README_ru.md — 俄文说明。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Alerting System Threshold strategy: RSI threshold crossover.
/// Buys when RSI crosses above oversold level, sells when crosses below overbought level.
/// </summary>
public class AlertingSystemThresholdStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private decimal _prevRsi;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public AlertingSystemThresholdStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "RSI oversold level", "Signals");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "RSI overbought level", "Signals");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
// Buy on cross above oversold
if (_prevRsi < Oversold && rsiValue >= Oversold && Position <= 0)
BuyMarket();
// Sell on cross below overbought
else if (_prevRsi > Overbought && rsiValue <= Overbought && Position >= 0)
SellMarket();
}
_prevRsi = rsiValue;
_hasPrev = true;
}
}
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class alerting_system_threshold_strategy(Strategy):
def __init__(self):
super(alerting_system_threshold_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._oversold = self.Param("Oversold", 30.0)
self._overbought = self.Param("Overbought", 70.0)
self._prev_rsi = 0.0
self._has_prev = False
@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 Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
def OnReseted(self):
super(alerting_system_threshold_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(alerting_system_threshold_strategy, self).OnStarted2(time)
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
if self._has_prev:
if self._prev_rsi < self.Oversold and rsi_val >= self.Oversold and self.Position <= 0:
self.BuyMarket()
elif self._prev_rsi > self.Overbought and rsi_val <= self.Overbought and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi_val
self._has_prev = True
def CreateClone(self):
return alerting_system_threshold_strategy()