在 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 中拖动水平线那样随时修改阈值。

执行流程

  1. 启动后调用 SubscribeLevel1().Bind(ProcessLevel1).Start() 订阅 Level 1 数据。
  2. 每条 Level1ChangeMessage 更新缓存的最优买价和卖价。
  3. 更新后检查上下提示条件:
    • 上方提示:当 BestBid >= UpperPrice 且之前价格低于该阈值时触发一次。
    • 下方提示:当 BestAsk <= LowerPrice 且之前价格高于该阈值时触发一次。
  4. 当市场回到区间内时,提示标志会重置,等待下一次突破。

日志与扩展

突破信息通过 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;
	}
}