在 GitHub 上查看

Alerting System 策略是 MetaTrader 4 顾问 AlertingSystem.mq4 在 StockSharp 平台上的精确移植版。原始脚本会绘制两条水平线,当价格触碰线条时播放提示音。StockSharp 实现通过订阅 Level1(最优买/卖)行情,并在价格越过预设水平时向日志写入提示,从而复现相同的监控流程。

核心思路

  1. 订阅 Level1 数据流,保证策略像 MQL4 中的 OnTick 一样在每个报价到来时获得买价和卖价。
  2. 读取用户设置的 UpperPriceLowerPrice 参数。数值为 0 表示关闭对应的预警,相当于在 MetaTrader 中删除该水平线。
  3. 将每个最新买价与上方水平比较,同时将最新卖价与下方水平比较。
  4. 当行情首次越过某个启用的水平时输出一条日志提示,并等待价格回到安全区后再次“上膛”,避免重复刷屏但仍保留原始脚本的提醒含义。

参数

名称 默认值 说明
UpperPrice 0 上方水平预警价。设置为 0 时禁用。
LowerPrice 0 下方水平预警价。设置为 0 时禁用。

参数可以在 Designer 界面中配置,也可以在运行时修改。新的报价到达后即会采用最新数值。

运行特性

  • 数据订阅GetWorkingSecurities 请求 Level1 流,因此即便没有蜡烛或成交明细,策略仍能获得最新报价。
  • 初始化日志OnStarted 会记录当前配置的上下水平,方便操作者核对。
  • 预警判定CheckUpperAlertCheckLowerAlert 使用内部布尔标志,确保价格突破某条水平线时只输出一次提示,直到行情回到原有区间。
  • 无交易指令:此移植版不下单,仅用于提醒,完全符合原始顾问只播放声音的定位。
  • 复位OnReseted 会清空内部标志,下一次启动从干净状态重新计数。

推荐使用流程

  1. 在 StockSharp Designer 中选择目标标的并附加 AlertingSystemStrategy
  2. 设定上方和/或下方预警价,若不需要某一侧则保持数值为 0
  3. 启动策略。日志窗口会显示哪些预警已启用。
  4. 关注日志输出:当买价突破上方水平或卖价跌破下方水平时,将出现描述性的提醒信息。

转换说明

  • MetaTrader 允许用鼠标拖动水平线。StockSharp 版本改为显式参数,便于重复执行并适应算法化环境。
  • 原脚本在符合条件的每个 tick 都会调用 PlaySound。为了避免日志泛滥,此实现加入“防抖”逻辑,等待价格回到水平线之外后再重新触发。
  • 策略没有使用任何指标,仅依赖原始报价,因此适用于任何能够提供 Level1 数据的品种和周期。

分类

  • 类别:工具 / 预警
  • 交易方向:无
  • 执行方式:事件驱动监控
  • 数据需求:Level1 买卖价
  • 复杂度:基础
  • 推荐周期:任意(基于报价)
  • 风险管理:不适用(不持仓)

以上内容展示了在 StockSharp 中如何还原 MetaTrader 的预警顾问,并提供了实际部署时的操作要点。

namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Alerting System strategy: Bollinger Band breakout.
/// Buys when price crosses above upper band, sells when below lower band.
/// </summary>
public class AlertingSystemStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _bbPeriod;
	private readonly StrategyParam<decimal> _bbWidth;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
	public decimal BbWidth { get => _bbWidth.Value; set => _bbWidth.Value = value; }

	public AlertingSystemStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_bbPeriod = Param(nameof(BbPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
		_bbWidth = Param(nameof(BbWidth), 2m)
			.SetDisplay("BB Width", "Bollinger Bands width multiplier", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		var bb = new BollingerBands
		{
			Length = BbPeriod,
			Width = BbWidth
		};
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(bb, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!bbValue.IsFinal) return;

		var typed = (BollingerBandsValue)bbValue;
		if (typed.UpBand is not decimal upper || typed.LowBand is not decimal lower) return;

		var close = candle.ClosePrice;

		// Mean reversion: buy at lower band, sell at upper band
		if (close < lower && Position <= 0)
			BuyMarket();
		else if (close > upper && Position >= 0)
			SellMarket();
	}
}