8.5 KiB
QMT 部署身份单一权威源方案(P2-18 根治)——66639661 硬编码调研与实施
2026-10-05 傍晚用户拍板(⚖️-2):确认 66639661 永为模拟柜台账号;同时指出「硬编码本身是问题」, 令详调根因并给出硬编码以外的方案。infra 派单(025b4e6):本档落 audit/ + repo 侧实施归 stratergy, VPS 班车 = 10-08 复市验证窗口之后(护 10-08 07:50 relogin 首班纯净)。
一、全量枚举(git grep 实证)
库内 32 文件 88 处 + 生产侧库外若干。按形态分五类:
| 形态 | 位置 | 处置 |
|---|---|---|
| 纯常量 | qmt_gate_common.py:14、qmt_relogin.py:41、qmt_bridge_probe.py:9、setclip_acc.py:8、check_xtquant.py:17 |
→ 读身份文件 |
| env 带硬编码默认 | xt_gateway.py:22、qmt_gateway_client.py:76、is_price_source_vps.py:24 |
→ 删默认值,env > 身份文件 |
| wrapper 注入字面量 | bridge_switch_ops.py:119/276/322/331、xt_eod_wrapper.ps1:10、生产 run_shadow_bridge.ps1:7(C:\sanguo_bigqmt\,不在 git) |
库内 → 读身份文件;生产 wrapper 10-08 后按非代码资产流程 |
| 配置文件(正确形态,保留) | config/live.yaml:9、config/data_platform.yaml:63 |
保留,CI 同步测钉一致 |
| 文档/注释/测试 | engine.py:41、runner.py:12、runner_live.py:12/294、live.yaml:4 注释、README、7 份 docs、9 个 test | 码内注释改占位符;docs 归档不动;测试保留 |
换号真实成本(改前):库内 14 个生产文件 + 生产侧 ≥4 个 wrapper ps1;漏一处 = 静默盯错账号。
二、根因(三层)
- 机制层:Windows schtask 无 per-task env(sentinel_task.xml 实证无 env 节);VPS 机器级 env 未设
(
HKLM\...\Session Manager\Environmentreg query 实证空)→ 每个计划任务被迫 wrapper 注入,字面量顺手写进 wrapper。 - 结构层:「部署身份」(这台机器驱动哪个 QMT 账号)无单一权威源。三代各自发明:
07 月 live 腿 = env
SANGUO_QMT_ACCOUNT> live.yaml > fail(正规,96b1924); 09 月大QMT 灾备腿 = 纯常量(stdlib 纯净 + 图省事,2b2ae3c/4444848); wrapper 层 = 注入字面量。env 名分裂三个:ACCOUNT_ID/SANGUO_QMT_ACCOUNT/BIGQMT_ACCOUNT_ID。 - 氛围层:模拟号泄漏无害 → 零安全压力(铁证
run_shadow_bridge.ps1同文件:redis 密码运行时读 conf 纪律在线,上一行账号就是字面量)。09-14/19 ops 批次 copy-paste 传播主力。
三、方案比选(已报用户+infra)
| 方案 | 结论 |
|---|---|
| A 全 env 化 | ❌ 字面量只是从 py 搬家到 ps1(生产 wrapper 已是此形态);灾备腿 fail-loud = 机器最坏时 gate 起不来 |
| B 机器级 env(setx /M) | ⭐ 优秀传输层非完整方案;机器事实≠版本管理事实,重建 VPS 丢(本批不采用,留作可选增强) |
| C 单一配置文件权威 | ✅ 采纳:config/qmt_identity.json + 各腿镜像读取器 + CI 同步测(休市表 7f9b413 先例) |
| D DB 注册表权威 | ❌ 鸡生蛋:relogin/gate 恰在「别的都坏」时要跑,不能加 DB 依赖(runtime 腿的 sticky 行机制 B1 已在用) |
C 的关键约束:灾备腿(gate/sentinel/relogin)stdlib 纯净 → 配置文件必须 JSON(json 是标准库,
pyyaml 不是)——这也是不能并进现有 data_platform.yaml 的原因。
定位:本方案买的是可维护性(换号一处生效、杜绝静默盯错账号),不是保密——git 历史已含该号; 真上实盘那天的正确动作 = 开新实盘号 + 模拟号退役,而非改配置。
四、实施设计
身份文件 config/qmt_identity.json(随 promote 走,git 管理)
{
"default_account": "66639661",
"kind": "simulated",
"mini_path": "C:\\国金QMT交易端模拟\\userdata_mini"
}
(mini_path 本批只存档不接线——账号才是 P2-18 的病灶;路径接线留后续按需。)
解析链(各腿一致)
env SANGUO_QMT_ACCOUNT > env BIGQMT_ACCOUNT_ID(兼容别名,存量 wrapper 设的就是它)
> config/qmt_identity.json > RuntimeError fail-loud(不猜、不默认)
文件查找顺序(灾备腿专用后两条):env SANGUO_IDENTITY_JSON 显式指路(不存在=立即报错,
显式覆盖不静默回退)> <脚本>/../../config/(repo 布局)> C:\sanguo_vnpy_v2\config\
(生产副本位——gate/relogin 以 C:\sanguo_bigqmt 副本运行时 repo 相对路径失效)。
改动清单(repo 侧本批)
| 文件 | 改动 |
|---|---|
config/qmt_identity.json |
新增 |
scripts/qmt_relogin/qmt_gate_common.py |
load_identity() 正典装载器 + ACCOUNT 改读它 |
scripts/qmt_relogin/qmt_relogin.py / qmt_bridge_probe.py / setclip_acc.py |
from qmt_gate_common import ACCOUNT(同目录,消重) |
sanguo_qmt_bridge/xt_gateway.py / check_xtquant.py |
包内镜像 _qmt_account()(env ACCOUNT_ID/SANGUO_QMT_ACCOUNT/BIGQMT_ACCOUNT_ID > json) |
sanguo_trader/qmt_gateway_client.py |
_setting() 删默认字面量 → 镜像链 |
scripts/pipeline/is_price_source_vps.py |
_ACCOUNT 删 → _identity_default_account() |
scripts/bridge_switch_ops.py |
3 处 env 注入 + 1 处内嵌代码串 → _qmt_account() |
scripts/data_platform/xt_eod_wrapper.ps1 |
Get-Content qmt_identity.json | ConvertFrom-Json(保持 ASCII) |
sanguo_live/engine.py / runner.py、runner_live.py、config/live.yaml:4 注释 |
字面量 → <账号> 占位 |
sanguo_qmt_bridge/README.md |
默认值表行 → 指向身份文件 |
tests/trader/test_qmt_identity.py |
新增(见下) |
docs/deployment/vps-production-runbook.md |
身份文件条目(CI 基建门:qmt_relogin/bridge_switch_ops 触发) |
docs/superpowers/specs/2026-10-03-monitoring-design.md |
gate 簇身份链一句话 + 坑台账 |
测试策略(tests/trader/test_qmt_identity.py 四组钉)
- 装载链语义:env 两名覆盖、
SANGUO_IDENTITY_JSON指空=RuntimeError、模块ACCOUNT≡ json。 - 单源一致性(anti-drift,休市表先例):identity ≡ live.yaml.account ≡ ∈ watch_accounts ≡ gate_common.ACCOUNT ≡ xt_gateway/qmt_gateway_client 镜像解析值(env 清空)。
- 字面量 grep-pin:
66639661只允许出现在 config 三件套 + tests/ + docs/ + audit/; 生产码面(scripts/、sanguo_*/)零字面量——任何新增扩散直接被单测抓住。 - 文件格式钉=纯 ASCII(10-07 补,🔴 生产事故反向钉):PS5.1
Get-Content默认按 ANSI/GBK 解无 BOM 文件,文件含 UTF-8 中文原始字节 →ConvertFrom-JsonFormatException → env 注入失败。10-06 21:00 xt_eod 实证全链:BIGQMT_ACCOUNT_ID未注入 → 桥 shim import 期硬错account_id is required→ universe ETF/基金=0 → rc=1(对照 10-05 同假期班 rows=37301 正常)。 修=文件中文一律\u转义(ensure_ascii),双端等值:Pythonjson.load与 PS5.1ConvertFrom-Json均原生解转义。phase-③ 生产 wrapper 裸Get-Content读此文件的安全性由本钉担保(改文件加中文必红)。
五、分阶段上线
| 阶段 | 时点 | 内容 |
|---|---|---|
| ① repo | 本班(10-05) | 上表全部 + 测试 + 两档更新;push 后 CI test 绿即达(nas-deploy 楔死=找 infra 手动 promote,既定协议) |
| ①′ ASCII 化修复 | 10-07(10-08 窗口前) | json 改 \u 转义 + 第④钉;紧急性=10-08 复市 21:00 xt-daily 必须能用(10-06 起已断,30d 窗口拉取自愈无数据丢失);部署须在 10-08 21:00 前落地(理想=10-07 当天,当晚 21:00 班即真班验证) |
| ② VPS | 10-08 复市验证窗口后班车 | promote 带上 json + 新脚本;10-08 07:50 relogin 首班跑旧码=纯净验证(注:①′ 若 10-07 发车则 ② 的 json 部分已随行,脚本部分仍候窗口后) |
| ③ 生产 wrapper | ②后 | run_shadow_bridge.ps1 等库外资产改读 json(runbook 非代码资产流程报备 infra);裸 Get-Content 安全性由第④钉担保,也可加 -Encoding UTF8 双保险 |
| ④ 可选增强 | 按需 | setx /M 机器级兜底;mini_path 接线;watch_accounts 多账号扩展 |
六、回滚
单 commit revert 即回旧形态(各文件改动均为等价替换,行为差异仅「默认值来源」一项); 身份文件缺失时 fail-loud(RuntimeError/启动非零)而非静默错号——方向保守。