Files
group_xinghuo_jinrong/docs/交接文档-基金转换.md
GaoYiYuan_0626 497cee289f docs: 交接文档合并为单一入口 + 全仓指向统一到 交接文档.md §A/§B/§C
背景:交接文档的定位是「给 AI 接手用的入口」,此前三份分散(项目根 交接文档.md
+ docs/交接文档-基金转换.md + docs/交接文档-架构改进.md),且 docs/ 两份停留在旧版
(v1.0 / v1.1,不含 T-0~T-2b、609 passed、D20 实施等进度),新会话极易被误导。

改动:
- 三份合并为项目根 交接文档.md v3.0(545 行 = §0 公共层 + §A 风控主线 + §B 基金转换线
  + §C 架构改进线);该文件在 .gitignore:47 内,按用户要求不入库(交接文档只留本地)
- 全仓指向统一到 交接文档.md §A/§B/§C:AGENTS.md(含顶部新增「接手先读交接文档.md」)、
  docs/memory/{MEMORY,TODO,FRAMEWORK,ITERATION}、PRD-架构改进与稳定性加固、
  开发计划-架构改进、TODO-架构改进、开发计划-基金转换交易
- 两份 docs/交接文档-*.md 加「已废弃(2026-09-10)· 勿读」横幅并指向新入口,
  保留作历史留档(不删除)
- 记录事故:对 docs/ 下中文名文件使用 git rm 会静默抹除整个 docs/ 目录(复现 2 次、
  退出码 0),已零损失恢复;纪律写入 交接文档.md §0.4 与工作区记忆

无代码改动;pytest 609 passed / 3 skipped。
2026-09-10 15:04:13 +08:00

13 KiB
Raw Permalink Blame History

交接文档 · 基金转换(convert)交易线

⛔ 本文件已废弃(2026-09-10)—— 请勿据此开工

内容已全部并入项目根 交接文档.md §B(三线合并版唯一交接入口),且本文件停留在 v1.0(设计阶段完结), 不含 T-0 / T-0b / T-1 / T-2 / T-2b 的落地进度与 609 passed 现状。 ⚠️ 下方「下一步是第 4 步开发计划」等表述早已过期(第 4 步、第 5 步前两批均已完成)。 正确入口:交接文档.md §B —— 本文件仅作历史留档,内容已过期,勿读。

版本:2026-09-10 v1.0(设计阶段完结,交接给开发会话) 用途:给接手本线的 AI / 人。读完本文即可开工,不需要重读全部代码。 代码基线:分支 risk-control-agent(HEAD 以 git log -1 为准);pytest 510 绿。 ⚠️ 本线代码尚未改动一行 —— 当前只完成了「讨论需求 → PRD → 架构设计」三步(AIcoding 流程第 1~3 步),下一步是第 4 步开发计划。


⚡ 当前状态(一句话)

项 状态
第 1 步 讨论需求 ✅ 完成(多轮讨论 + 外部事实核验)
第 2 步 PRD ✅ v0.9 定稿(33 条外审闭环 + 2 处架构回填)
第 3 步 架构设计 ✅ v1.0 定稿(独立评审通过,13 条建议 0 悬空)
门控 M-7(PRD 未定稿不得进第 4 步) ✅ 已满足
第 4 步 开发计划 ⬜ ← 下一步做这个
第 5 步 todo 开发 / 第 6 步 集成测试 ⬜ 未开始
代码改动 ⬜ 零(架构里的 T-1 之后才有代码)

开工前必须先做的两件阻断前置:T-0(sqlite/MySQL 列名统一)与 T-0b(DB 账号分离)。


1. 五分钟背景

1.1 项目是什么

XingHuo 智能财富管家:金融四 Agent(客户财富 / 代理人 / 数据分析 / 风控)共用数据层与合规底座,四 Agent 不互调 LLM。

技术栈:Python 3.13 + FastAPI + SQLAlchemy Core(text SQL,无 ORM)+ MySQL 双库(jinrong_core 模拟 Core / jinrong_agent 业务)+ Redis + Milvus + Neo4j。

1.2 本线要做什么

docs/PRD/PRD-风控监测Agent.md 的 FR-1 一期显式拒收 convert(trade_gateway 里直接 400 拒绝)。 本线把它放开为可交易:新增基金转换(转出 A 基金 → 转入 B 基金)的完整链路。

1.3 业务本质(看懂这段才不会写错)

一次基金转换 = 一次赎回(转出)+ 一次申购(转入),但不是两笔独立交易:

  • 未知价法:按申请当日净值计价,T 日申请 → T+1 权益登记(扣转出、登转入)→ T+2 可用
  • 逐批次计费:按 core_share_lot 的批次,每批按各自持有期查赎回费率(<7 日 1.5% / 7–30 日 1.0% / 30–180 日 0.5% …)
  • 先进先出(FIFO):注册日期在前的批次先转出
  • 单笔计算法:当日多笔转换不合并计费
  • 补差费:转入费率高于转出费率时补差额(口径 B 为默认,见 §3)
  • 份额/金额一律 2 位四舍五入,误差「在基金资产列支」(rounding_diff 可正可负)

合规基准:证监会公告〔2025〕22 号《公开募集证券投资基金销售费用管理规定》(2026-01-01 施行) —— 申购费率上限(主动偏股 ≤0.8% / 其他混合 ≤0.5% / 指数·债券 ≤0.3% / 货基 0)、赎回费下限(<7 日 ≥1.5% …)、赎回费全额计入基金财产。


2. 设计资产(读这四份就够,不用翻代码)

顺序 文档 看什么
1 本文 全局、状态、坑、禁止事项
2 docs/项目框架设计/架构设计-基金转换交易.md(v1.0) 怎么实现:§2 目录 / §3 时序 / §4 决策 D1~D20 / §5 事务补偿 / §7 计算口径 / §8 契约 / §9 DDL 落点 / §10 测试 / §11 配置 / §12 风险门禁 / §15 任务映射
3 docs/PRD/PRD-基金转换交易.md(v0.9) 需求权威:场景 FR-C1~C16 / §4 表结构 / §5 接口 / §7 流程与事务 / §9 验收
4 docs/项目框架设计/评审待办-风控主架构与基金转换.md 评审逐条判定与核实证据(§二)+ 交叉点(§三)+ 执行期风险(§四)
5 docs/项目框架设计/基金转换-审查意见处置表.md 33 条外审处置 + §九 用户拍板记录

3. 已拍板的关键决策(不能改,改了会破坏已验证方案)

# 决策 口径
补差费 口径 B(价外法两端差)为默认,A(费率差法)以 convert_diff_fee_mode='rate_diff' 保留
份额精度 2 位 ROUND_HALF_UP(不是 4 位、不是向下舍);金额同
T+1 确认 建模:confirmed_at = T+1 自然日(近似,注释标明真实为工作日);持有期自确认日起算
最低持有余额 两种都做:min_hold_action ∈ {force_transfer, force_redeem},默认 force_transfer
普通申赎无批次 不跳过 → 按 core_holding.as_of 兜底补建初始批次(与 rebuild_lots.py 同调 lot_bootstrap.bootstrap_lots,D18)
转出归零 保留 qty=0 行,不删;持仓查询过滤 qty <= 0
Core 侧明细 仅补偿,不对外查询(core_convert_lot_detail)
引擎时序 阶段 1.5:阶段一提交后、阶段二之前同步跑 process_convert_event;失败 不阻断交易(D17)
引擎异常钩子 on_error_hook=None 预留(D19),二期注入 Redis 事件/补偿队列;hook 异常不得反噬主流程
DB 账号分离 D20:xh_core_ro(SELECT)/ xh_core_rw(4 表写,无 DELETE/DDL)/ xh_agent_rw(audit_log 只授 INSERT);见 §11.1
批次上限 convert_batch_max_lots=200,超限 400 TOO_MANY_LOTS,不自动分拆;单笔 = 单事务 = 单 convert_group_id
交易发起主体 仅客户本人或 risk_demo;代理人只能查、不能交易(simulate.py:42 现状即如此,convert 沿用)

4. 开工前置(⛔ 阻断,两个都要绿)

任务 内容 门禁
T-0 以 MySQL 为准统一 sqlite DDL:tests/_ddl.py 的 core_holding 改成 qty/cost_amount/as_of/pnl_pct + 主键(现状 tests/_ddl.py:50-54 只有 market_value/quantity 且无 PK);conftest.py 加启动期列名断言 pytest tests/test_db.py::test_core_holding_columns 必绿才准跑 T-1 之后
T-0b DB 账号分离(D20):scripts/core/00-grant.sql 建 3 账号授权;settings.py 加 3 组账号;db.py 改 get_engine(database, role="rw")(缓存键改 (库名, 角色));core_ro 走 ro、gateway 写路径走 rw(读仍 ro) 3 个真 MySQL 权限断言:ro 账号 INSERT 被拒 · audit_log 改/删被拒 · rw 账号改第 5 张表被拒

账号未配置时回退单账号(settings.mysql_user)——本地开发不被阻塞,但真 MySQL 集成测试必须跑在拆分账号下。 sqlite 测试路径零影响(无账号概念),现有 510 用例一行都不用改。


5. 任务清单(架构 §15 已定稿,可直接作为开发计划输入)

T-0 (⛔ 列名统一 + 门禁)─┐
T-0b(⛔ DB 账号分离 D20)─┴─► T-1(DDL/种子 00-grant·01-ddl·07·08·09·02-agent + reset.ps1)
      └─► T-2(纯函数 types/calc/fee/nav/lot_bootstrap)─► T-2b(calc_convert_demo 实算回填)
           ├─► T-3 ─┐
           ├─► T-4 ─┼─► T-6(阶段一事务)─► T-7(编排 · 关键路径)
           └─► T-5 ─┘                        ├─► T-9(API + gateway 分派)
                                             └─► T-12(补偿脚本)
T-8(引擎改造 · 可全程并行)─► T-11(工具汇总去重)
T-10(批次维护 · 回归风险最大:先跑 510 基线再动)
 └─► T-13(全量回归 + 50 并发压测 + 性能补录)
  • 并行组 A:T-3 / T-4 / T-5(只依赖 T-1,可与 T-2 同时开工)
  • 并行组 B:T-8 与 T-2 之后任意阶段并行
  • 关键路径:T-0 → T-1 → T-2 → T-6 → T-7 → T-13
  • 高风险任务:T-10(改 trade_gateway 主流程,直接影响现有 510 用例)
  • 基线:510 → 预计 580~600 用例

6. 执行期风险(用户 2026-09-10 提供,架构 §12 已并入三列表)

# 风险 必须守住的验收点
1 T-0 未完成就跑 T-1~ CI 门禁 test_core_holding_columns 必绿
2 批次补建规则写两处 → 漂移 D18 抽 lot_bootstrap 纯函数,两侧同调;单测断言同源
3 Decimal.quantize 默认 ROUND_HALF_EVEN calc/fee/nav 所有量化处显式传 ROUND_HALF_UP;.5 边界断言
4 阶段 1.5 引擎异常丢预警 D19 预留 on_error_hook;hook 抛异常不得反噬主流程
5 convert_batch_max_lots=200 超限 400 错误体带 batch_count/max_lots;前端提示「请拆分多笔申请」

7. 本线特有的坑

  1. sqlite 与 MySQL 列名不同名(T-0 根因):tests/_ddl.py:50-54 的 core_holding 用 market_value/quantity,MySQL 用 qty/cost_amount/as_of/pnl_pct,且无 PK。手工同步无迁移工具,不先统一一定在集成测试期才炸。
  2. Decimal.quantize() 默认是银行家舍入(ROUND_HALF_EVEN),必须显式传 ROUND_HALF_UP,否则 .5 边界错。
  3. core_ro.py:390 / :432 写死了 AND trade_type IN ('subscribe','redeem') —— 不改的话 convert 在 RISK-001/002/003 聚合里完全隐形且不报错(架构 D7 _amount_view 就是为此)。
  4. 改路由必须同步 tests/test_main.py::test_all_routers_mounted 的路径清单,否则必红。
  5. 测试/演示库重灌两步缺一不可:scripts/core/reset.ps1 + scripts/demo/prepare_risk_demo.sql(只跑第一步 → 27/33 客户风评过期,是种子设计不是 bug)。
  6. 用系统 Python 3.13.14 跑测试:C:/Users/YUAN/AppData/Local/Programs/Python/Python313/python.exe(managed 3.13.12 未装依赖)。
  7. sqlite 不支持绑定 Decimal,测试插 core_trade.amount 须转 float。
  8. 双库无跨库事务(jinrong_core / jinrong_agent 各自 engine)→ 所以有「三阶段 + 补偿」,不要试图写一个跨库事务。

8. 禁止事项

项目五条红线(碰了就是事故):Core 表只读 · 审计表只 INSERT · 代理人草稿不外发 · 仅 R-02 可阻断交易 · 四 Agent 不互调 LLM。

本线纪律:

  • ❌ 不改表结构 / 接口协议而不经用户确认(改表需确认是项目硬规则)
  • ❌ 不做清单外的改动(看到别的问题记下来,别顺手改)
  • ❌ 不动 tests/_ddl.py 之外的第二份 sqlite DDL(单一事实源)
  • ❌ 不新增第三方依赖
  • ❌ 所有阈值不得硬编码(走 settings.py,架构 §11)
  • ❌ 提交前必须 pytest 全绿;推送前须用户目视验收

9. 另一条线的遗留(别混淆,不在本线范围)

架构改进与稳定性加固线(T-101~T-202)2026-09-10 已全部收尾闭环:

  1. §7.2 七项手工冒烟已补跑完毕,7/7 PASS(含「停 Redis → 退回进程内锁」「调换装饰器 → T-202 守卫变红」两项)
  2. 037ce7e 已 commit 未 push → 本文原表述已过期:实测 git ls-remote,远程 risk-control-agent = fffb78a = 本地 HEAD,037ce7e 早在祖先链上,该线全部提交已推送,无待推内容
  3. 冒烟产生的演示库残留已按 SOP §2 重灌清除,全量 510 passed 复绿

明细与入口见 交接文档.md §C(原 docs/交接文档-架构改进.md 已废弃为历史留档)。本线 T-0b 与其无关(那是基金转换自己的前置)。


10. 完成后要回写的文档

  1. docs/memory/TODO.md —— 勾选进度
  2. docs/memory/MEMORY.md §0 —— 进度与仓库地图
  3. docs/项目框架设计/开发计划-基金转换交易.md —— 第 4 步产出(待创建)
  4. docs/memory/YYYY-MM-DD.md —— 当日工作日志
  5. 本文件 —— 状态块与任务清单勾选

11. 有问题怎么办

  • 发现文档与代码不符 → 以代码为准,并在 TODO 备注记下差异(先核实,别急着改文档)
  • 对设计有疑问 → 查架构对应章节;仍不明确则停下问用户,不要猜
  • 评审意见与自己的判断冲突 → 先核实证据(文件行号),不盲从也不护短;驳回要给出核实过的证据