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

182 lines
20 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 调整不再贡献成本或盈亏;指令真实进度不会因修复自动生成新单;原始成交、回放游标、策略阈值和明日启用门槛不变。测试覆盖金额、部分成交、漏笔、匹配歧义、并发、幂等、失败回滚及旧指令不复活。生产读回后逐条判收。
## 10-15 12:40 历史账本修复完成与判收
本节取代前文“三项历史差异尚未修复”的当前状态。用户明确批准先备份、逐笔核对并恢复数量、成本、盈亏及指令执行进度。已在 155 模拟仓完成,188 未操作。
### 备份、部署和写回
功能提交 `0871885`,Python 指纹 `01251315d8d1`。本地配置隔离禁网和 155 候选镜像 `--network none` 均通过全部 44 批测试,新批次 40 有 21 例。未将用户原有未提交台账 017、讨论稿或探针纳入提交。
先于任何写账保存完整备份,再在午间暂停 beat/worker,拉取已提交版本并重建四容器。web/ws 于 12:37:27 启动,worker/beat 于 12:39:33 恢复。四容器同镜像 `sha256:22422dc46784ff87c8ccfc6f9a2ae05ba8ee111eb18e3f1a369f9d913279ff0b`,标签 `pms:history-repair-0871885`;上一镜像留为 `pms:rollback-before-history-repair-20261015`。
155 宿主 `~/pms_logs/` 的证据如下,JSON 文件权限为 600。
| 文件 | 内容或 SHA256 |
| --- | --- |
| historical-repair-backup-20261015-1225.json | 初始完整备份,81cfc59600d7ca07de7fc4b83b7c5b153719d0ea248be21fd9d910bcfc32e636 |
| history-repair-before-write-20261015.json | 12:37:55 写前完整备份,d1b3eeaede0ea418ec071fa593ee6a98177a55433ee0201b502ff80ff6226f02 |
| history-repair-preview-20261015.json | 逐笔映射、每笔卖出分配、逐日数量金额、全部目标列和原值 |
| history-repair-after-write-20261015.json | 写后完整备份,8463f0da2e1d152e7606c38607918b05555481f4ecbd15b482a326951ebd3606 |
| history-repair-apply-20261015.log / history-repair-verify-20261015.json | 实际提交及独立读回判收,全部检查通过 |
| history-repair-build-20261015.log / history-repair-tests-20261015.log / history-repair-deploy-20261015.log | 候选构建、断网测试及容器重建 |
修复批号 `HIST_c036a684cab040387321396e`。同一事务校验原行、完整逐笔和有效参数,修改 122 行,保存 122 条逐行原值审计和 1 条完成记录。再次执行同一备份返回 already_applied,审计数仍 122+1。原始成交、回放游标、方案、命令、手续费流水、旧日报净值及其他股票原行均未被改写。
### 实际恢复结果
| 股票 | 恢复数量 | T+1 可卖量 | 摊薄成本(元) | 账期已实现盈亏(元) | 可追溯起点 |
| --- | ---: | ---: | ---: | ---: | --- |
| 002050.SZ | 700 | 600 | 44.109 | -6762.70 | 2026-09-22 |
| 002979.SZ | 1900 | 1900 | 55.060 | -1684.25 | 2026-09-21 |
| 688100.SH | 1400 | 0 | 23.704 | 16.86 | 2026-10-14 |
成本沿用 PMS 的含已实现盈亏摊薄口径,盈亏为原批次核销规则下的毛额,不含费用。134 笔成交对应 61 个真实买入批次;33 个重复汇总或 RECON 批次归零留痕,原 ID 保留。逐笔成交与 39 个留存子单的累计数量一致,累计均价差异最大 0.004964 元,处于 QMT 两位小数舍入范围内;其中 2 个子单属于 688100 的已闭合旧轮次,未混入当前账期。
24 条父指令恢复实际成交量、成交均价,并在 progress 中增加逐笔证据、原进度、剩余量及超额量。七条原误判 CONFIRMED 的指令改为 CANCELLED,含义为“历史修复暂停剩余执行”;其子单都已终态,无需撤单,也没有向 QMT 发送撤单或新单。仍在执行的 002050 DCA 指令实际进度为 100/300,保持原生命周期。
002979 的 `STR20261012093212_002979SZ_g` 目标 1300 股,两个真实子单累计买入 2600 股,保留超额 1300 的事实。这是实际执行异常,无法通过删账撤销,后续须单独排查历史出手并发/重试链路;本次没有反向交易。688100 活跃网格第 3 档恢复为真实剩余 1000 股,批次成本 23506.51 元、均价 23.50651;原仅记 700 股/16471 元。已取消的旧策略未恢复。
12:40 真机健康 200、db_ok=True、calendar_degraded=False,QMT ONLINE、last_seq=acked_seq=1226583、last_error 为空,待入账 0。独立对账返回 diffs=[]、errors=[]、severity=OK。历史 streak=14 是尚未到日终重置的旧计数,不是仍有三项差异。既有 ws_first 源覆盖警告仍为 562590.SH、688825.SH 两票,单列保留,不混同已修复差异。
### 明确不能还原或未判收的部分
1. 134 笔逐笔费用为空,无法还原真实扣费与净盈亏;费用流水原为空,未补零冒充真实费用。
2. 缺完整盘中行情和当时账本,历史安全垫峰值无法准确恢复,保留原值并标明限制。
3. 688100 的 08-27 买 800、09-01 卖 800 的旧轮次可核对成交毛差 330.47 元,但原父指令和批次已不在当前账本;无法恢复原始批次分配与完整决策链,不把该旧轮次利润写进本轮。
4. 历史日报和全组合净值受旧错账影响,缺少重建全组合所需的全部原始状态,本次保持原档案。修复预演提供逐票逐日数量、成交净额和批次盈亏,不能将其冒充当时实际报表或已修复全历史绩效。
5. 防双记代码的自然新成交判收仍需真实事件;不得制造成交来验收。真实超目标成交和两个事实源覆盖分歧仍单列待查。
### 后续和回退
`automation-2` 已更新修复结论,保持 ACTIVE 和北京时间 10-16 07:25。首个完整晨版通过后启用已创建且停止的 XXL 137,再开启 155 的 PMS_PLAN_VERSION_RECHECK。当前该参数仍 False,ws、propose_only、旧传导排序及档位和仓位参数保持;不增加额外五日模拟观察。版本一致、顺序、自然传播十二分钟、失败守成和无重复指令仍须实际事件判收。
手工入口 `scripts/repair_ledger_history.py` 开头列有 backup/preview/apply/rollback 跑法。回退须读取审计并比较修复后原行,后续有新交易或状态变化会拒绝覆盖;不得通过切旧镜像恢复旧账,更不得直接用旧备份覆盖新的成交。回退保留新旧审计与计划版本。
### 父指令进度逐条核验
| 父指令 | 原记成交量 | 真实成交量 | 真实均价 | 写后状态 | 剩余量 / 超额量 |
| --- | ---: | ---: | ---: | --- | --- |
| INS_20260918_002050SZ_OPEN_001 | 622 | 500 | 36.160 | CONFIRMED | 0 / 0 |
| STR20260923100400_002050SZ_g | 3277 | 1900 | 35.936 | CONFIRMED | 0 / 0 |
| STR20260923130400_002050SZ_g | 616 | 500 | 36.031 | CONFIRMED | 0 / 0 |
| STR20260928093600_002050SZ_g | 3544 | 1900 | 35.995 | CONFIRMED | 0 / 0 |
| STR20260928094700_002050SZ_g | 2000 | 1900 | 35.985 | CONFIRMED | 0 / 0 |
| STR20260928094900_002050SZ_g | 3609 | 1900 | 35.844 | CONFIRMED | 0 / 0 |
| STR20260930095702_002050SZ_g | 2183 | 1900 | 36.028 | CONFIRMED | 0 / 0 |
| INS_20260930_002050SZ_OPEN_100000 | 1600 | 800 | 35.032 | CANCELLED | 800 / 0 |
| INS_20261009_002050SZ_EXIT_103100 | 2711 | 1400 | 32.213 | CANCELLED | 1300 / 0 |
| INS_20261012_002050SZ_EXIT_93100 | 1346 | 700 | 33.044 | CANCELLED | 600 / 0 |
| INS_20261015_002050SZ_DCA_001 | 200 | 100 | 30.679 | DISPATCHED | 200 / 0 |
| INS_20260921_002979SZ_OPEN_001 | 307 | 200 | 54.470 | CANCELLED | 100 / 0 |
| STR20260923094502_002979SZ_g | 1401 | 1300 | 54.192 | CONFIRMED | 0 / 0 |
| STR20260923095800_002979SZ_g | 291 | 200 | 54.249 | CONFIRMED | 0 / 0 |
| STR20260924093702_002979SZ_g | 2438 | 1300 | 54.210 | CONFIRMED | 0 / 0 |
| STR20260924094001_002979SZ_g | 1458 | 1300 | 54.351 | CONFIRMED | 0 / 0 |
| STR20260924094200_002979SZ_g | 1683 | 1300 | 54.260 | CONFIRMED | 0 / 0 |
| INS_20261012_002979SZ_EXIT_001 | 2600 | 1300 | 54.178 | CANCELLED | 1300 / 0 |
| STR20261012093212_002979SZ_g | 2600 | 2600 | 53.677 | CONFIRMED | 0 / 1300 |
| INS_20261015_002979SZ_EXIT_95100 | 3900 | 2000 | 52.681 | CANCELLED | 1900 / 0 |
| INS_20261014_688100SH_OPEN_001 | 800 | 400 | 24.240 | CANCELLED | 400 / 0 |
| STR20261015094200_688100SH_g | 1023 | 700 | 23.571 | CONFIRMED | 0 / 0 |
| STR20261015094500_688100SH_g | 524 | 400 | 23.620 | CONFIRMED | 0 / 0 |
| STR20261015100700_688100SH_g | 1236 | 700 | 23.482 | CONFIRMED | 0 / 0 |
## 10-15 12:50 最终运行版本与均价有效期
最终功能提交为 `a56dac5`,包含 0871885 的历史修复及指令均价有效期补齐。原增量入账接口只收到数量,不能重算全量均价;后续新成交时同步清空旧 exec_avg_price,固定修复时点的准确金额继续保存在 progress.historical_repair 与操作审计中,避免显示过期均价。未再次改写历史账本,也未恢复暂停指令。
batch40 增至 22 例,内存数据库验证数量增加、均价失效、固定证据保持;本地与候选镜像 `--network none` 全部 44 批再次通过。12:50:11 四容器重建并恢复,镜像 `sha256:f3d4e698bc31be521f59b39588444495e143f9a52a3d5233b42a699f4030093d`,标签 `pms:mean-validity-a56dac5`,Python 指纹 `3de5fc3a261e`。前一版保留为 `pms:rollback-before-mean-validity-20261015`。
12:50:47 再次独立逐字段核验通过,三票数量、成本与盈亏保持上一节读数,审计仍为 122+1,对账 diffs=[]、severity=OK。健康 200、db_ok=True、calendar_degraded=False,QMT ONLINE、last_seq=acked_seq=1226717、last_error 为空。PMS_PLAN_VERSION_RECHECK 仍 False。全部备份及不可还原边界继续有效,明日 07:25 接续安排保持。
最终日志位于 155 `~/pms_logs/mean-validity-build-20261015.log`、`mean-validity-tests-20261015.log`、`mean-validity-deploy-20261015.log`、`mean-validity-ledger-verify-20261015.json`。四仓文档继续同步;只有文档更新无需再次重启容器。
## 2026-10-16 自然成交防双记补验
只读核对 10-15 12:50:47 修复后发生的 14 笔深科技卖出。成交发生在 13:31:36—13:41:45,合计 1,000 股、28,270.78 元,14 个成交号唯一且 processed=1。五条子单 D01、D02、D03、D04、D06 的实际成交数量分别为 400、300、100、100、100。汇总表中同五个委托的完成镜像已存在。
父指令 INS_20261015_000021SZ_EXIT_133100 的 exec_qty=1,000,状态 CONFIRMED。原持仓 1,000 股归零。批次 1302、1303 分别核销 400、600 股,其余历史批次逐字段保持。没有出现汇总镜像导致的第二次持仓扣减或执行进度累加。exec_avg_price 为 NULL,符合新增成交后不沿用旧均价的修复约定。
独立按原批次成本 36.182 元逐笔重算,分笔入账舍入到分后,两批已实现毛盈亏为 -3,237.32 和 -4,673.91 元,与账本一致。合计 -7,911.23 元。一次性按总金额减总成本为 -7,911.22 元,一分钱差异来自既有逐笔舍入。该票原批次是历史 RECON 成本,本次只判收修复后成交未双记,不宣称其全部早期历史成本已经重建。费用、旧批次与历史净值边界保持。
证据:155 ~/pms_logs/natural-fills-20261016-1035.json,读取的基线为 history-repair-after-write-20261015.json。核验脚本 verify_natural_fills_20261016.py 仅做 SELECT;没有调用回放、修复写入、采纳或交易。后续 10-16 仍无新成交,本项自然事件证据不再列为完全缺失。
## 2026-10-16 11:25 威胜信息自然卖出与网格数量边界
11:07:45—11:14:37 新增七笔威胜信息卖出,合计 1,400 股、32,933.49 元。成交号唯一且均已处理。两条父指令 STR20261016110502_688100SH_g、STR20261016111300_688100SH_g 各为目标 700、实际 700,均 CONFIRMED,执行均价为空。两条汇总镜像已被回放识别并跳过;11:08:01、11:14:00 原 worker 日志保留了逐笔入账与汇总 actions=0 的证据。
按修复后的原批次及既有核销顺序独立逐笔重算,所有批次的剩余数量、已核销量及已实现毛盈亏逐项一致。原 1,000 股 ADD 与 400 股 BASE 均已卖出,当前持仓 0、成本价空、已实现毛盈亏从 16.86 元变为 -252.01 元。七笔新增毛盈亏合计 -268.87 元。其中底仓 400 股已实现毛亏为 -282.93 元;这是按历史批次成本计算的金额,不能据此推断不卖出时的后续收益。费用仍未提供。
需与双记区分的实际策略缺陷:历史修复后的网格第 3 档为 1,000 股,配置 per_lot=700。11:05 首笔卖 700 后,11:09:02 运行器正确恢复档位剩余 300。11:13 卖出取量却只受配置档量和总可卖量限制,仍发 700,额外侵入底仓 400。11:16:01 原清场流程已取消该策略。修复 _eval_grid 的数量边界,保持触发条件与配置。真实成交不删除、不回填持仓,不作反向交易。
11:19 首次只读对账遇到旧持仓快照差异;11:19:31 完整快照更新后,11:20:04 diffs=[]、severity=OK。核验使用 apply_fix=False,未做数量调整。七条历史暂停指令继续保持,122+1 条原修复审计不重写。
证据位于 155 ~/pms_logs/。grid-incident-worker-20261016.log 的 SHA256 为 9fd10e419b46de2854a6e8e6793ec91de7218e531892d12ce0a97b1b9bfabd4c。grid-natural-20261016-1125.json 的 SHA256 为 24c22500168b1e9a9951cd3332a91e4385ca4027d6534cff3a7dcf4dfbd77f68。natural-fills-688100-20261016-1125.json 的 SHA256 为 929b2c4ace7ee12afb3bc411c2dc7e5521d62c5903294a9d7dc762a609d48200。独立逐笔结果在 688100-natural-fill-independent-20261016.json。修复及部署判收见同日四仓交接末节与桥台账 070。