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_data 或 get_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