102 lines
10 KiB
Markdown
102 lines
10 KiB
Markdown
# IPO、Trend、ZT 策略审计
|
||
|
||
复审日期:2026-09-13。基线:`67b46ca0492cccdda8a41263b727a56df4a637c1` 加本次读取的工作区代码,包含手工修改及未提交文件。保留原文件名以便沿用引用。
|
||
|
||
范围:三个策略及直接相关的委托簿、状态存储、SDK、服务端成交映射、网格和快照模块。本次只更新报告,不修改策略、测试或生产数据,不连接交易账户。
|
||
|
||
仅保留复审后仍需处理的问题。已修复及用户已标记忽略的条目、历史复现和过时统计已移除;编号沿用原报告。部分修复的问题仅描述尚未解决的策略范围。
|
||
|
||
## 结论
|
||
|
||
**待处理:P0 0 项、P1 3 项、P2 2 项。** IPO 本次未确认新的待处理问题。
|
||
|
||
| 编号 | 级别 | 范围 | 问题 |
|
||
|---|---|---|---|
|
||
| P1-07 | P1 | ZT | 跨轮分批归档覆盖已有持仓数量及成本 |
|
||
| P1-08 | P1 | Trend | 超时撤单仍覆盖手工单及其他非 IPO 策略订单 |
|
||
| P1-09 | P1 | ZT | 首次初始化重复处理已包含在快照中的成交 |
|
||
| P2-01 | P2 | Trend | 止盈峰值跨仓位沿用 |
|
||
| P2-07 | P2 | 共用模块测试 | 旧构造调用及 State 契约变化导致回归验证失效 |
|
||
|
||
P0 表示已确认的紧急严重风险;P1 表示主要交易控制或记账问题;P2 表示特定条件下的可靠性和回归验证问题。本次未确认 P0。
|
||
|
||
## P1
|
||
|
||
### P1-07:ZT 跨轮分批归档覆盖已有持仓数量及成本
|
||
|
||
**位置:** [state.py](../py-client/libs/state.py) 的 `sync_deals()`、`merge_deals()` 及第 264–265 行;[ZT 同步适配](../py-client/strategy/zt/boot.py) 第 97–115 行。
|
||
|
||
**证据:** 本轮按用户明确的上游保证,视 `order_sys_id` 为每笔成交的唯一值、本地委托编号非空,不再将重复编号假设作为确定缺陷。`merge_deals()` 仅汇总未归档成交,但买入归档将其数量和价格直接覆盖已有仓位。逐笔等待超过 30 秒不能保证同一委托的所有成交落在同一轮。
|
||
|
||
**本轮复现:** 使用临时数据库和真实 `_sync_state()`,两笔成交分别使用不同且唯一的 `order_sys_id`,共享同一个本地委托编号。第一轮买入 40 股并归档,第二轮加入 60 股成交,账户快照为 100 股。最终保存两笔原始成交,持仓却仅为 60 股,证券进入 `blocked_codes`。两笔均已归档,重复查询不会重新汇总为 100 股。
|
||
|
||
**影响与边界:** 原始成交保留,但派生持仓数量及成本不正确。当前适配会在数量不符时暂停对应证券交易,降低继续下单的风险,但不能修复已归档的错误状态。本项不依赖成交编号重复,也不要求改变上游身份契约。
|
||
|
||
**修改建议:** 将未归档成交作为持仓增量处理,按已有数量和金额计算加权成本,或建立等价且可重算的订单汇总机制。不要将 30 秒延迟当作订单全部成交的证明。当前授权不允许修改 `state.py`,本项仅记录问题,不实施修复。
|
||
|
||
**验收:** 同一本地委托的 40、60 股分别在不同轮次归档后合计 100 股;不同价格成交得到正确加权成本;重复同步及重启不重复记账;原始成交全部保留;数量一致后证券恢复交易。
|
||
|
||
### P1-08:Trend 超时撤单仍覆盖手工单及其他策略订单
|
||
|
||
**位置:** [Trend boot.py](../py-client/strategy/trend/boot.py) 第 40、119 行;[OrderBook.refresh()](../py-client/libs/order.py) 第 61、77–88 行。
|
||
|
||
**证据:** Trend 启动及每轮运行均传入完整账户委托,未指定 `cancel_prefix`,默认值为 `None`。所有满足超时及可撤状态的非 `IPO-*` 委托都会进入撤单路径,包括空备注手工单和 `zt-*` 订单。
|
||
|
||
**本轮复现:** 四笔超时、状态 50 的模拟订单分别使用 `TREN-BUY-*`、`zt-base-*`、`IPO-*` 和空备注。按 Trend 当前调用方式刷新,Trend、ZT 和手工订单触发撤单,IPO 未触发;四笔均保留缓存。
|
||
|
||
**影响与边界:** 运行 Trend 可能干扰人工或其他策略希望继续等待的委托。本项仅保留 Trend 范围;当前 IPO 排除有效,不再沿用旧报告的 IPO 误撤结论。Mock 只能证明撤单调用,不能证明柜台受理。
|
||
|
||
**修改建议:** 若 Trend 只管理自己的订单,应明确归属后限制自动撤单,同时保留全账户在途防重。Trend 开仓使用信号前缀、持仓管理使用 `TREN-*`,不能简单改为单一 `trend-` 前缀。如果全账户非 IPO 撤单是预期行为,应明确记录这一范围后再决定是否关闭本项。
|
||
|
||
**验收:** Trend 自有超时订单能撤,手工单和其他策略订单不被 Trend 自动撤销;全部活动委托继续参与防重。
|
||
|
||
### P1-09:ZT 首次初始化重复处理已包含在快照中的成交
|
||
|
||
**位置:** [ZT 同步适配](../py-client/strategy/zt/boot.py) 第 97–103 行;[state.py](../py-client/libs/state.py) 的 `sync_state()`、`archiving()`。
|
||
|
||
**证据:** 初始化依次调用 `sync_state(positions)`、`sync_deals(deals)` 和 `archiving()`。持仓快照已经包含历史成交,但归档仍重新处理这些成交,没有初始化快照与后续增量的边界。该问题位于本轮策略适配流程,不应沿用旧 `sync_account()` 的初始化验收结论。
|
||
|
||
**本轮复现:** 空数据库初始化,账户净持仓 100 股,当日历史为买入 200、卖出 100,两笔均超过 30 秒且编号唯一。真实 `_sync_state(..., initialize=True)` 先写入 100 股快照,随后买入归档将底仓覆盖为 200 股,卖出 100 股因不符合当前卖出分支而归档失败。最终记录 200 股,证券被暂停交易。
|
||
|
||
**影响与边界:** 首次启动即可能产生错误持仓和未归档记录。数量核对可以阻止对应证券继续交易,但不能自动恢复正确基准。该复现包含买卖混合历史,不能由单笔买入初始化测试覆盖。
|
||
|
||
**修改建议:** 明确已核对快照覆盖的成交集合与后续增量边界,使历史成交能够保存而不重复改变初始持仓;同时设计初始化失败后的重试行为。保持 `state.py` 不变的限制下,先明确策略侧能够使用的初始化接口,不能把三个独立调用视为原子同步的等价替代。
|
||
|
||
**验收:** 净持仓 100、历史买入 200/卖出 100 初始化后仍为 100;已包含的历史成交不重复记账;延迟超过 30 秒才进入存储的历史回报不重复影响基准;初始化失败、重复启动及重启恢复均保持一致。
|
||
|
||
## P2
|
||
|
||
### P2-01:Trend 止盈峰值跨仓位沿用
|
||
|
||
**位置:** [Trend positions.py](../py-client/strategy/trend/positions.py) 第 120–121、198–199 行;[Trend boot.py](../py-client/strategy/trend/boot.py) 第 60、113–119 行;[网格跟踪器](../py-client/libs/grid_take_profit.py) 第 44–74 行。
|
||
|
||
**证据:** Trend 使用普通 `GridTrailingTracker`,键仅包含账户及证券。清仓、重新建仓和补仓成本变化时,没有清理旧峰值;每轮快照更新也未同步网格生命周期。
|
||
|
||
**本轮复现:** 使用真实 `manage_positions()`、真实 tracker 和模拟委托簿,依次输入成本 10 元、价格 12 元的持仓,价格 11.9 元的同一持仓,空持仓,再输入成本 10 元、价格 11 元的新持仓。各步新增卖出调用次数为 `0、1、0、1`。新仓位第一次观察仍沿用旧峰值触发卖出。
|
||
|
||
**影响:** 重新建仓或成本基准变化后,当前收益率可能被当成旧仓位的回撤,提前触发止盈。本项仅保留 Trend 范围。
|
||
|
||
**修改建议:** 根据已确认的清仓、重新建仓及成本变化同步止盈基准。不要仅在提交成功时清理,避免撤单或部分成交丢失有效峰值。
|
||
|
||
**验收:** 新仓位首次观察只建立峰值;同一仓位正常回撤仍触发卖出;未成交提交、撤单及同一成本下部分卖出不误清理。
|
||
|
||
### P2-07:旧构造调用及 State 契约变化导致回归验证失效
|
||
|
||
**位置:** [test_deal_model.py](../py-client/tests/test_deal_model.py) 第 71 行;[OrderBook 构造函数](../py-client/libs/order.py) 第 35–38 行;[ZT 状态测试](../py-client/tests/test_zt_state.py)、[ZT 审计回归测试](../py-client/tests/test_zt_audit_fixes.py)、[归档测试](../py-client/tests/test_state_archiving.py)。
|
||
|
||
**本轮复现:** `ActiveOrders('trend')` 仍在 `max(1, lock_timeout_sec)` 处抛出 `TypeError`。另有多项测试调用已删除的 `sync_account()`、使用当前成交表不接受的 23/24 方向,或直接写库后未刷新 `archiving()` 依赖的缓存。完整发现并执行 86 项测试,47 项通过,记录 13 条断言失败、30 条错误;后两项含子测试记录,不能直接相加推算失败用例数。
|
||
|
||
**影响与边界:** 原报告“85/86 通过、ZT 相关全部通过”已失效。当前生产入口使用无参 OrderBook,ZT 已通过 `_sync_state()` 对接现有接口,不再因调用已删除的 `sync_account()` 而启动失败。失败记录既包含契约过时,也包含真实记账问题,不能全部视为生产缺陷或全部通过改断言消除。
|
||
|
||
**修改建议:** 按现有接口更新测试入口和数据准备,保留有业务意义的持仓一致性、原始成交保留、失败回滚及重启断言。新增对真实 ZT 适配入口的分批成交和初始化混合历史验证。不要为旧测试恢复已删除的构造参数或简单放宽业务断言。
|
||
|
||
**验收:** 回归测试适配当前契约,默认刷新、ZT 范围过滤、IPO 排除和在途防重断言有效;P1-07、P1-09 在修复前能失败、修复后通过;统计明确区分测试用例和子测试错误记录。
|
||
|
||
## 本轮验证及边界
|
||
|
||
- 完整执行 `unittest` 发现的 86 项测试:47 项通过,13 条断言失败、30 条错误,错误统计包含子测试,详情见 P2-07。IPO 专项另行执行 15 项,全部通过。
|
||
- 使用真实 ZT `_sync_state()`、State、OrderBook 和 Trend `manage_positions()`/tracker,配合模拟客户端及临时数据库,复现 P1-07、P1-08、P1-09、P2-01。
|
||
- 验证使用模拟客户端及临时 SQLite 数据库,未发送真实委托、撤单或采集请求,未修改生产数据库和锁文件。
|
||
- 按用户确认采用上游成交编号唯一、本地编号非空的前提;不再把重复编号假设列为确定缺陷。验证未证明真实柜台受理结果。
|
||
- 不新增旧 IPO 编号兼容要求,不恢复已忽略事项,不将已修复内容作为待办重复列出。
|