大QMT实时行情推送教程:xtquant_big_convert的subscribe_whole_quote如何实现零丢失增量推送

原创2026-10-26 20:47:522,158 阅读
文章标签:金融科技后端RPC框架

大QMT实时行情推送教程:xtquant_big_convert的subscribe_whole_quote如何实现零丢失增量推送

xtquant_big_convert 是把大 QMT 接口远程化、并兼容 MiniQMT(xtquant)方法名的桥接工具。本文带你用它内置的 subscribe_whole_quote 真推送能力,实现「订阅即收、增量更新、断线自愈」的大QMT实时行情推送,并解析它为何能做到零丢失、零乱序。

一、为什么实时行情推送很难做对?

普通用户写量化程序时,行情获取通常有两种姿势:

  • 轮询快照:不停调用 get_full_tick,每秒拉一次全量 tick——简单粗暴,但带宽浪费大,且每次都是全量;
  • 事件订阅:调用 subscribe_whole_quote(code_list, callback),行情变化时由服务端主动回调,只推「有变化的品种」。

后者的体验接近交易所逐笔,但要做到「零丢失」并不简单,至少有三个大坑:

  1. 大 QMT 的回调是增量的——只推发生变化的标的,订阅后不会立刻给全量,首帧会「空窗」;
  2. 客户端掉线期间的推送会丢——行情语义上丢一帧不致命(等下一帧即可),但本地状态必须能重建;
  3. 服务端重启会丢掉所有订阅——客户端若不会自动重放,推送就永久中断。

xtquant_big_convert 的 subscribe_whole_quote 实现逐条解决了这三个问题。它的完整设计见 docs/SUBSCRIBE_WHOLE_QUOTE_PUSH.md,真机联调数据见 docs/SUBSCRIBE_WHOLE_QUOTE_LIVE_VERIFICATION.md。

二、整体架构:控制面与数据面分离

整个推送链路拆成三条通道,职责清晰:

client(你的策略程序)
 ├─ 控制面 RPC ──────────► 服务端 QuoteSubscriptionManager(引用计数订阅)
 │   subscribe / unsubscribe / quote_keepalive
 ├─ 数据面推送 ◄────────── 服务端 QuotePusher(PUB/SUB 通道,msgpack 编码)
 └─ 大 QMT 行情源 ◄─────── ContextInfo.subscribe_whole_quote 回调
通道 作用 实现
控制面 RPC 管理订阅生命周期与心跳 复用现有 redis / zmq transport
数据面推送 承载实时行情帧 quote_push_channel.py 的 PUB/SUB 抽象
大 QMT 行情源 真正产生回调的源头 quote_subscription_manager.py

多客户端共享订阅是这套设计的一大亮点:两个 client 订阅同一组标的(如都订 ["SH","SZ"]),服务端只会向大 QMT 建一个订阅,行情只回调一次、广播给所有订阅者——既省 QMT 端资源,又天然避免重复推送。

三、三步上手:最快速的订阅方法

1. 安装客户端

pip install xtquant-big-convert

建议全市场推送加装 msgpack 可选依赖(未装时自动退化为 json 编码,功能不受影响)。

2. 写一个回调并订阅

from bigqmt_signal_trader.xtquant_compat import configure, xtdata

configure()  # 加载配置(参考 bigqmt_signal_trader_client_config.example.py)

def on_quote(data):
    # data 形如 {code: {lastPrice, volume, bid/ask 五档, ...}}
    for code, tick in data.items():
        print(code, tick.get("lastPrice"))

seq = xtdata.subscribe_whole_quote(["600000.SH", "000001.SZ"], callback=on_quote)
  • 参数与 MiniQMT 完全一致,老代码可平移;
  • 全市场订阅用板块代码:["SH"]、["SZ"]、["SH","SZ"](也支持 "BJ"、"HK");
  • 返回值 seq 是订阅号,之后可用 xtdata.unsubscribe_quote(seq) 退订。

3. 停止推送

xtdata.unsubscribe_quote(seq)  # 返回 0,对齐 miniqmt 语义

四、零丢失的四个关键机制 🔑

1. 初始全量打底(prime)

大 QMT 的全推回调是增量的:只推变化品种,不保证订阅后立即给全量。xtquant_big_convert 在订阅成功后会主动调一次 get_full_tick 触发回调打底,随后再由增量推送驱动——既保留了 miniqmt「订阅即给一帧」的体验,又对齐了增量的语义。

这个打底过程还有不少性能细节:标的数超过 100 只时,会改走「交易所令牌」路径整所拉取再过滤,避免阻塞 QMT 的 adjust 线程数秒(见 xtquant_compat.py 中 subscribe_whole_quote 的注释)。

2. 心跳保活 + 超时回收

  • 客户端每 3 秒(可由 BIGQMT_QUOTE_HEARTBEAT_SECONDS 调整)发一次 quote_keepalive 心跳;
  • 服务端连续 10 个心跳周期(约 30s) 收不到,判定该 client 消亡,自动移除引用;
  • 组合内所有 client 都超时/退订后,才真正退订大 QMT。

心跳与数据面解耦:即使行情静默(比如盘后),心跳照发,引用计数始终准确。

3. 断线自动重放

服务端重启后订阅表是空的,推送会断。客户端内置推送静默检测:订阅期间超过约 30s 无任何推送帧到达,自动对本地所有活跃订阅重放 subscribe_whole_quote(重放是幂等的,不会重复建大 QMT 订阅)。该逻辑位于 whole_quote_session.py 的心跳循环中,真机实测服务端重启后约 42s 内自动恢复推送。

4. 高效编码 + 按组合广播

  • payload 用 msgpack 序列化(对 {code: tick} 结构比 json 快数倍、体积更小),未装时退化 json;
  • 服务端按组合 topic 广播(zmq PUB/SUB 或 redis pub/sub),订同一 topic 的所有客户端都能收到;
  • 行情线程回调通过队列 + 专属发送线程串行化,避免跨线程并发写 socket 竞态。

五、真机实测数据:3 秒一帧,零丢失零乱序

以下是 docs/SUBSCRIBE_WHOLE_QUOTE_LIVE_VERIFICATION.md 中交易日真实联调的结果:

测试规模 结果
单标的 60s 21 笔推送,平均间隔 2.96s,volume/amount/time 单调性零违规
20 只活跃股 120s 每只 41~42 次推送,最大间隔 3.3s,gap>4s 为 0
100 只标的 订阅 2.1s,60s 内 336 次增量推送,覆盖 100/100,零错误
双 client 同组合 服务端只建 1 个订阅,各自稳定收推;一方退订不影响另一方
服务端重启 推送静默检测自动重放,中断 42s 后恢复
心跳超时回收 kill 客户端不发退订 → 30s 后被服务端回收,重连安全

一句话总结:每 3 秒一份增量推送、零丢失、零乱序、零数据回退。

六、常用配置速查

客户端(环境变量或 client 配置文件):

配置 默认 说明
BIGQMT_QUOTE_CLIENT_ID 自动生成并持久化 client 唯一 id,重启稳定,用于重放识别
BIGQMT_QUOTE_HEARTBEAT_SECONDS 3.0 心跳周期,须小于服务端超时阈值

服务端(策略侧 config["quote_push"]):

配置 默认 说明
enabled true 是否启用全推推送服务
heartbeat_timeout_seconds 30.0 无心跳超时阈值(= 10 个心跳周期)
zmq_bind_address RPC zmq 端口 + 1 zmq PUB 绑定地址(仅 zmq 部署)

⚠️ 多机部署提示:跨机器共用同一份配置时,请为每个进程各设一个 BIGQMT_QUOTE_CLIENT_ID,避免被服务端识别成同一个订阅者(详见 docs/USER_GUIDE.md 的多进程章节)。

七、常见问题 FAQ

Q1:订阅后第一帧迟迟不来? 先确认配置里 redis/zmq 链路正常;再确认 get_full_tick 能否返回数据。打底快照失败时回调仍会被触发一次(哪怕为空 dict),不会让等待首帧的策略死等。

Q2:期权(.SHO/.SZO)收不到推送? 部分大 QMT 版本中,显式期权合约不会从 subscribe_whole_quote 推送。xtquant_big_convert 已做兼容:服务端对 .SHO/.SZO 代码逐合约改用 ContextInfo.subscribe_quote(..., result_type="list"),客户端仍只看到一个订阅号,ETF + 期权混合组合也能正常收推。

Q3:和 full_tick_cache(get_full_tick 轮询缓存)是什么关系? 二者独立、互不冲突:full_tick_cache 服务 get_full_tick 按需拉取快照,subscribe_whole_quote 服务事件推送。低延迟策略用推送,低频快照用 get_full_tick,可各取所需。

Q4:推送丢一帧要紧吗? 行情推送是 fire-and-forget 语义:丢掉的增量帧等下一帧即可,无需逐条补发。真正要防的是「本地状态失真」,这正是「全量打底 + 增量更新 + 断线重放」三件套要解决的问题。

八、相关资源

按上述配置跑通后,你的策略代码可以完全沿用 MiniQMT 的 subscribe_whole_quote 写法,底层却跑在大 QMT 之上——迁移成本接近于零,行情体验却是「3 秒一帧、零丢失」的真推送。

登录后查看全文
xtquant_big_convert