tradingSystem/docs/PMS三项对账差异排查_2026-10-15.md

63 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PMS 三项对账差异排查与修复规格
2026-10-15,北京时间。对象是 155 模拟 PMS,ws 下发、propose_only 新建仓。188 真实仓不在本次范围。
## 事实与预判
只读 reconcile(apply_fix=False) 发现 002050.SZ 账本 800 / QMT 700,002979.SZ 账本 0 / QMT 1900,688100.SH 账本 2400 / QMT 1400。
八个委托的 trading_order.order_id 与 pms_qmt_order.instruction_id 相同,qmt_order_id 与 broker_order_id 也全部一致。
WebSocket 逐笔回报均 processed=1,汇总委托全部已进入 PMS_REPLAY_CURSOR.seen。
| 股票 | 当日重复回放的汇总数量 | 结果 |
| --- | --- | --- |
| 002050.SZ | 买入 100 | 净多 100;批次 1361 以委托限价 31.560 重复入账,逐笔实际为 73 股 30.690 和 27 股 30.650 |
| 002979.SZ | 卖出 1300、500、100、100,合计 2000 | 重复核销后数量扣到零,与 QMT 1900 不符;数量下限截断使差额不是 2000 |
| 688100.SH | 买入 700、卖出 400、买入 700 | 重复净买入 1000;部分重复买入又被错误认作外部 BASE |
根因在 ledger_service.replay_fills:注释承诺 trading_order 只兜外部/人工成交,实际没有排除本 PMS 的 WebSocket 委托。
这不是新计划版本造成的;三项当日账本更新时间均早于 10:42 PMS 部署。成交价格也因汇总表回退委托价格而被污染。
## 修复原理及边界
在 trading_order 回放认领前,以委托编号及券商编号精确查询本实例 pms_qmt_order,不按股票、时间和数量猜测重复。
属于本实例出口表的委托只由 WebSocket 逐笔入账,汇总镜像只推进既有 seen 游标并留跳过原因。
即使逐笔迟到、部分到达、联调单或当前模式已经切回 shadow,也不得从汇总表补记同一委托。
身份查不到的外部/人工成交保留原回放。身份读取失败、两个标识对应不同委托、代码/方向/券商身份冲突时,不入账、不记 seen,留错供下一跳重试。
单表 SQL,不新增表、调度或交易参数。历史账本与历史指令进度不由此补丁自动回写。
## 验收
先用合成数据复现双来源买入、卖出、部分成交与乱序。新增第 39 批测试;执行配置隔离与禁止联网的全量测试。
部署前再核验实例、在途委托与容器指纹;匹配候选镜像断网全量通过后,按本聊天授权重建 PMS 四进程。
真机检查健康、通道、回放无重复动作与只读身份分类。已有错账单独标记,不能把修复代码通过说成历史账已恢复。
## 历史修复约束
日终 RECON 只调数量,并会把差额按行情价格当成新增或核销,不能修复被污染的原始成本、已实现盈亏和指令 exec_qty。
因此不为消除告警直接 force 对账。先保存当前批次、原逐笔成交、汇总回放游标及指令快照,评估能否完整重建。
历史已有 RECON 与过往双记相互叠加;冻结证据不足时不能伪造无误差历史。任何后续修复须另存可逆备份与调整依据。
## 12:05 实测结果
修复提交 e9afc7a 已于 12:02:54 部署 155 四个 PMS 服务。新增 12 例、开发机全量 43 批、候选镜像断网全量 43 批均通过。
真实八笔委托的只读归属核验全部正确。健康、数据库、QMT 通道正常,待入账和在途出口委托为零。
当前为午休,修复后自然新成交回放尚待事件验证;三项历史差异仍保留,不报告为清零。
原始证据文件 ~/pms_logs/reconciliation-evidence-20261015-before-fix-v2.json 的 SHA256 为 8e0badf4d771fbf8a3227250c6cc2c2bf3c8234dda2fc33a31f0ab00920f5501。
原始交易持仓表额外出现 562590.SH 与 688825.SH 属另一项事实源覆盖差异,继续按既有 ws_first 取实际持仓,不混同三项账本差异。
## 历史修复开发规格与预判(2026-10-15 12:30,北京时间)
用户已明确授权先备份,再逐笔恢复数量、成本、盈亏和指令执行进度。修复限定 155 模拟仓 002050.SZ、002979.SZ、688100.SH。188 不在范围内。首份完整备份为主机 pms_logs/historical-repair-backup-20261015-1225.json,SHA256 为 81cfc59600d7ca07de7fc4b83b7c5b153719d0ea248be21fd9d910bcfc32e636。包含全部持仓、批次、指令、方案、命令、出口单、手续费、策略、提议、日报、净值,三票动作账本,完整留存逐笔成交及下游成交表。写前还要重新备份并校验并发变化。
实现采用纯函数重建与单表 SQL 事务写回。逐笔成交必须能唯一对应出口子单,代码、方向、委托号、累计数量一致;同成交号冲突、漏笔、卖超、外部成交无法解释、来源读取失败均阻断。每笔真实买入以父指令、数量、价格、原入账时间唯一对应原批次。重复买入批次须能聚合对应同一条下游汇总单,RECON 调整须单列;不能直接按数量差删除。真实卖出按既有批次优先级逐笔重算核销量与盈亏。原批次 ID 保留,虚假批次归零并注明备份与修复批号,不删除原证据。
成本使用成交金额或等价的“剩余批次成本减已实现盈亏”,避免三位小数核销均价再乘数量带来的舍入误差。费用不摊进成本;缺失费用保持未知。父指令按真实逐笔量与金额重建 exec_qty/exec_avg_price,同时在 progress 中保存原值、子单证据和剩余量。原误判完成但未足量的指令改为 CANCELLED 并记录“历史修复暂停剩余执行”,前提是所有子单均已终态,绝不恢复自动续单;原在执行的指令保持生命周期。真实超目标成交如 002979 的 1,300 股网格指令成交 2,600 股要如实保留并标异常。方案、命令当前为空,无需伪造。
完整性边界按当前留存批次的起始日验证,之前成交净数量必须为零。002050 从 09-22、002979 从 09-21、688100 从 10-14 重建。688100 的 08-27 至 09-01 已闭合旧轮次缺父指令及原批次,单列成交金额,不混入当前账期。历史安全垫峰值、手续费、过往日报与全组合净值如缺原始材料不宣称恢复;保存原值和修复附注。
写回只在受影响子单无在途、无未处理逐笔时进行。短暂停止 pms-beat 与 pms-worker,保留通道接收能力;所有快照比较和更新位于同一数据库事务,每条 SQL 仍经过表名前缀与单表守卫。事务写入审计记录;备份指纹不符、数据已变或中途失败均不提交。重复执行同一批号返回已完成。回退必须先比较修复后字段,若已有新成交则拒绝覆盖。写完只读核对,再恢复调度。
预判:重建后数量分别为 700、1,900、1,400;全部相关子单逐笔量等于累计量;虚假买入批次和 RECON 调整不再贡献成本或盈亏;指令真实进度不会因修复自动生成新单;原始成交、回放游标、策略阈值和明日启用门槛不变。测试覆盖金额、部分成交、漏笔、匹配歧义、并发、幂等、失败回滚及旧指令不复活。生产读回后逐条判收。