diff --git a/docs/design/2026-09-07-miniqmt-shutdown-migration-assessment.md b/docs/design/2026-09-07-miniqmt-shutdown-migration-assessment.md new file mode 100644 index 0000000..d7c845f --- /dev/null +++ b/docs/design/2026-09-07-miniqmt-shutdown-migration-assessment.md @@ -0,0 +1,81 @@ +# miniQMT 停服迁移评估(xtquant_big_convert / 大QMT RPC) + +> 2026-09-07 infra session 出品。背景:券商通知 **2026-09-18 停 miniQMT**(用户与国金确认),VPS 上 8 组合实盘 + 8 影子 + CTA/bridge/monitor + xt_eod 采集全部依赖 XtMiniQmt(模拟端,账号 66639661)。 +> 依据:双 agent 只读探查(对方仓库 `~/.openclaw/sanguo_projects/xtquant_big_convert` v0.3.25 + 我方调用面盘点),本文引用均来自探查报告的文件:行号。 + +## 一、结论 + +**方向可行:xtquant_big_convert 的 xtquant shim 兼容面覆盖我方三形态全部交易 API 与绝大多数行情 API,回调机制完整,多客户端(16 引擎)协议级支持。** 但有 4 个风险,其中前 2 个是 go/no-go 级,必须在动手前闭环: + +### 🔴 R1(置顶):我们跑的是「模拟端」,对方 server 可能只在「实盘模式」工作 +- 我们的 miniQMT 是**国金QMT交易端模拟**(路径 `C:\国金QMT交易端模拟\userdata_mini`,66639661 模拟账号),全平台是 QMT 模拟柜台上的纸面交易 +- 对方 README.md:264 明言:**「模拟模式下单返回 -1 且查不到」「QMT 必须实盘模式才能收到完整回报」** +- 待办:①在装好的大 QMT 上用模拟账号实测 server 下单路径 ②若确认不行,问维护者(issue/群,项目日更很活跃)③与券商确认模拟柜台与大QMT模拟模式的对应关系。**此项不闭环,整个迁移无从谈起** + +### 🔴 R2:大 QMT 客户端与权限尚未确认 +- VPS 现只有 XtMiniQmt 极简版;大 QMT 是另一个完整客户端(如 `D:\国金证券QMT交易端\`),需券商侧权限 + 安装 + 下载内置 Python 组件 +- 用户 09-07 与券商确认了停服日期,**大 QMT 权限口径待补核**——若需审批,这是 11 天里最长周期项,最先启动 + +### 🟡 R3:串行吞吐与头阻塞 +- 对方约束:交易类请求全部 defer 到 QMT 主线程 adjust 串行(Redis 串行 20 次/s,4 线程并发 143 次/s);慢调用堵队列(实测首次 `get_financial_data` 346s) +- 我方压力:16 引擎轮询叠加(vnpy_qmt EVENT_TIMER + bullet_trade LiveEngine 轮询 query_orders/trades)开盘段可能 >20 req/s +- 对策(工程化可解):①组合实盘链路本就是轮询制,迁移时顺势切回调制(对方有全套推送)或调低轮询频率 ②`get_financial_data/download_financial_data` 等重数据腿限时盘后跑或改走本地 sanguo_data(fundamentals provider 本就有本地化计划) + +### 🟡 R4:xtdata 长尾接口待验证(xt_eod 断供风险) +- 对方明确缺口:`get_sector_list`/`get_holidays` 仅 fallback、`get_stock_type` 不可用 +- 我方在用但对方报告未明确列出的:`get_stock_list_in_sector`(xt_eod 的 ETF/基金/北交所名单命脉)、`get_index_weight`/`get_divid_factors`/`get_trading_dates`(bullet_trade MiniQMTProvider)——需装好后逐个验证;`call_method` 万能入口可兜底一部分 +- 若不支持:ETF/基金/北交所 EOD 断供 → 数据 session 另找源(与交易迁移解耦) + +## 二、接口对照(我方最小集 vs 对方兼容面) + +**交易(三形态合并去重)——全绿 ✅** +| 我方调用 | 对方实现 | 备注 | +|---|---|---| +| `XtQuantTrader(path,session)/start/connect/subscribe/stop/register_callback` | shim `BigQmtXtTrader`(xtquant_compat.py:4864) | connect 语义=连 Redis/ZMQ;**session_id 撞车问题(08-30 案)自动消失**;path 参数失效 | +| `order_stock` 同步 8 参(bridge/bullet_trade) | :3576-3586,int 子类 OrderId(>0 成功/-1 失败,str()=合同号) | 契约同 miniQMT | +| `order_stock_async`(vnpy_qmt) | :4184-4208,<1ms 返回 seq | 「事件先于响应」常态,按 order_remark 设屏障(我们 remark 指纹体系现成) | +| `cancel_order_stock(_sysid)`(+async) | :4333/:4312/:4654/:4701 | 返回 0 成功/-1 失败 | +| `query_stock_asset/positions/orders/trades`(+async+callback 参数) | :3222/:3322/:3473/:3495,async 变体 :4584-4651 | 服务端白名单 137 只读+3 下单 | +| 回调 `on_disconnected/on_stock_asset/on_stock_order/on_stock_trade/on_stock_position/on_order_error/on_cancel_error/on_order_stock_async_response/on_cancel_order_stock_async_response` | `XtQuantTraderCallback` 基类全套(xtquant_compat.py:88-123) | Redis PUBLISH+XADD(stream maxlen 2000 可短时回放),柜台回报 ms 级 | +| `StockAccount(id[,type])`/`XtOrder/XtTrade/...`/`xtconstant`(ORDER_* 48-57、市价链) | shim xttype + 539 常量 | 我们三套状态码映射不用改 | + +**行情——大面积绿,四个待验证** +| 我方调用 | 对方 | +|---|---| +| `get_full_tick`(影子撮合价/涨跌停、实盘自检、fundamentals 批量) | ✅ 但注意破坏性变更:`get_full_tick(["SH"])` 默认只取股票,要全量传 `types=["all"]` | +| `get_market_data(_ex)/get_local_data/get_instrument_detail/get_last_quote/subscribe_quote/subscribe_whole_quote/unsubscribe_*/download_history_data(2)` | ✅(订阅多客户端引用计数共享) | +| `get_financial_data/download_financial_data` | ✅ 但重腿盘后跑(R3) | +| `get_stock_list_in_sector` / `get_index_weight` / `get_divid_factors` / `get_trading_dates` | ⚠️ 待验证(R4) | + +## 三、我方三形态迁移差异(探查 B 的链路梳理) + +| 形态 | 现状 | 迁移动作 | +|---|---|---| +| A. HTTP bridge(备胎) | 纯同步 API、无回调 | shim 顶替后零改动;remark 现传空——若启用需补指纹 | +| B. vnpy_qmt CTA(进程内) | 全 async+callback | shim 兼容签名;回调链最全,重点联调 | +| C. bullet_trade QmtBroker(组合实盘×8 主路径) | 同步 API+轮询制、回调只挂 on_disconnected、`bt:live_{id}:{hash}` remark 归因 | **最小改动**:装 shim 即可跑;轮询 query 兼容;remark 往返保真是验收重点(对方 0.3.25 #216 正好修了 strategy_name 归因优先级) | +| 影子×8 | 无 xttrader,仅 xtdata 取价 | shim 顶替 xtdata;`get_full_tick` 注意 types 参数 | +| xt_eod 采集 schtask | get_stock_list_in_sector+download_history_data2 | 客户端 pip 包装上即连(跨平台),R4 验证后定去留 | + +**连接参数变化**:`SANGUO_QMT_PATH`(userdata_mini 扫描)与 session_id 体系全部失效 → 换成 Redis/ZMQ 端点配置;`live_accounts` 注入 env 的机制保留,只换内容。多账号单 server 或多策略实例(对方 README.md:813-865)——我们单账号 66639661,单 server 足够。 + +**执行回报广播过滤**:对方按 account_id 广播全部委托/成交,16 引擎各自按 order_remark 过滤——我们已有 remark 指纹库(7b7667e 重建 own 集 + live_{id} 唯一 label),天然匹配。 + +## 四、灰度时间表(deadline 09-18,目标 09-15 全量切完留 3 天观察) + +| 阶段 | 日 | 内容 | 闸门 | +|---|---|---|---| +| P0 前置 | 09-08 前 | **用户**:券商确认大QMT权限+下载安装到 VPS(`D:\国金证券QMT交易端\`),下载 python 库组件 | 大 QMT 能登录(模拟+实盘口径都问清) | +| P1 部署 | D+1 | Redis-windows 装同机(bind 127.0.0.1+强密码)或 ZMQ;`deploy_qmt_bridge.ps1`;QMT 模型交易页加载 `BIGQMT_REDIS_DRYRUN.py`(**实盘模式、不勾"启动本地python"**) | server 起、Redis 队列可见 | +| P2 只读验证 | D+1~2 | pip 装 client(editable 防 shim 冲突);跑通 query_stock_asset/positions/orders + get_full_tick;**R1 模拟模式实测 + R4 四接口验证** | 只读全绿;R1/R4 定性 | +| P3 下单冒烟 | D+2~3 | 官方 6 步灰度第 4 步开 `rpc_allow_order_methods=True`;模拟小单 1 笔买+卖,验 remark 往返/成交回报/撤单 | 冒烟单闭环 | +| P4 影子试水 | D+3~5 | 影子×1 切 shim 跑 2 个交易日,与 miniQMT 侧对照 | 撮合价/涨跌停判定无异常 | +| P5 全量 | D+5~8 | 8 影子全切 → 组合实盘逐个切(每切一个跑一日恒等式对账绿再切下一个);CTA/monitor/bridge 收尾 | 恒等式连续绿 | +| 回退 | 至 09-17 | miniQMT 在 09-18 前天然是回退路径(保留不卸载,env 切回即回退) | — | + +## 五、开放项(按优先级) +1. **R1 模拟模式 go/no-go**(阻塞一切) +2. **R2 大QMT权限+安装**(用户动作,最长周期) +3. R4 四个 xtdata 接口验证清单 +4. 策略 session 参与:P5 实盘切换涉及策略代码/调度,需策略 session 联合(三向一致性检查按项目 CLAUDE.md 流程走 Gitea Issue) +5. xt_eod 若断供,数据 session 找替代源(独立工作流,不阻塞交易迁移)