subscribe 是 QMT 内置 Python 的事件驱动机制,通过 subscribe_quote 订阅指定品种的实时行情数据,在数据更新时触发回调函数。与 handlebar 的逐 K 线驱动不同,subscribe 可以在任意时刻响应行情变化,适用于实时监控和多品种跟踪场景。

QMT subscribe 与 handlebar 的区别

handlebar 是逐 K 线驱动的回调函数,按 K 线周期触发;subscribe 是事件驱动的机制,通过订阅行情数据在数据更新时触发回调。两者的触发逻辑、频率和适用场景存在本质区别。

handlebar 绑定于主图品种的 K 线周期,触发时机由 K 线完成或更新决定。subscribe 独立于 K 线周期,只要订阅的品种有行情数据更新就会触发回调。这意味着 subscribe 可以实现比 K 线周期更细粒度的监控,例如在 Tick 级别响应价格变化。两者可以同时使用,互不冲突。

小建议 如果策略逻辑基于 K 线形态或指标,使用 handlebar 即可。如果需要实时监控价格变化、买卖盘变化或跨品种联动,使用 subscribe 更合适。复杂策略可以同时使用两者,在 handlebar 中做信号计算,在 subscribe 回调中做实时风控。

对比维度 handlebar subscribe
驱动方式 逐 K 线驱动 事件驱动
触发依据 主图品种 K 线更新 订阅品种行情数据更新
触发频率 由 K 线周期决定 由行情更新频率决定
品种范围 主图品种(可读多品种数据) 可订阅任意品种
回测支持 支持 不支持
典型场景 K 线指标计算、信号生成 实时价格监控、多品种跟踪

QMT subscribe_quote 的使用场景

subscribe_quote 用于订阅指定品种的实时行情数据。调用后在 init 中注册订阅,当订阅品种的行情数据更新时,指定的回调函数被触发。subscribe_quote 仅在仿真交易环境和真实交易环境中有效,不适用于回测环境。

subscribe_quote 的典型使用场景包括:实时监控非主图品种的价格变化、跟踪多只品种的买卖盘、在价格触及阈值时立即响应而非等待 K 线完成。订阅后回调函数会被异步调用,与 handlebar 的执行相互独立。

小建议 在 init 中完成订阅注册,确保策略启动后立即开始接收行情。回调函数中避免执行耗时操作,以免阻塞行情接收。如果回调中需要下单,注意使用正确的 quickTrade 参数(参见 quickTrade 概念文档)。

def init(ContextInfo): # 订阅单个品种的实时行情 ContextInfo.subscribe_quote('000001.SZ', 0, 'on_quote', ContextInfo) print('行情订阅完成') def on_quote(ContextInfo, accountid, tick): # 行情数据更新时触发 print(f'{tick.m_strCode} 最新价: {tick.m_dLastPrice}')

QMT subscribe 回调函数的参数与触发时机

subscribe_quote 的回调函数接收三个参数:ContextInfo(上下文对象)、accountid(账户 ID)、tick(行情数据对象)。回调在订阅品种的行情数据每次更新时触发,触发频率取决于行情推送频率。

tick 对象包含了订阅品种的最新行情快照,包括最新价、开高低收、成交量、买卖盘等字段。回调的触发频率与行情推送频率一致,在交易活跃时段可能非常频繁。回调函数与 handlebar 在同一线程中执行,回调期间的耗时操作会阻塞后续回调。

小建议 回调函数应保持轻量,只做数据记录和简单判断,复杂计算留给 handlebar 或定时任务处理。如果回调中需要修改共享状态,注意与 handlebar 的执行顺序可能产生竞争。

def init(ContextInfo): ContextInfo.subscribe_quote('000001.SZ', 0, 'on_quote', ContextInfo) ContextInfo.price_history = [] def on_quote(ContextInfo, accountid, tick): # 记录最新价格 last_price = tick.m_dLastPrice ContextInfo.price_history.append(last_price) # 保持列表长度合理 if len(ContextInfo.price_history) > 100: ContextInfo.price_history.pop(0) # 简单价格监控 if last_price > 0: print(f'最新价: {last_price}')

QMT 订阅数量与性能的关系

每增加一个 subscribe_quote 订阅,都会增加行情回调的处理负担。订阅数量过多会导致回调函数频繁触发,占用大量 CPU 资源,可能影响 handlebar 的正常执行和策略整体性能。

假设订阅了 50 只品种,每只品种每秒推送 2 次行情,则每秒会产生 100 次回调。如果每次回调耗时 10 毫秒,则每秒消耗 1 秒 CPU 时间,回调将无法及时处理完所有行情。此外,大量的 print 输出也会显著拖慢策略运行速度。

小建议 控制订阅数量在合理范围内,通常不建议超过 20 只品种。对不需要实时监控的品种,使用 handlebar 中的 get_history_dataget_market_data 按需获取数据即可。回调中避免使用 print 输出高频数据,改用状态标记在 handlebar 中统一输出。

def init(ContextInfo): # 只订阅需要实时监控的关键品种 watch_list = ['000001.SZ', '600000.SH'] for stock in watch_list: ContextInfo.subscribe_quote(stock, 0, 'on_quote', ContextInfo) # 其他品种通过 handlebar 按需获取 ContextInfo.set_universe(['000002.SZ', '000333.SZ', '600519.SH']) ContextInfo.alert_triggered = False def on_quote(ContextInfo, accountid, tick): # 轻量回调:只做标记,不做复杂计算 if tick.m_dLastPrice > 100 and not ContextInfo.alert_triggered: ContextInfo.alert_triggered = True # 实际处理留给 handlebar

QMT 取消订阅的方法

可以通过 unsubscribe_quote 取消已订阅的行情数据。取消订阅后,该品种的行情更新不再触发回调。策略停止时所有订阅会自动取消。

在策略运行过程中,如果某个品种不再需要实时监控,可以动态取消订阅以释放资源。这在品种轮换策略或条件性监控场景中很有用。取消订阅后,已注册的回调函数不会再被该品种的行情触发。

小建议 在策略逻辑中动态管理订阅,订阅不再需要的品种及时取消。避免在策略运行期间不断新增订阅而不取消,导致订阅数量累积影响性能。

def init(ContextInfo): ContextInfo.subscribe_quote('000001.SZ', 0, 'on_quote', ContextInfo) ContextInfo.subscribed = True def handlebar(ContextInfo): if not ContextInfo.is_last_bar(): return # 满足条件后取消订阅 if ContextInfo.subscribed and some_condition(ContextInfo): ContextInfo.unsubscribe_quote('000001.SZ') ContextInfo.subscribed = False print('已取消订阅 000001.SZ') def some_condition(ContextInfo): # 示例条件 return ContextInfo.get_close_price() > 15

QMT subscribe 的适用场景

subscribe 机制适用于以下场景:实时价格监控(价格触及阈值时立即响应)、多品种跟踪(同时监控多只品种的行情变化)、买卖盘监控(实时跟踪盘口变化)、跨品种联动(根据一个品种的行情变化操作另一个品种)。

这些场景的共同特点是需要在 K 线周期之外实时响应行情变化。例如,止损逻辑如果只在 handlebar 中执行,可能因为 K 线未完成而延迟响应;使用 subscribe 可以在价格触及止损位时立即触发,减少滑点。再如,多品种套利策略需要同时监控多个品种的价差,subscribe 可以实时计算价差变化。

小建议 根据策略需求选择合适的驱动方式。止损止盈等需要快速响应的逻辑适合用 subscribe;基于 K 线形态、均线等指标的交易逻辑适合用 handlebar;盘前检查、定时调仓等固定时间执行的任务适合用 run_time。三者配合使用可以构建完整的策略框架。

def init(ContextInfo): # subscribe 用于实时止损监控 ContextInfo.subscribe_quote('000001.SZ', 0, 'on_quote', ContextInfo) ContextInfo.stop_loss_price = 10.0 ContextInfo.position = 0 def on_quote(ContextInfo, accountid, tick): # 实时止损:价格触及止损位立即响应 if ContextInfo.position > 0 and tick.m_dLastPrice <= ContextInfo.stop_loss_price: print(f'触发止损!最新价: {tick.m_dLastPrice}') # 下单逻辑(注意使用 quickTrade=2) # ContextInfo.passorder(..., 2, ContextInfo) ContextInfo.position = 0 def handlebar(ContextInfo): if not ContextInfo.is_last_bar(): return # handlebar 用于信号计算和建仓 close = ContextInfo.get_close_price() closes = ContextInfo.get_history_data('close', 20) if closes and len(closes) >= 20: ma20 = sum(closes) / len(closes) if close > ma20 and ContextInfo.position == 0: print(f'买入信号,收盘价: {close}, MA20: {ma20}') ContextInfo.position = 100