Files
qmt_bridge/docs/设计/整体设计方案_v3.md
T

26 KiB

QMT Bridge 整体设计方案(统一单端口 8610)

版本: v3(2026-08-19, 2026-08-26 整合环境调研/最终方案) 状态: 实施中(WS 单通道推送一期已完成,见设计变更记录)


一、设计目标

  1. 复刻旧桥接口:把 miniqmt_bridge/bridge.py(FastAPI, 8610)的 9 个接口 1:1 覆盖,旧脚本零改动
  2. 新增订阅与推送:HTTP 订阅(单票/全量)+ WS 推送(行情 + 交易结果)
  3. 统一单端口:HTTP + WebSocket 都走 8610(性能无影响,技术问题可解,见下)
  4. 跑在大 QMT 内:策略编辑器加载,Python 3.6.8,纯标准库零依赖
  5. 薄桥原则: 桥只做协议转换 + 透传,QMT 统一处理业务逻辑。不追求大并发时不做订阅合并/缓存/过滤等自研逻辑,订阅、回调、数据处理全部透传给大 QMT(官方:多策略订阅同品种计数不累加,即 QMT 已内置合并)
  6. 实施路径(2026-08-20 定): 最小骨架先行(/health + /data/kline + /data/quote + WS 连接/心跳),验证整条链路;订阅端点、交易接口、WS 推送后续增量添加

1.1 命名空间(2026-08-19 定稿)

命名空间 内容 官方依据
data 行情、K线、板块、日历、合约信息、快照、订阅 xtquant.xtdata(行情中心)
trade 持仓、资金、委托、成交、下单、撤单 xtquant.xttrader(交易中心)
通用 健康检查 -

新旧并存: 旧桥无前缀路径保留(内部转发),新接口用 /data/* /trade/*


一之二、QMT 环境调研(设计依据,2026-08-19 实测)

整合自 QMT内置Python环境与pip扩展调研.md(已删除,内容并入本设计文档)。 QMT 安装路径: C:\Programs\GJQMT_BIG(桌面快捷方式指向 bin.x64\XtItClient.exe)

本机两个 QMT 实例

C:\Programs\GJQMT C:\Programs\GJQMT_BIG(★ 桥运行目标)
身份 miniQMT 版(有 XtMiniQmt.exe + userdata_mini) 大 QMT 完整版(纯交易端)
Python 极简嵌入式(pythonw + python36.dll,无 site-packages) 完整 3.6.8 + 101 包
不在此跑(miniQMT 关停中) 策略编辑器 + 桥都跑这里

内置 Python 环境(实测)

解释器 C:\Programs\GJQMT_BIG\bin.x64\pythonw.exe(嵌入式,无 python.exe/pip.exe)
版本 Python 3.6.8 (Dec 19 2022) 64位
发行包 101 个(site-packages 实测)
pip 21.0.1(内置,可导入)
threading 实测可用(线程能起)
调用方式 pythonw.exe -c "..."(GUI 子系统,无 stdout/stderr,需重定向到文件/StringIO)

已内置关键包(桥方案相关)

  • 通信: redis 3.5.3 、pyzmq 18.0.1 、requests 2.24.0、tornado 6.0.2
  • 科学计算: numpy 1.19.1、pandas 0.22.0、scipy 1.5.2
  • ML: scikit-learn 0.23.2、tensorflow 1.8.0、torch 1.0.1、talib 0.4.17
  • 数据库: pymongo 3.11.3、pymssql 2.1.4

pip 扩展能力(实测可用)

  • 结论: 可以用 pip 安装外部包(pythonw 无控制台需先设 stdout/stderr 代理,否则报 'NoneType' object has no attribute 'write')
  • 实测: pip.main(["install","--target",target,"flask==2.0.3"]) → exit 0,安装成功并可 import
  • 方式 A: --target 装到独立目录(推荐,不污染 QMT);方式 B: 直接装进 site-packages(需管理员)
  • ⚠️ 注意: 只能装支持 cp36 的包;SSL 旧,HTTPS 可能失败(用 --trusted-host 或国内镜像);C 扩展包需 cp36 wheel

对本方案的意义

  1. 零依赖方案成立: 纯标准库即可实现桥(当前实现);redis/pyzmq 内置可作备选传输
  2. HTTP 桥可扩展: 可 pip 装 flask/websockets 等
  3. 线程实测可用: 多线程在 QMT 内 OK(线程池方案成立)
  4. 策略可上 ML: talib/tensorflow/torch 内置

二、总体架构

┌──────────────────── 大 QMT 进程(策略) ────────────────────┐
│  qmt_bridge.py (统一入口, 薄桥)                            │
│  ├─ init(ContextInfo)                                      │
│  │    ├─ set_account / GIL 调优                            │
│  │    ├─ 启动 8610 统一服务器(HTTP + WS 共端口)             │
│  │    └─ run_time("adjust", 5ms) 注册定时器                │
│  │                                                         │
│  ├─ HTTP server (ThreadingMixIn, 8610)                     │
│  │    ├─ /data/* 查询 + 订阅                                │
│  │    ├─ /trade/* 交易查询                                  │
│  │    └─ Upgrade: websocket → WS 帧循环 (RFC6455)          │
│  │                                                         │
│  ├─ WS 推送 (单通道 /ws, 按 type 区分)                      │
│  │    ├─ 行情推送: type=whole (全量tick增量, 已实现)         │
│  │    └─ 交易结果推送: type=trade_result (预留未实现)        │
│  │                                                         │
│  ├─ 订阅管理 (bridge_subscription.py, 单客户端)             │
│  ├─ adjust() 定时器 → 下单/撤单队列执行 (passorder/cancel)   │
│  └─ 回调: order_callback / deal_callback → 回写 + WS 推送    │
└────────────────────────────────────────────────────────────┘
         │ 8610 (HTTP + WS 同端口)
         ▼
外部: sfgrid / monitor_signals / 策略脚本 / AI 助手

注: 调度周期由 v3 初稿的 500ms 更新为 5ms(见"二之二、架构决策与性能实测")。


二之二、架构决策与性能实测(2026-08-20 定稿,整合自最终方案)

架构决策:自研 HTTP 桥(线程池 + 高频调度),不用 big_convert

经过多轮实测,最终确定:在 QMT 策略内跑自研 HTTP 桥,TS 客户端直接 fetch。

为什么不用 big_convert:

  • big_convert 的 ZMQ 方案成熟,但要写 TS ZMQ library(协议复杂,2-4 周)
  • HTTP 方案 TS 直接 fetch,零协议成本
  • 关键突破:发现"线程池 + 高频 run_time 调度"能让 QMT 内 HTTP 变快

性能实测数据(5ms 调度 + 8 线程池)

调度周期 平均延迟
500ms(默认) 500-2500ms
50ms 62.6ms
5ms 28.2ms(最快 7ms)
接口 速度 真实数据
/health 24-57ms -
/data/quote 29-34ms 真实行情
/data/kline 正常 真实K线

关键技术发现(QMT 环境特性)

  1. 线程创建慢,复用快 → 用线程池(预建 8 线程)
  2. run_time 调度周期决定线程调度频率 → 周期越短,线程响应越快
    • 500ms 周期 → 线程每 500ms 才被调度(慢)
    • 5ms 周期 → 线程每 5ms 被调度(快)
  3. 但周期太小有风险(big_convert 警告):adjust 热循环占 GIL,饿死后台线程
    • 5ms = 200次/秒,远低于危险的 2500次/秒,实测稳定
  4. 行情读取线程安全(后台线程直接调 get_full_tick 快)
  5. 交易查询需要主线程(实测: get_trade_detail_data 在 HTTP 线程报 'NoneType' object has no attribute 'request_id' → 用策略线程缓存/队列方案)

最终架构

TS 客户端(DSH 插件)
   │  HTTP fetch(简单)
   ▼
QMT 策略内 HTTP 桥
   ├─ 线程池 8 线程(预建,复用)
   ├─ run_time 5ms 调度
   ├─ 单端口 8610
   └─ data/trade 接口 + WS 单通道推送
   │  ContextInfo/passorder
   ▼
大QMT

三、单端口(8610)方案分析

3.1 原理:WebSocket 握手就是 HTTP

客户端: GET /ws HTTP/1.1
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: xxx

服务端: 请求头含 Upgrade: websocket → 回 101 → 进入 WS 帧循环 否则 → 普通 HTTP 处理

3.2 性能影响:无

维度 分析
连接模型 HTTP 已是 ThreadingMixIn(每连接线程);WS 也是每连接线程 → 合并后线程模型不变
握手成本 WS 握手 = 一次 HTTP 请求,同成本
分流开销 Upgrade 头判断是 O(1)
GIL 同进程,合并不改变竞争;已 setswitchinterval(0.001)
吞吐 单端口 accept + 双 handler,与双端口等价

3.3 潜在技术问题与解法

问题 影响 解法
HTTP 短连接 vs WS 长连接超时差异 HTTP server 的 socket 配置可能误伤 WS WS 升级后 socket 单独 settimeout(2)(_client_loop 已有)
鉴权统一 WS 握手也能带 header 统一走 HTTP 层 token 检查(Upgrade 前)
连接泄漏 WS 断开要清理 _mark_dead 已有(引用计数拆除)

3.4 结论:单端口可行


四、HTTP 接口(8610,复刻旧桥 9 接口 + data/trade 前缀)

详细规格见 旧桥接口规格对照.md;此处列结构。 命名空间: 大 QMT/xtquant 官方即分 data(行情) / trade(交易)两套,故接口加前缀。

4.0 命名空间设计(官方依据)

命名空间 内容 依据
data 行情、K线、板块、日历、合约信息、快照、订阅 官方 xtquant.xtdata(行情中心)
trade 持仓、资金、委托、成交、下单、撤单 官方 xtquant.xttrader(交易中心)
通用 健康检查 -

新旧并存策略: 旧桥无前缀路径(/kline 等)保留,内部转发到新前缀;新接口统一用 /data/* /trade/*

data 命名空间(行情/数据)

方法 新前缀路径 旧兼容路径 底层大QMT函数
GET /data/kline /kline ContextInfo.get_market_data_ex
GET /data/kline/batch /kline/batch 同上(多代码)
POST /data/kline/download /kline/download 全局 download_history_data
GET /data/instrument /instrument ContextInfo.get_instrument_detail
GET /data/calendar/trading_dates /calendar/trading_dates ContextInfo.get_trading_dates
GET /data/sectors /sectors ContextInfo.get_sector_list
GET /data/sectors/stocks /sectors/stocks ContextInfo.get_stock_list_in_sector
GET /data/stocks/delisted /stocks/delisted 板块差集 + 本地目录 + instrument
GET /data/quote /quote ContextInfo.get_full_tick(实时快照)

trade 命名空间(交易)

方法 路径 底层大QMT函数
GET /trade/positions get_trade_detail_data(ACCOUNT,'position')
GET /trade/asset get_trade_detail_data(ACCOUNT,'account')
GET /trade/orders get_trade_detail_data(ACCOUNT,'order')
GET /trade/trades get_trade_detail_data(ACCOUNT,'deal')
POST /trade/order passorder(队列→adjust)
POST /trade/cancel cancel(队列→adjust)
GET /trade/order/status ORDER_RESULTS 查询(轮询兜底,推荐用 WS 收结果)

订阅端点(HTTP,2026-08-19 定稿)

方法 路径 body 返回
POST /data/subscribe {"code":"600519.SH","period":"1d","client_id":"..."} {"ok":true,"sub_id":123}
POST /data/subscribe_whole {"codes":["SH","SZ"],"client_id":"..."} {"ok":true,"sub_id":124}
POST /data/unsubscribe {"sub_id":123} {"ok":true}

订阅是 HTTP 请求-响应;行情推送走 WS(需先建立 WS 连接并带同 client_id)。详见第五节。

通用

方法 路径 说明
GET /health 健康检查

错误格式统一: {"detail": "..."} + 400/404/500/503(与旧桥一致)

4.1 K 线周期合成规则(官方,直接影响 /kline 实现)

基础周期(实际存储): tick1m5m1d

合成周期:

  • 3m ← 由 1m 合成
  • 10m, 15m, 30m, 60m, 2h, 3h, 4h ← 由 5m 合成
  • 2d, 3d, 5d, 1w(周), 1mon(月), 1q(季), 1hy(半年), 1y(年) ← 由 1d 合成

三条规则:

  1. 取合成周期历史 → 必须先下载基础周期(如取 15m 先下载 5m)
  2. 实时 → 可直接订阅原始周期
  3. 基础+合成混用 → 只下载基础周期一次(5m+15m 只需下载 5m)

/kline 实现映射(旧桥已遵循):

请求周期 底层动作
tick/1m/5m/1d 直接 download + get_market_data_ex
3m 先下载 1m → 取 3m
10m/15m/30m/60m/2h/3h/4h 先下载 5m → 取合成周期
1w/1mon/1q/1hy/1y 先下载 1d → 取合成周期

五、订阅与推送(HTTP 订阅 + WS 回调推送)

5.0 架构原则(2026-08-19 定稿):按协议特性划分

不按 data/trade 划分协议,而是按"请求-响应 vs 推送"划分:

协议 职责 承载
HTTP(请求-响应) 所有主动操作:查询、订阅/退订、下单请求、下载 /data/* /trade/* 全部查询 + 订阅端点
WebSocket(双向长连接) 所有异步回调/推送:行情、交易结果 行情 tick、trade_result、心跳

理由:

  • 订阅是"一次性动作 + 明确返回",天然适合 HTTP(返回订阅号、幂等重试)
  • WS 连接专一化:客户端只收推送,不用发请求(除心跳),连接管理简单
  • 交易结果异步推送(替代 HTTP 轮询 order/status)
  • 符合薄桥原则:桥只做"HTTP 请求→QMT 调用"和"QMT 回调→WS 推送"
HTTP (8610)                             WebSocket (8610 同端口)
├─ POST /data/subscribe     订阅单票 ─►  行情推送 {type:"quote",...}
├─ POST /data/subscribe_whole 全量 ──►  行情推送 {type:"whole",...}
├─ POST /data/unsubscribe   退订
├─ GET  /data/kline         查询K线
├─ POST /trade/order        下单 ────►  交易结果 {type:"trade_result",rid,...}
├─ POST /trade/cancel       撤单 ────►  交易结果 {type:"trade_result",rid,...}
└─ ... 其余查询/操作                      只有:行情推送 + 交易结果 + 心跳

5.1 订阅关联机制(HTTP 订阅 ↔ WS 推送)

核心问题: HTTP 订阅后,行情推给哪个 WS 连接?

采用方案:client_id 绑定

1. 客户端建立 WS 连接,握手带 client_id(或首个消息声明):
   ws://host:8610/ws?client_id=my-app-1   (或 X-Client-Id 头)

2. 客户端 HTTP 订阅时带 client_id:
   POST /data/subscribe  body: {"code":"600519.SH","period":"1d","client_id":"my-app-1"}

3. 桥记录: 订阅(sub_id) → client_id → WS 连接
   QMT 回调 → 查订阅表 → 按 client_id 找 WS 连接 → 推送

关联表:

_SUBS[key]     = {qmt_subid, refcount, clients: {client_id}}
_WS_CONN[conn] = {client_id, alive, subs: set(key)}
  • 同一 client_id 可多个 WS 连接(负载/重连),推送广播到该 client_id 的所有连接
  • 无 WS 连接时订阅仍生效(数据缓存),连接建立后补推? (MVP: 不补推,客户端重连后重新订阅)

5.2 HTTP 订阅端点

方法 路径 body 返回
POST /data/subscribe {"code":"600519.SH","period":"1d","client_id":"..."} {"ok":true,"sub_id":123}
POST /data/subscribe_whole {"codes":["SH","SZ"],"client_id":"..."} {"ok":true,"sub_id":124}
POST /data/unsubscribe {"sub_id":123}{"code":"...","period":"...","client_id":"..."} {"ok":true}
  • 单票订阅透传 → ContextInfo.subscribe_quote(code, period, cb)
  • 全量订阅透传 → ContextInfo.subscribe_whole_quote(codes, cb)
  • 返回 sub_id(QMT 订阅号或桥生成),用于退订
  • 错误: {"detail":"..."} + 400/404/503

5.3 单票订阅 vs 全量订阅(QMT 官方定义)

前提确认(实盘验证): 大 QMT 的 ContextInfo 订阅逻辑,与 miniQMT 同名但形态不同。 依据 xtquant_big_convert 实盘验证(quote_subscription_manager.py 第 48-51 行):

"Verified against the real environment: ContextInfo.subscribe_whole_quote(code_list, callback) returns an int subscription id (<0 on failure) and pushes INCREMENTAL {code: tick} batches on a dedicated quote thread; ContextInfo.unsubscribe_quote(sub_id) cancels it."

方法 大 QMT 行为
ContextInfo.subscribe_whole_quote(codes, cb) 全量订阅,返回 int 订阅号,失败 <0
ContextInfo.subscribe_quote(code, period, cb) 单票订阅(官方文档确认)
ContextInfo.unsubscribe_quote(sub_id) 反订阅
ContextInfo.get_full_tick(codes) 取当前快照(全推增量前的 prime)

大 QMT vs miniQMT:

miniQMT(xtdata) 大 QMT(ContextInfo)
调用 xtdata.subscribe_quote(...) ContextInfo.subscribe_quote(...)
位置 外部 Python 策略进程内
回调线程 独立行情线程 专用 quote 线程(不阻塞主线程)
连接 连 miniQMT 58610 无需连接,策略内直接用
单票订阅 全量订阅
QMT 函数 ContextInfo.subscribe_quote(code, period, cb) ContextInfo.subscribe_whole_quote(code_list, cb)
数据 指定周期 K 线(分笔/1m/5m/1d) 分笔快照(最新值,无历史)
推送时机 该股行情更新 增量推送有变化的品种
五档盘口 有(视行情权限) 默认无,只有最新价(需开全推行情级别)
数量限制 非VIP ~300 个(按周期累加计数) 不占单股订阅数,无品种上限
数据形态 {code: DataFrame}(K线) {code: {lastPrice, volume, amount, ...}}(tick dict)
典型场景 网格策略盯指定票 全市场监控 / 异动扫描

重要官方细节:

  • 单票订阅同一品种不同周期累加计数(如订阅 1m+5m+1d 算 3 个)
  • 多策略订阅同一品种计数不累加(QMT 内部去重)
  • 全推数据服务器对交易所数据即时转发,打包增量下发
  • 全推默认无五档(需修改行情源的全推行情级别);若要五档需客户端确认权限

5.4 WS 推送协议(服务端 → 客户端)

客户端建立 WS 连接后,主要收推送(除心跳外不发请求):

// 行情推送 - 单票(HTTP 订阅后)
{"type":"quote","code":"600519.SH","data":{"close":[1500.0,1510.0],"volume":[...],"_index":["20260818","20260819"]}}

// 行情推送 - 全量(HTTP 订阅后,增量tick)
{"type":"whole","data":{"600519.SH":{"lastPrice":1500.0,"volume":12345,"amount":...,"askPrice":[...],"bidPrice":[...]}}}

// 交易结果推送(HTTP 下单后,替代轮询)
{"type":"trade_result","rid":"r123","status":"submitted","code":"600519.SH","opType":23,"volume":100}
{"type":"trade_result","rid":"r123","status":"order_update","orderSysId":"SYS1","orderStatus":50,"volumeTraded":50}
{"type":"trade_result","rid":"r123","status":"dealt","price":1500.0,"volume":100,"amount":150000.0}
{"type":"trade_result","rid":"r123","status":"order_error","error":"..."}

// 心跳响应
{"type":"pong","ts":...}

客户端 → 服务端(仅心跳,可选):

{"action":"ping"}

5.5 服务端实现

                        ┌─ 单票订阅(subscribe_quote) ──► QMT回调 → {type:"quote"} 推送
HTTP POST /data/subscribe ──► 订阅表
                        └─ 全量订阅(subscribe_whole_quote) ─► QMT回调 → {type:"whole"} 推送

HTTP POST /trade/order ──► 队列 → adjust → passorder
                              └─ order_callback/deal_callback → {type:"trade_result"} 推送

订阅表(桥内状态):

_SUBS[key] = {qmt_subid, refcount, clients: {client_id}}
key:
  - 单票: ("quote", code.upper(), period)
  - 全量: ("whole", tuple(sorted(codes.upper())))

QMT 回调 → WS 推送转换:

  • 单票回调 data = {code: DataFrame}{"type":"quote","code":code,"data":{col:[vals],"_index":[...]}}
  • 全量回调 data = {code: tick_dict}{"type":"whole","data":{code: tick_dict}}
  • 交易回调(ORDER_RESULTS 更新后)→ {"type":"trade_result","rid":remark,...} 推给该 rid 关联的 client_id

分发:

  • QMT 回调 → 按订阅表找 client_id 集合 → 推给该 client_id 的所有 WS 连接
  • 无 WS 连接在线时:行情丢弃(增量);交易结果存 ORDER_RESULTS(可 HTTP 查)

5.6 边界与注意

  • 订阅上限: 单票超 ~300 时拒绝新订阅,HTTP 返回 {"detail":"sub failed/limit"} + 503
  • 全量五档: 默认只有最新价;若客户端要五档,需 QMT 行情源开启全推行情级别(文档提示)
  • 初次订阅耗时: 单票订阅"初次订阅耗时长",客户端应容忍订阅后首次数据延迟
  • 周期: 单票仅 4 种基本周期(tick/1m/5m/1d)+ L2(有权限);全量只有分笔
  • 并发推送: QMT 回调可能频繁触发,推送用短帧、不阻塞回调;广播失败(客户端断开)静默清理
  • 订阅后先推快照(全量订阅必须,实盘验证): 全推回调是增量的,客户端订阅后第一次要先 get_full_tick([codes]) 拿当前快照,再进入增量推送(xtquant_big_convert 实盘验证了此设计)

5.7 分时以上 K 线订阅的处理(重要设计决策)

依据 xtquant_big_convert 实盘实现: 分时以上 K 线(1m/5m/1d)不做 QMT 推送订阅,而是轮询 get_market_data_ex

源码证据(xtquant_compat.py subscribe_quote):

  • K 线周期的"订阅"回调 = 拉一次 get_market_data_ex 历史
  • 服务端 save_quote_subscription 只登记状态,不建立 K 线推送通道
  • 客户端靠自己的定时器反复拉最新 K 线

原因:

  • K 线是聚合数据,1m 一分钟一根、1d 一天一根,轮询成本极低
  • QMT 单票 subscribe_quote 有 ~300 上限,不值得为 K 线占配额
  • 全推只有分笔,给不了 K 线

我们的桥对应设计:

数据 方式 通道
分笔/tick(实时) QMT 全推真推送 WS(HTTP 订阅后推送)
1m/5m/1d K线 轮询 get_market_data_ex HTTP /kline(客户端主动拉)
K线推送式(可选) 桥内定时器轮询 → 推 WS WS (type=kline, 桥内周期拉取)

决策: MVP 阶段 K 线走 HTTP /kline 轮询(简单、不占配额);若客户端需要推送式 K 线,再加桥内周期轮询推送(第 3 行)。


六、模块结构(薄桥版)

决策: 保持单文件(qmt_bridge.py),因为:

  • QMT 策略编辑器加载单文件最简单(一个文件一个策略)
  • 薄桥逻辑简单,拆多个文件反而增加 QMT 目录同步负担
  • 后续需要再拆(WS 复杂化时)
qmt_bridge.py   统一入口 + HTTP handler + WS 订阅 + 下单队列(薄桥,全部透传)

6.1 单文件内部结构

qmt_bridge.py
├─ 配置区        PORT/ACCOUNT/ACCOUNT_TYPE/TOKEN/WS...
├─ QMT 生命周期  init / handlebar / adjust / stop
├─ HTTP handler  BaseHTTPRequestHandler
│   ├─ /data/*  行情查询 + 订阅端点(旧路径兼容转发)
│   ├─ /trade/* 交易查询/下单
│   └─ Upgrade: websocket → WS 逻辑
├─ WS 推送       握手/帧/心跳(只收心跳,专注推送行情 + trade_result)
├─ 订阅表        HTTP 订阅 → 关联 client_id → QMT subscribe(透传)
├─ 下单队列      adjust() 里执行 passorder/cancel(唯一非透传: 下单必须策略线程)
└─ 回调         order_callback / deal_callback(回写 ORDER_RESULTS + WS 推送)

6.2 薄桥下"唯一需要桥自己逻辑"的地方

功能 是否透传 说明
行情查询(/data/*) 透传 直接调 ContextInfo
订阅(HTTP) 透传 直接 subscribe_quote/whole_quote,按 client_id 记录关联
WS 推送 ⚠️ 桥内关联 QMT 回调 → 查订阅表 → 推给 client_id 的 WS 连接
持仓/资金/委托/成交查询(/trade/*) 透传 直接 get_trade_detail_data
下单/撤单 ⚠️ 队列 passorder 必须在策略线程(adjust)执行,HTTP 线程不能直调(官方约束)
状态回写 ⚠️ 回调+推送 passorder 无返回,靠 order_callback/deal_callback 回写 + WS trade_result 推送

六之二、实现计划(薄桥 MVP)

Phase 1: 最小骨架(2026-08-20 定,本次实现)

  1. 单文件 qmt_bridge.py:HTTP handler + /health + /data/kline + /data/quote
  2. WS 连接打通(Upgrade + 帧循环 + 心跳,暂不推送内容)
  3. 旧路径兼容(/kline/data/kline,/quote/data/quote)
  4. 本地 mock 测试(最小骨架 3 接口 + WS 连接)

Phase 1.5: 增量(骨架验证后)

  1. data 其余查询接口(/kline/batch /kline/download /instrument /calendar /sectors /sectors/stocks /stocks/delisted)
  2. trade 查询接口(/positions /asset /orders /trades)
  3. 下单/撤单队列 + adjust + 回调回写

Phase 1.6: 订阅与推送(骨架 + 查询稳定后)

  1. HTTP 订阅端点(/data/subscribe /subscribe_whole /unsubscribe)+ client_id 关联
  2. WS 行情推送(type=quote / type=whole)
  3. WS 交易结果推送(type=trade_result)

Phase 2: 真实环境验证

  1. 部署到 QMT(GJQMT_BIG),跑通待实测项
  2. 模拟盘验证下单/撤单/回报

Phase 3: 扩展(按需)

  1. 交易类接口细化(两融/期权/新股)
  2. 推送式 K 线(桥内轮询)
  3. Redis/MQ 中转(海量客户端时)

七、端口决策记录

方案 结论 理由
双端口(8610 HTTP + 8611 WS) 备选 实现简单,但多开一个端口
单端口(8610 统一) 选定 无性能影响,技术问题可解,部署/安全/客户端更简单

八、待实测项(真实 QMT)

  1. ContextInfo.get_market_data_ex 返回结构确认(index=时间? columns=字段?)
  2. download_history_data 全局函数是否可用
  3. /stocks/delisted 的大QMT 本地数据目录结构
  4. get_sector_list 是否原生(还是 fallback)
  5. WS 单端口升级在 ThreadingMixIn 下是否正常
  6. HTTP 订阅后,subscribe_quote/subscribe_whole_quote 回调能否正确触达(真实行情环境)
  7. 下单后 order_callback/deal_callback 是否实盘模式才触发(决定 trade_result 推送的可靠性)

九、设计变更记录

日期 变更
2026-08-19 v3 定稿:薄桥 + data/trade 前缀 + 单端口 8610
2026-08-19 协议划分变更: HTTP 负责请求/订阅/下单,WS 只做推送(行情 + 交易结果)。订阅从 WS 移入 HTTP,新增 /data/subscribe 等端点;WS 新增 trade_result 推送,替代 order/status 轮询
2026-08-20 架构决策: 自研 HTTP 桥(线程池 8 线程 + run_time 5ms 调度),不用 big_convert;整合自最终方案.md(详见二之二)
2026-08-26 WS 单通道推送(一期)完成: 单通道 /ws + 全量tick订阅(type=whole),订阅/退订走 HTTP(/data/subscribe,/data/unsubscribe),新增 /data/tick 透传 get_full_tick;详情见 docs/迭代记录/WS单通道推送设计.md