docs: 基金转换(convert)设计阶段成果入库

此前 AIcoding 第 1~3 步的产出仅存在于本地磁盘、未纳入版本库,本次一次性入库,
补齐「设计可追溯」。本提交为纯文档 + 一个实算脚本,**代码零改动**,接口契约与表结构不变。

入库 6 个文件:

- docs/PRD/PRD-基金转换交易.md(v0.9 · 需求权威:FR-C1~C16 / §4 表结构 / §5 接口 / §9 验收)
- docs/项目框架设计/架构设计-基金转换交易.md(v1.0 · 实现依据:D1~D20 / §5 事务补偿 /
  §11.1 DB 账号分离 / §15 任务映射 T-0~T-13 与 §15.1 依赖拓扑)
- docs/项目框架设计/基金转换-审查意见处置表.md(33 条外审处置 + §九 用户拍板记录)
- docs/项目框架设计/评审待办-风控主架构与基金转换.md(评审逐条判定 + 核实证据)
- docs/交接文档-基金转换.md(本线交接入口,读完即可开工)
- scripts/dev/calc_convert_demo.py(PRD §5.3 主示例实算回填脚本——派生值禁手算)

门控:PRD 已定稿并经独立评审闭环(M-7 已满足);架构 v1.0 独立评审 13 条建议 0 悬空。
下一步:AIcoding 第 4 步「产出开发计划」,输入为架构 §15 / §15.1。
This commit is contained in:
2026-09-10 11:29:55 +08:00
parent fffb78a11c
commit ea9a132a40
6 changed files with 2413 additions and 0 deletions
@@ -0,0 +1,128 @@
# 基金转换 · 外部审查意见处置表
> 日期:2026-09-10 · 审查结论原文:**❌ 不建议进入编码**(33 条:B×5 / I×7 / M×7 / L×7 / S×3 + 自检九问复核)
> 处置原则(用户红线):**逐条判定 = 接受 / 修正性接受 / 驳回**;驳回与修正性接受必须给出核实过的证据。
> 判定基准:**贴近真实业务**(证监会公告〔2025〕22 号 + 10 家基金公司转换公告,见架构 §0.5 来源清单)
> 结果:**接受 27 · 修正性接受 5 · 驳回 0 · 合并 1**(S-1 并入 B-1),另**自查补 2 条**(X-1 / X-2)
> 后续:本表结论 → PRD v0.8 → 架构设计 v1.0 → 独立 AI 评审 → 第 4 步开发计划
---
## 一、阻断级(B-1~B-5)
| # | 审查意见 | 判定 | 处置与证据 |
| --- | --- | --- | --- |
| B-1 | PRD「已定稿」与架构 §0 合规查证矛盾 | **接受** | PRD 前言改「**v0.7 已冻结**(架构校准发现合规问题),待出 v0.8」;架构 v0.2 加冻结声明;新增门控:PRD v0.8 未产出不得进第 4 步 |
| B-2 | 申购费率种子全超法定上限 | **接受** | 按架构 §13.3 重定:货基 0 / 债基·指数 **0.0030** / 其他混合 **0.0050** / 主动偏股 **0.0080**。依据 22 号文 §8。§5.3 示例、§9 第 19 条同步重算 |
| B-3 | 赎回费 7–30 日档低于法定下限、边界与 180 日不符 | **接受** | 新档:`<7 日 1.5%` / `7–30 日 1.0%` / `30–180 日 0.5%` / `180–365 日 0.25%` / `≥365 日 0`。依据 22 号文 §10。PRD §2.1、07 种子、§5.3、§9 同步 |
| B-4 | 份额 4 位向下舍去,与真实(2 位四舍五入)不符 | **接受** | 改 **2 位 `ROUND_HALF_UP`**;`rounding_diff` 语义改「可正可负,在基金资产列支」。`core_share_lot.remain_qty` 存储精度保持 DECIMAL(18,4)(真实 TA 内部精度高于展示),**计算与展示按 2 位** |
| B-5 | T+1 确认时序与真实不符 | **修正性接受**(范围需收窄,方向需澄清) | **只改「转入端新批次」的 `confirmed_at = T+1`**。证据:PRD §4.4 表格已区分三行(转入新批次 / 普通申购新批次 / 转出·赎回不产新批次);转出批次是**历史日期**,`hold_days = (交易日 − confirmed_at).days` 恒为正,不受影响。<br>**方向澄清**:审查称「当前设计使持有期比真实**多** 1 天」——实际相反。真实持有期**自确认日(T+1)起算**,比从 T 起算**少** 1 天(持有期更短、费率档更严格)。PRD Q1 精确化为:「不模拟 T+1 的**权益登记延迟**(转入份额立即可用),但 `confirmed_at` 取 T+1」 |
## 二、重要级(I-1~I-7)
| # | 审查意见 | 判定 | 处置与证据 |
| --- | --- | --- | --- |
| I-1 | 补差费口径未拍板(A 费率差 vs B 价外法两端差) | **接受** | PRD 新增 **Q9**:明确本期选 **B(价外法两端差)**,理由:中国申购费标准算法即价外法(净申购额 = 金额/(1+费率)),B 与之数学自洽,且为国投瑞银/东海/华商/金鹰/鹏华新版主流;A 以 `convert_diff_fee_mode` 配置保留,两口径均有单测。PRD §2.1 补双口径公式与数值差异示例 |
| I-2 | `hold_days` 缺边界定义 | **接受** | PRD §12 补:`hold_days = (交易日 − confirmed_at).days`(**不含申请日**);分档**左闭右开** `[min, max)`;**满 7 日归 7–30 日档(1.0%)**,满 30 日归 30–180 日档(0.5%)。与架构 §7 同步 |
| I-3 | 阶段二补偿自动/手动未声明 | **接受** | PRD §7.1 补:一期为「**监控告警 + 人工脚本补偿**」(可接受简化),SLA 24h;`cleanup_pending_convert.py` 由定时/手动触发。**另补审查未提的缺口 X-1:引擎执行时序**——阶段一提交后、阶段二之前同步跑引擎;引擎失败沿用现有 `engine_error` 模式(架构 §X) |
| I-4 | 强制全转触发条件不清晰 | **接受** | PRD §12 补判断流程图:① 份额足够(Σ remain_qty)→ ② 最低转出份额(申请 == 全部可转份额时**豁免** min_redeem)→ ③ **余额 < min_hold_qty 才触发强制全转**。审查例(持 6000、申请 5000、min_hold_qty=1000):余额 1000 **不低于**下限 → **不触发**,正常转 5000 |
| I-5 | `min_hold_action` 两种模式 PRD 未回应 | **接受** | 两种都做:`core_product` 加 `min_hold_action ENUM('force_transfer','force_redeem') DEFAULT 'force_transfer'`。依据:阈值大(100/500/1000 份)→ 强制赎回(财通/汇添富);阈值小(0.1/1 份)→ 强制全转(中银/东海) |
| I-6 | 份额精度对现有 510 用例影响未评估 | **接受(已实测,影响 = 0)** | 实测 `grep`:现有测试仅 **2 个文件**触碰 `core_holding`(`test_chat_tools.py`、`test_concentration_c4.py`),且只断言 `market_value` / `quantity`,**无任何份额精度断言** → 改动影响为 0。<br>**但实测发现真坑**:sqlite `_ddl.py:53` 列名 `quantity`,MySQL DDL 为 `qty`——convert 首次要 `UPDATE core_holding.qty`,该失配会在本次开发**第一次暴露**。T-1 必须补 `qty`/`cost_amount`/`as_of`/`pnl_pct` |
| I-7 | RISK-006 转换后集中度未定义 | **接受** | 明确:引擎在阶段一**提交后**执行,`concentration_profile` 读到的是**已更新**持仓(含转入端新持仓、已扣减的转出端)→ 转换的集中度连带效应天然被覆盖,无需新增规则 |
## 三、中等级(M-1~M-7)
| # | 意见 | 判定 | 处置 |
| --- | --- | --- | --- |
| M-1 | 重试判定 SQL 依赖 `idx_convert_group` | 接受 | 开发计划门控:**T-1(DDL 含索引)必须先于 T-7(幂等重试逻辑)** |
| M-2 | 409 `LOT_CONFLICT` 重试策略缺失 | 接受 | PRD §5.4 增行:调用方自行重试,建议最多 3 次,间隔 100/200/400ms |
| M-3 | 幂等键格式未指定 | 接受 | 客户端生成、格式自由、**≤64 字符**、建议含时间戳便于排查 |
| M-4 | `out_amount` 与 `convert_amount` 语义重叠 | 接受 | §5.3 注释分组:`out_amount` = 原始转出额;`convert_amount` = 扣赎回费后可用于转入的金额 |
| M-5 | `convert_batch_max_lots=200` 超限无错误码 | 接受 | 超限返回 **400 `TOO_MANY_LOTS`** |
| M-6 | `pnl_pct` 更新口径未同步 | 接受 | PRD FR-C13 补:`pnl_pct = (market_value − cost_amount) / cost_amount`,`cost_amount = 0` 时置 0 |
| M-7 | PRD 与架构同步依赖未管理 | 接受 | 硬性门控写入 PRD v0.8 前言 + 架构冻结声明(已加) |
## 四、低优先级(L-1~L-7)
| # | 意见 | 判定 | 处置 |
| --- | --- | --- | --- |
| L-1 | 架构校准结论未回流 PRD 评审记录 | 接受 | PRD v0.8 §13 新增「**架构校准轮(2026-09-10)**」记录,附本处置表索引 |
| L-2 | 主审计应关联组而非单条 | 接受 | `input_summary` **以 `convert_group_id` 为主键关联**,附 `out_trade_id` / `in_trade_id` |
| L-3 | 响应示例字段过多 | 接受(可选) | §5.3 按「基础 / 转出端 / 转入端 / 规则引擎 / 审计」分组注释 |
| L-4 | 「仅 R-02 可阻断」表述歧义 | 接受 | 加注:**503 / 409 为技术故障码,不适用「仅 R-02 可阻断」业务规则** |
| L-5 | 撤单未预留字段 | 接受 | `risk_convert_detail` **建表即加** `status ENUM(...,'cancelled')` 与 `cancelled_at`(新表,零 ALTER 成本) |
| L-6 | 种子版本管理 | **修正性接受** | 不单独建 CHANGELOG 文件(维护成本高、易与 SQL 漂移);改为**每个种子 SQL 文件头加版本注释块**(版本/日期/依据/变更摘要) |
| L-7 | `rebuild_lots.py`「回滚」措辞误导 | 接受 | 改措辞为「**批次快照重建,不是交易回滚**」 |
## 五、结构问题(S-1~S-3)
| # | 意见 | 判定 | 处置 |
| --- | --- | --- | --- |
| S-1 | 前言「已定稿」不符 | — | **并入 B-1** 处理 |
| S-2 | 评审记录过长(≈200 行,占 22%) | **修正性接受** | **不移到附录**。理由:用户红线要求「PRD 必须经独立 AI 审查、评审闭环可追溯」,删详录会破坏可追溯性。折中:正文前加「**阅读导航**」(新读者路径 + 每轮 3 行决策摘要表),详录保留原处 |
| S-3 | 待确认项需新增 | 接受 | 新增 **Q9 补差费口径 / Q10 份额精度 / Q11 T+1 建模 / Q12 min_hold_action** |
## 六、自查补充(审查未覆盖,本次一并收口)
| # | 发现 | 判定 | 处置 |
| --- | --- | --- | --- |
| X-1 | **引擎执行时序未定义**——PRD §7.0 七步里没有「跑规则引擎」这一步 | 补充 | 插入为第 6.5 步:阶段一(Core 提交)→ **阶段 1.5:同步跑引擎(读全量两条流水 + 更新后持仓)** → 阶段二(回写 + 审计)。引擎异常沿用 `engine_error` + 本地日志,不阻断已成立的交易 |
| X-2 | **两条流水的 `trade_status`**——真实 T 日申请为「待确认」,T+1 才 confirmed;而 `_eligible` 只认 confirmed | 补充 | 定:两条流水**直接写 `confirmed`**(沿用模拟库现有行为,否则规则引擎读不到),折算结果以 `estimated=true` 标识;PRD 注明此为「不模拟 T+1 权益登记延迟」的连带简化,与 Q1 一致 |
## 七、自检九问复核(审查评价 + 本轮补正)
| # | 问 | 审查评价 | 本轮补正 |
| --- | --- | --- | --- |
| 1~4 | 谁写 / 谁读 / 枚举 DDL / 跨库事务 | ✓ | 第 4 问「完整但补偿路径为手动」→ I-3 已明确一期手动 + SLA 24h |
| 5 | 种子数据 | ⚠️ 费率超上限 | B-2 / B-3 已重定合规值 |
| 6~8 | 并发 / 汇总语义 / 向后兼容 | ✓ | — |
| 9 | 示例自证 | ⚠️ 数字基于错误口径 | **T-2b:`scripts/dev/calc_convert_demo.py` 用生产同一套 `calc.py` 实算回填,禁止手算** |
| **10** | **(新增)外部事实核验** | — | 本次 4 项合规硬伤全部源于「只做内部自洽、未做外部核验」。新增第 10 问:涉及**费率 / 时限 / 资质 / 精度**等**行业有明文规定**的量,必须联网核对现行法规与真实业务公告并列来源 |
---
## 八、PRD v0.8 修订范围(下一步执行清单)
| 序 | 修订点 | 对应条目 |
| --- | --- | --- |
| 1 | 前言状态改「v0.7 已冻结,待 v0.8」+ 门控声明 | B-1 / M-7 |
| 2 | §1.4 非目标:撤单改「二期,字段已预留」 | L-5 |
| 3 | §2.1 补差费:双口径公式 + 选 B 的理由 + 数值差异例 | I-1 |
| 4 | §2.1 赎回费档位按 22 号文重排 | B-3 |
| 5 | §2.5 份额 2 位 `ROUND_HALF_UP`,误差在基金资产列支,`rounding_diff` 可正可负 | B-4 |
| 6 | §4.1 `risk_convert_detail` 加 `cancelled` / `cancelled_at`;`core_fee_rule` 加 `to_fund_ratio` | L-5 / D16 |
| 7 | §4.2 `core_product` 加 `min_hold_action` | I-5 |
| 8 | §4.3 种子费率改合规值;赎回费种子改新档 | B-2 / B-3 |
| 9 | §4.4 / Q1:`confirmed_at = T+1`(仅新批次),精确化表述 | B-5 |
| 10 | §5.3 响应:示例由脚本实算回填 + 分组注释 + 字段语义 | B-2 / M-4 / L-3 / T-2b |
| 11 | §5.4 增 `TOO_MANY_LOTS` + 409 重试策略 + 503/409 非业务阻断注 | M-2 / M-5 / L-4 |
| 12 | §7.0 插入「阶段 1.5 跑引擎」 | X-1 |
| 13 | §7.1 补偿路径:一期手动 + SLA 24h + 触发者 | I-3 |
| 14 | §7.3 审计关联 `convert_group_id` | L-2 |
| 15 | §10 新增 Q9~Q12 | S-3 |
| 16 | §12 补 `hold_days` 定义 + 强制全转判断流程图 | I-2 / I-4 |
| 17 | §13 新增「架构校准轮」评审记录 | L-1 |
| 18 | 种子 SQL 文件头加版本注释块 | L-6 |
| 19 | FR-C13 补 `pnl_pct` 口径;`rebuild_lots.py` 措辞订正 | M-6 / L-7 |
| 20 | 全文:示例数字按新费率/精度/时序重算(脚本实算) | B-2~B-5 |
> **执行状态(2026-09-10)**:以上 **20 项已全部落于 PRD v0.8**。
> 示例数字由 `scripts/dev/calc_convert_demo.py` 实算回填(口径 B:`diff_fee = 252.40`、`in_qty = 53456.95`、`rounding_diff = -0.0026`;口径 A 对照 `253.91` / `53455.36`)。
> **下一步**:架构设计 v0.2 按本表结论转 **v1.0** → 送独立 AI 评审 → 第 4 步开发计划。
---
## 九、用户拍板记录(2026-09-10)
行业多口径 2 项 + 架构取舍 3 项,用户确认「**按建议**」:
| # | 事项 | 拍板结果 | 依据 |
| --- | --- | --- | --- |
| 1 | 补差费默认口径 | **口径 B(价外法两端差)** | 与申购费价外法数学自洽;A 属近似写法,保留为 `convert_diff_fee_mode` 配置项 |
| 2 | 最低持有余额处置 | **两种都做**(`min_hold_action` ENUM 两值 + 种子按产品设定) | **法规无强制**(22 号文不涉及最低持有余额),两种均为真实基金合同约定 |
| 3 | P1 普通申赎无批次是否跳过 | **不跳过**:批次表覆盖全部交易类型;遇历史持仓无批次 → **兜底补建**,保证 FIFO 可执行、两表一致 | 跳过会导致批次表与持仓表永久失配 |
| 4 | P2 转出归零保留还是删行 | **保留(`remain_qty = 0`),不删行** | 与「审计只 INSERT」精神一致,保可追溯;避免 `core_convert_lot_detail` 断链 |
| 5 | P3 Core 侧明细是否对外查询 | **仅补偿,不对外查询** | 保持 Core 只读边界最小;对话侧走 agent 库粗粒度详情 |
> **落地位置**:第 1、2 项属业务口径 → 已落 **PRD v0.8**(§2.1.1 / §4.2 / §10 Q9·Q12,§10 已加拍板状态说明);
> 第 3~5 项属架构层 → **将在架构设计 v1.0 落实**(对应架构 v0.2 的 P1~P3 待拍板项)。