tradingSystem/WS_INTEGRATION_STATUS.md

142 lines
7.4 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.

# ws 联调进度与交接(截至 2026-07-29 收盘)
> 给下一次接手的人(或下一个对话)用。三分钟读完就能接着干。
> 入口文档仍是 `README.md` / `POSITION_MGMT_DESIGN.md`V0.4 定稿,勿改)/
> `QMT_WS_PROTOCOL.md`V1.0 定稿)。本文只记**当前状态**和**踩过的坑**。
---
## 1. 一句话状态
**ws 通道全线打通并实测通过;账本被一次对账事故清空,待重建;`PMS_DISPATCH_MODE` 仍是
`shadow`,一次真实指令都没发过。**
---
## 2. 已验证(实测,非推演)
| 项 | 依据 |
|---|---|
| Ed25519 双向签名 | 与对端 `crypto.py` 逐字比对 + 双向验签实测 |
| 握手 / 心跳 / 重连退避 | 一天累计近百次重连seq 水位每次无缝续接 |
| `ack_seq` 累积确认§4.5 | 对端补上实现后PMS 不再降级停发 |
| **§6.1 序号补发** | `ws_smoke rewind --by 60` 人为造缺口,对端从 `last_seq+1` 起严格按序补回 60 条,约 15ms/条,一条不漏 |
| 委托全链路 | `place_order → ack → order_update(SUBMITTED) → trade → persist_result → order_update(FILLED) → funds_update`,状态推进与字段全对 |
| 入账分流 | 只有 `trade` 进账本(`processed=0`),其余一律「已消化」 |
| 页面 ws 监控页签 | 真浏览器实测含告警渲染、pong 过滤、轮询起停) |
协议三条安全底座(签名、补发、对账兜底)前两条已齐。
---
## 3. 当前状态与未决项
### 3.1 账本是空的
15:10 `daily_settle``reconcile()` 读到**下游持仓表空集**(对端模拟环境重启),
按铁律「以下游为准」把 22 个持仓、约 126 万市值全部核销。
- 机制已由代码确认(`reconcile` 无条件应用 fixes且空结果集会跳过数量列校验
- **未逐条核实 `pms_action_ledger` 里的 RECON 留痕**——想追认的话查
`SELECT * FROM pms_action_ledger WHERE action='RECON' ORDER BY decided_at DESC`
- `pms_lot``close_lot_qty` 核销的,行还在没删,理论上可反推,但**没有真相源可校验**
- 结论:接受损失,等对端模拟环境装上像样持仓后重新认领
### 3.2 下游只有垃圾
`trading_position` 现在只剩 `999999.SH × 2000`——我们自己测拒绝路径用的假代码。
现已被代码合法性校验过滤,不会再流进账本。
### 3.3 7 张风控 EXIT 已撤销
`origin_type=system` / `origin_id=intraday` / `from_signal=true`,理由「风控 SELL 置信度
95% ≥ 85%, 清仓」。持仓归零后指令悬空,已全部 cancel。**不是用户下的命令。**
### 3.4 组合基线(事故前,供重建时参考)
22 只 · 市值 1,258,357 / 成本 1,515,13916.9%)· 62.9% vs 60% 上限 ·
22 家 vs 15 家上限 · 16 只 ≤ 15%。
`688825.SH` 成本 8.66 / 现价 48.5 是**新股中签**,数据正确,非异常。
---
## 4. 今天新加的防线(都有测试锁死)
| 防线 | 位置 | 起因 |
|---|---|---|
| 对账爆炸半径 | `ledger_service._recon_blast_guard` | 下游读空清了 22 个持仓 |
| 下游代码合法性 | `command_spec.is_stock_code` | `999999.SH` 差点被补进账本 |
| 联调单不入账 | `ledger_service.consume_ws_trades` + `qmt_repo.SMOKE_PREFIX` | 对端按限价全成,每张测试单都在赌账本 |
| 卖出认领不到时告警 | `recon.map_trades_to_book` | 买入告警而卖出静默核销,不对称 |
| 补发期告警限流 | `runner._persist_upstream` | 一次补发刷 60 行 WARNING真乱序会被埋掉 |
| 时延采样跳过补发件 | `runner._track_skew` | 补发原样重放旧 ts误报 345 秒卡顿 |
---
## 5. 待对端QMT 侧)
`QMT_TEST_ENV_REQUIREMENTS.md` 里的 R1R4。**用户 2026-07-29 收盘时反馈「已实现模拟环境」,
下次开工第一件事是验证到底实现了哪几条。**
另有一项在 `QMT_SIDE_CONTROL_PATH.md` §5.1,未得到明确答复:
`_sender_loop` 的队列绑定(每轮重读 `self._send_queue`)与 socket 绑定(创建时绑死)
生命周期不一致,重连频繁时旧 sender 会从新队列取消息发往已关闭 socket消息静默丢失。
S3 密集测试正是高发场景,值得再确认一次。
---
## 6. 下一步
1. **确认对端实现了 R1R4 的哪几条**`ws_smoke place` 发一张远离市价的单,看是否还秒成)
2. **重建账本**:对端装上像样持仓 → `reset_ledger.py` → 认领
3. **S3 五类覆盖**:全成 ✅ / 部分成交 / 主动撤单 / 到期过期 / 各类拒绝
4. **盘中**跑 `scan-proposals?dry_run=true` + `exec-tick?dry_run=true`,看真实候选清单
5. 两边都确认,再切 `PMS_DISPATCH_MODE=ws`
第 4 步必须在**交易时段**做,原因见下。
---
## 7. 已知的坑(别再踩)
**`docker compose restart` 不重载代码。** 源码是 `COPY . .` 打进镜像的,`restart` 只是
重启老容器。改完代码要生效:`./scripts/deploy.sh`,或在开发机上
`cp docker-compose.dev.yml docker-compose.override.yml` 挂载源码(此后 `restart` 才有效,
但改 `requirements.txt` 仍需 build。**这个坑吃掉过一整轮排查。**
**`pms_ws_state.last_seq` 运行时手改无效。** 两条机制会撤销它:`_shutdown` 会把内存水位
刷回库;`_boot` 会用 `inbox_recover_watermark` 顺着 inbox 连续段走回去。要造缺口用
`ws_smoke rewind`(它会拒绝在 pms-ws 运行时执行,也会拒绝跨过已入账的成交)。
**对端模拟环境按限价全成、不校验代码。** 一张 99.99 的浦发卖单(实际价 10 元)会全成,
`999999.SH` 也会全成。**任何测试单都会产生成交回报**——所以联调单隔离(`SMOKE_` 前缀)
是必需的,不是可选的。
**收盘后的 dry run 看不出东西。** 取不到实时价时 `positions_view` 用摊薄成本兜底当现价,
安全垫齐刷刷归零DCA/ADD/TRIM 三个评估器全不触发,结果永远是 `candidates: 0`
判断"切了 ws 会发生什么"必须在交易时段跑。
**`pong` 带 seq符合 §5。** 5 秒一条、一天约 1.7 万行进 `pms_qmt_inbox`
看 inbox 记得用 `ws_smoke inbox --type trade` 过滤,否则全是 pong。
**协议 `reject` 不带 `instruction_id` 时是「协议级拒绝」**——拒的是消息本身不是委托,
走完全不同的分支。对端曾因未实现 `ack_seq` 而回 `unknown type ack_seq`,形成
「我们 ack → 对端拒 → 拒绝带 seq → 我们又要 ack」的 2 秒自激循环,通道显示 ONLINE
但只在刷 reject。
---
## 8. 文档欠账
**✅ 已清2026-07-30用户批准**`QMT_WS_PROTOCOL.md` §11 变更记录里 V1.0 那行原本
排在 V0.9.2 上面,而 `ack_seq` 恰恰是 V0.9.2 补进来的——对端从上往下读到「V1.0 定稿」
就停,`ack_seq` 因此被漏掉,直接导致了 §7 那个自激循环。现已改为
V0.9 → V0.9.1 → V0.9.2 → V1.0。**纯调两行顺序,条款一字未动**(总行数不变,可 `git show` 核)。
**遗留一项(待定夺,暂不动)**:端点在 `README.md`「ws 通道实现清单」与协议 §11 的 V1.0
那行里都写作 `ws://192.168.16.98:8080`,而 `config/settings.py:94` 实际是
`ws://192.168.16.98:9443/pms`(提交 `0a1a932 调整端口`)。**代码是对的、文档是旧的**
联调不受影响(运行时读 settings / 运行参数,不读文档),但下一个接手的人会按文档去连 8080。
改的是定稿协议正文里的一处事实值,故未擅动。