Files
sanguo_vnpy_v2/audit/20261005_qmt_identity_env_plan.md
T
claude_dev 434d7cb357
CI/CD / test (push) Failing after 7s
CI/CD / nas-deploy (push) Has been cancelled
CI/CD / nas-verify (push) Has been cancelled
feat(config): QMT 部署身份单一权威源 config/qmt_identity.json——P2-18 账号硬编码根治(⚖️-2 用户拍板:确认永为模拟号+令根治硬编码)
根因三层(全案=audit/20261005_qmt_identity_env_plan.md): schtask 无 per-task env+机器级 env 未设→wrapper 被迫字面量;「部署身份」无单一权威源(env 名分裂三个);模拟号零安全压力致 32 文件 88 处 copy-paste 扩散。

- qmt_gate_common.load_identity 正典装载器(:14-59): env SANGUO_QMT_ACCOUNT/BIGQMT_ACCOUNT_ID(兼容别名)>json>RuntimeError fail-loud;SANGUO_IDENTITY_JSON 显式指路不存在=立即报错;查找=repo 相对>C:\sanguo_vnpy_v2\config\(生产副本回退位)
- 灾备腿同源: relogin/qmt_bridge_probe/setclip 改 from qmt_gate_common import ACCOUNT
- 桥腿镜像同链: xt_gateway._qmt_account/qmt_gateway_client._identity_default_account/is_price_source_vps/bridge_switch_ops(3 处 env 注入+1 处内嵌探针串)/check_xtquant——env 带默认的硬编码默认值全数退役
- xt_eod_wrapper.ps1 改 Get-Content identity json(保持 ASCII);码内 docstring 字面量→占位符(engine/runner/runner_live/live.yaml 注释/README)
- .gitignore 白名单 !config/qmt_identity.json(全局 *.json 曾吞之,git check-ignore 实证)
- tests/trader/test_qmt_identity.py 五钉: 装载链语义(env 双名覆盖/显式指路 fail)+单源一致性(identity≡live.yaml≡watch_accounts≡gate_common≡各腿镜像,休市表 CI 同步测先例)+码面字面量 grep-pin(生产码面零 66639661,只 config 三件套可含)
- 监控 spec: §3.2 账号来源 bullet/§9.A-18 坑条/§12 行数+行号勘正(插入块致 bridge_ping/calendar_probe/relogin_running 引用行号推移);runbook: 非代码资产两行(副本回退路径+生产 wrapper 待改清单=10-08 窗口后 §五③)

生产 wrapper(C:\sanguo_bigqmt 库外)字面量收尾=10-08 复市验证窗口后非代码资产流程;VPS 班车同窗口(护 10-08 07:50 relogin 首班旧码纯净)。trader+api 620 绿。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-05 17:32:42 +08:00

7.4 KiB
Raw Blame History

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;漏一处 = 静默盯错账号。

二、根因(三层)

  1. 机制层:Windows schtask 无 per-task env(sentinel_task.xml 实证无 env 节);VPS 机器级 env 未设 (HKLM\...\Session Manager\Environment reg query 实证空)→ 每个计划任务被迫 wrapper 注入,字面量顺手写进 wrapper。
  2. 结构层:「部署身份」(这台机器驱动哪个 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。
  3. 氛围层:模拟号泄漏无害 → 零安全压力(铁证 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 三组钉)

  1. 装载链语义:env 两名覆盖、SANGUO_IDENTITY_JSON 指空=RuntimeError、模块 ACCOUNT ≡ json。
  2. 单源一致性(anti-drift,休市表先例):identity ≡ live.yaml.account ≡ ∈ watch_accounts ≡ gate_common.ACCOUNT ≡ xt_gateway/qmt_gateway_client 镜像解析值(env 清空)。
  3. 字面量 grep-pin:66639661 只允许出现在 config 三件套 + tests/ + docs/ + audit/; 生产码面(scripts/、sanguo_*/)零字面量——任何新增扩散直接被单测抓住。

五、分阶段上线

阶段 时点 内容
① repo 本班(10-05) 上表全部 + 测试 + 两档更新;push 后 CI test 绿即达(nas-deploy 楔死=找 infra 手动 promote,既定协议)
② VPS 10-08 复市验证窗口后班车 promote 带上 json + 新脚本;10-08 07:50 relogin 首班跑旧码=纯净验证
③ 生产 wrapper ②后 run_shadow_bridge.ps1 等库外资产改读 json(runbook 非代码资产流程报备 infra)
④ 可选增强 按需 setx /M 机器级兜底;mini_path 接线;watch_accounts 多账号扩展

六、回滚

单 commit revert 即回旧形态(各文件改动均为等价替换,行为差异仅「默认值来源」一项); 身份文件缺失时 fail-loud(RuntimeError/启动非零)而非静默错号——方向保守。