diff --git a/CLAUDE.md b/CLAUDE.md index 7f823a3..00bc155 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ data → universe → factors(特征) → alpha(合成/预测) → portfolio(+ri ## 开发约定 - **交流中文**;代码/注释/commit message 用**英文**。 -- **Git**:feature 分支 + PR。**PR #1(P0+P1)、#2(P2-1)、#3(P2-2)、#4(进度文档)、#5(P2-3)、#6(进度文档)、#7(P2-4)、#8(进度文档)、#9(P3-1)、#10(进度文档)、#11(P3-2)、#12(P3-3)、#13(进度文档)、#14(P3-4)、#15(进度文档)、#16(P3-5)、#17(进度文档)、#18(P3-6)、#19(P3-7)、#20(进度文档)、#21(P3-8)、#22(进度文档)、#23(P4-1)、#24(进度文档)、#25(P4-2)、#28(tushare 权限/限频探测 + capability registry)、#29(I1–I4 分钟级 intraday pipeline,4 commit 一 PR)、#30(进度文档)、#31(P4-3 因子支撑端点缓存 + 21:00 data updater,2 commit 一 PR)、#33(P-I5a 事件驱动回测架构重构 + opt-in 分钟尾盘 event model,4 commit 一 PR)、#35(P-I5b 分钟尾盘执行期 raw stk_limit 涨跌停可行性,4 commit 一 PR)、#37(P-I5c MMP 分钟因子端到端 opt-in alpha)、#39(P-I5d MMP 五分位分组回测 standalone,含 I5c plumbing)、#41(数据层 D1 契约文档 + token 解析去重)、#43(数据层 D2 TushareCache specs/parsers 拆分)、#45(数据层 D3 report-only 数据质量层)、#47(数据层 D3b default-off `data-update` 质量报告钩子)、#49(数据层 D4 coverage ledger 批量写 + 进程内查找缓存)、#51(数据层 D5 opt-in 有界并发 + 全局限频器)、#53(P-I5e MMP 五分位 CSI300 独立泛化检验,负结果)、#55(P-I5f 分钟尾盘执行流动性/容量诊断,report-only)、#57(数据层 schema 注册表 + default-off drift 守卫)、#59(数据层 全A 增量 auto-warm + systemd 定时,default-off)、#61(数据层 全A 历史回填 `data-backfill`,分块/续跑/失败隔离)、#62(进度文档)、#63(因子评估契约层 `analytics/eval/` + StandardFactorEvaluator + dashboard + PR-C 因子,4 commit 一 PR)、#64–#73(PR-D…PR-M 十个分钟因子复现,**一因子一分支一 PR**)、#74(因子评估契约 v0.8 + v0.9 判定门修复)、#75(尾盘执行价 VWAP + 分钟持有期收益复权)、#76(报告披露修正 + I5f 名义按真实规模重测)、#77(`adj_factor` 下降质量检查)、#78(#76 的 follow-up:扫描式正则守卫被七种改写实测证否 → 闸门描述改单一来源)、#79(十一因子改 14:51 VWAP exec-to-exec 基准,**评审未返回、靠自查合并**)、#80(进度文档)均已 merge 到 `main`**;**OPEN 未合并:#38**(见 P-I5d 处置,未获授权关闭)。commit 用 conventional 格式,**无 attribution**(不加 Co-Authored-By)。 +- **Git**:feature 分支 + PR。**PR #1(P0+P1)、#2(P2-1)、#3(P2-2)、#4(进度文档)、#5(P2-3)、#6(进度文档)、#7(P2-4)、#8(进度文档)、#9(P3-1)、#10(进度文档)、#11(P3-2)、#12(P3-3)、#13(进度文档)、#14(P3-4)、#15(进度文档)、#16(P3-5)、#17(进度文档)、#18(P3-6)、#19(P3-7)、#20(进度文档)、#21(P3-8)、#22(进度文档)、#23(P4-1)、#24(进度文档)、#25(P4-2)、#28(tushare 权限/限频探测 + capability registry)、#29(I1–I4 分钟级 intraday pipeline,4 commit 一 PR)、#30(进度文档)、#31(P4-3 因子支撑端点缓存 + 21:00 data updater,2 commit 一 PR)、#33(P-I5a 事件驱动回测架构重构 + opt-in 分钟尾盘 event model,4 commit 一 PR)、#35(P-I5b 分钟尾盘执行期 raw stk_limit 涨跌停可行性,4 commit 一 PR)、#37(P-I5c MMP 分钟因子端到端 opt-in alpha)、#39(P-I5d MMP 五分位分组回测 standalone,含 I5c plumbing)、#41(数据层 D1 契约文档 + token 解析去重)、#43(数据层 D2 TushareCache specs/parsers 拆分)、#45(数据层 D3 report-only 数据质量层)、#47(数据层 D3b default-off `data-update` 质量报告钩子)、#49(数据层 D4 coverage ledger 批量写 + 进程内查找缓存)、#51(数据层 D5 opt-in 有界并发 + 全局限频器)、#53(P-I5e MMP 五分位 CSI300 独立泛化检验,负结果)、#55(P-I5f 分钟尾盘执行流动性/容量诊断,report-only)、#57(数据层 schema 注册表 + default-off drift 守卫)、#59(数据层 全A 增量 auto-warm + systemd 定时,default-off)、#61(数据层 全A 历史回填 `data-backfill`,分块/续跑/失败隔离)、#62(进度文档)、#63(因子评估契约层 `analytics/eval/` + StandardFactorEvaluator + dashboard + PR-C 因子,4 commit 一 PR)、#64–#73(PR-D…PR-M 十个分钟因子复现,**一因子一分支一 PR**)、#74(因子评估契约 v0.8 + v0.9 判定门修复)、#75(尾盘执行价 VWAP + 分钟持有期收益复权)、#76(报告披露修正 + I5f 名义按真实规模重测)、#77(`adj_factor` 下降质量检查)、#78(#76 的 follow-up:扫描式正则守卫被七种改写实测证否 → 闸门描述改单一来源)、#79(十一因子改 14:51 VWAP exec-to-exec 基准,**评审未返回、靠自查合并**)、#82(闸门/成交价描述全仓清扫,12 处;#78 守卫射程不足的修正)、#80(进度文档)均已 merge 到 `main`**;**OPEN 未合并:#38**(见 P-I5d 处置,未获授权关闭)。commit 用 conventional 格式,**无 attribution**(不加 Co-Authored-By)。 - **不过度设计**:按路线图 MVP 先打通一条端到端链路,再加层(architecture.html §11,Phase 0→3)。 - **secrets** 一律走外部 `.config.json`;repo `.gitignore` 已排除数据产物(`*.parquet`等)、缓存、`tmp/`(仅留架构文档)。 - 文件小而专(<800 行),immutable 优先。 @@ -356,6 +356,12 @@ data → universe → factors(特征) → alpha(合成/预测) → portfolio(+ri - **VWAP 与 bar close 在无条件分布层面几乎同一序列**:均值差 **+1.0e-07**(SE 1.3e-06,t = 0.074)、中位差 0、逐日相关中位 **0.998** → 改基准买到的是**执行锚点的真实性,不是另一个信号**,与 I5d 归因(VWAP 什么都没动、复权动了全部)一致。**close-to-close 结果保留作并排对照**,exec_to_exec 为操作基准。 - **正确性验收(CSI500 全规模,996 票 / 1,210 日,cache-only,十个日频/universe gap-fetch 计数器全 0)**:① 与 close_to_close 的逐日截面相关中位 **0.9890**(p10 0.9821 / p90 0.9933,Spearman 中位 0.9814),下限 0.90 → PASS(**低于下限时 builder 直接 raise 而不是继续跑**);② 量级同阶(exec mean 0.000429 / std 0.027718 vs c2c 0.000451 / 0.028050);③ **5 行从 raw 1min parquet 用普通算术手算复核**(不 import 任何执行层 helper,故不会继承其 bug),最坏绝对差 **0.0 精确**;④ 除权持有期两种口径并列(如 `000564.SZ` 2021-12-29→12-31:未复权 −0.598581 → 复权 **−0.045367**)。gates(PR 正文):`pytest` **1774 passed**(= 当时 `main` 的 1740 + 34,恰为两个新测试文件、0 个既有测试被挤掉)、`ruff` clean、phase0 `0.9600/0.8408`、31 配置、secret scan 0。 - ⚠️ **它是怎么合并的:派出的 code review 约 1.7 小时未返回,本 PR 靠自查而非评审通过而合并**(PR 正文下有一条 comment 如实说明,以免日后有人以为它被评审签过字)。**合并前独立核过的**:① 既有路径没动——分支的 `close_to_close` artifact vs 已验证的主树版本 **5,756 叶、0 处不同**(两个目录分别读取、无共享中间产物),与 diff **+2595/−0 纯新增**一致;② **`h` 走的是因子自己的评估网格而非价格面板的行**(`exec_forward_returns` 先 `isin(dates)` 再 `groupby(symbol).shift(-h)`,正是 `analytics/eval/ir.py::_resolve_forward_returns` 标注为 close 路径隐患的那条性质;非正分母掩成 NaN);③ 因子信息集**无前视**(日 d 的入场锚是 d 当天 14:51 的执行 bar,因子 14:50 已知,因果成立);④ 共享 artifact 的 **cache key 完整**(含 schema 版本 / universe 类型 + 指数代码 / 起止 / 缓存根 / `price_adjust` / 全部执行参数 / **精确 symbol 集合与精确日期网格各自的 sha1**)——陈旧 artifact 无法被喂给另一份配置,那正是"悄悄拿错收益给一族因子打分"的失效模式;⑤ 覆盖率成因**按构造互斥划分**(先归入场锚、否则归出场锚)。**未独立核、因此仍对后续检查开放的**:五条 mutation 声称(只读了、未重跑)、十一个 runner 改动是否机械等同、`EvalContext.execution_capacity` 透传是否在作者自带测试之外仍保持 Tradable 轴 `NOT_ASSESSED`。**若日后评审在这个未核集合里查出问题,要重述的是 exec 基准的数字——不是 close-to-close,后者未被触碰且仍是已验证的参照。** -- ✅ 质量门(**当前 `main` @ `b04c966` 实测**:`python -m pytest -p no:warnings` **1776 passed**、`ruff` clean、`validate-config` **31/31 配置逐个 OK**、`run-phase0`(demo)**ic 0.9600 / annual 0.8408 不变**)。构成对得上:`aa19968` 实测 **1740** → #78 加 2(含钉住破折号闭合的那条)= **1742** → #79 加 34(两个新测试文件,0 个既有测试被挤掉)= **1776**。新增测试分组(`--collect-only` 实数,`aa19968` 口径):**因子评估契约层 contract=337 + standard=113 + figures=7 / 十一因子(factor + eval runner,21 文件)=421 / PR#75 exec-vwap-basis=46 / D3 market quality 15→21(含 #77 `decreasing_adj_factor` 及其相对-vs-绝对尺度 mutation 测试)**。历史分组(旧 793 基线):`pytest` **793 passed**(P0=97 / P1=78 / P2-1=22 / P2-2=22 / P2-3=14 / P2-4=8 / P3-1=10 / P3-2=18 / P3-3=16 / P3-4=15 / P3-5=22 / P3-6=27 / P3-7=25+1 throttle / P3-8=8 / P4-1=28 / P4-2=17 / **intraday I1 schema=14 + feed=9 / I2 cache=10 / I3 aggregate=13 / I4 execution=10 / P4-3 cache=10 + updater=7 / P-I5a event-backtest=19 / P-I5b exec-feasibility=16 / P-I5c mmp-minute=18 / P-I5d mmp-quintile=19 / P-I5e 仅 config(无新测试,`test_cache_config` 的 config glob 自动 +1)/ P-I5f intraday-liquidity=17(+ `test_cache_config` config glob 自动 +1)/ D1 token-dedup index-feed +2 / D2 cache-modularization +5 / D3 data-quality market=15 + intraday=12 + report=14 / D3b data-update-quality=18 / D4 coverage-ledger-scaling=13 / D5 scheduler=9 + concurrency=12 / schema-registry-guard=62 / all-A-incremental=12 / all-A-backfill=22**);当时 `ruff` clean、`validate-config` 20 配置(data_update + phase_i5a…phase_i5f)+ `run-phase0`(demo)均 OK(**ic 0.9600 / annual 0.8408 自 P0 起从未变过**)。 +- ✅ **闸门/成交价描述全仓清扫**(**PR #82 已 merge 到 `main`**,`f432dc5`;#78 的 review follow-up):#78 的守卫**只扫它刚修过的那两个 report 模块**,于是同一缺陷的**六处存活实例原则上就够不到**——`qt/config.py:343` 加 **五个 I5b/c/d/e/f config YAML**。后者是操作者启用 `price_limit_check` 前会读的文件,**读者层级高于 docstring**。 + - **教训(本条是全轮最值得留下的一条)**:**一个只扫你已经修过的文件的守卫,永远只能确认你已经知道的事。** 把扫描扩到整个特性面后**立刻又抓出六处**——不是同一个声明,是同一次 PR #75 行为变更留下的另一批:`执行价 = 窗口内第一根有效 1min **收盘价**`(`qt/intraday_tail_framework.py` module docstring + I5a/I5b/I5f config),而 #75 之后成交价是 bar VWAP。**共 12 处:7 处描述闸门,5 处描述成交价。** + - **两处因导入环无法组合短语,改为指针并写明原因**:`runtime/backtest/event_models.py` 与 `qt/config.py` 都在 `qt.intraday_tail_framework` 的**上游**(是它 import 这两个),反向 import 必成环 → 不再复述,改指向 `limit_basis_phrase`,扫描作为这两处的兜底。`event_models.py` 的 docstring 明写这一点,免得下一个人再去试那个 import。 + - **刻意不扫、逐条核过的**:`RUNBOOK.md` / `CLAUDE.md` 的「涨跌停用未复权 raw close 比 `stk_limit`」是**日频** universe 可交易性过滤(`universe/filters.py` + `data/clean/tradability.py`),另一套更老的机制,本来就该比日收盘——**粗心的 sed 会把真话改成假话**。 + - **守卫的射程如实写进它自己的 docstring**:它抓的是"close"写在"execution minute"附近,**抓不住换词改写**(review 实测七种逃逸)。**组合(author once)才是真答案,扫描只是导入环挡住组合的那两处下面的网。** review 独立复核:对 merge-base 与本 PR tip 各跑一遍 → **修前 12 命中 / 修后 0 命中,行号逐一对上**;另用更宽松模式扫全仓 `.py/.yaml/.md` 共 54 处命中**逐条读过**,无新增真缺陷。 + - **顺手把 #79 的 `qt/exec_basis_*.py` / `qt/exec_forward_returns.py` 提前纳入扫描**(今天是对的,但同属"执行基准"领地)——等它们变陈旧再加,就是把刚吃过的亏再吃一遍。 +- ✅ 质量门(**当前 `main` @ `f432dc5` 实测**:`python -m pytest -p no:warnings` **1776 passed**、`ruff` clean、`validate-config` **31/31 配置逐个 OK**、`run-phase0`(demo)**ic 0.9600 / annual 0.8408 不变**)。构成对得上:`aa19968` 实测 **1740** → #78 加 2(含钉住破折号闭合的那条)= **1742** → #79 加 34(两个新测试文件,0 个既有测试被挤掉)= **1776**。新增测试分组(`--collect-only` 实数,`aa19968` 口径):**因子评估契约层 contract=337 + standard=113 + figures=7 / 十一因子(factor + eval runner,21 文件)=421 / PR#75 exec-vwap-basis=46 / D3 market quality 15→21(含 #77 `decreasing_adj_factor` 及其相对-vs-绝对尺度 mutation 测试)**。历史分组(旧 793 基线):`pytest` **793 passed**(P0=97 / P1=78 / P2-1=22 / P2-2=22 / P2-3=14 / P2-4=8 / P3-1=10 / P3-2=18 / P3-3=16 / P3-4=15 / P3-5=22 / P3-6=27 / P3-7=25+1 throttle / P3-8=8 / P4-1=28 / P4-2=17 / **intraday I1 schema=14 + feed=9 / I2 cache=10 / I3 aggregate=13 / I4 execution=10 / P4-3 cache=10 + updater=7 / P-I5a event-backtest=19 / P-I5b exec-feasibility=16 / P-I5c mmp-minute=18 / P-I5d mmp-quintile=19 / P-I5e 仅 config(无新测试,`test_cache_config` 的 config glob 自动 +1)/ P-I5f intraday-liquidity=17(+ `test_cache_config` config glob 自动 +1)/ D1 token-dedup index-feed +2 / D2 cache-modularization +5 / D3 data-quality market=15 + intraday=12 + report=14 / D3b data-update-quality=18 / D4 coverage-ledger-scaling=13 / D5 scheduler=9 + concurrency=12 / schema-registry-guard=62 / all-A-incremental=12 / all-A-backfill=22**);当时 `ruff` clean、`validate-config` 20 配置(data_update + phase_i5a…phase_i5f)+ `run-phase0`(demo)均 OK(**ic 0.9600 / annual 0.8408 自 P0 起从未变过**)。 - ⚠️ 剩余(已显式披露):日线 only、demo 路径非真数据、旧三因子无信号(P3-3/P3-4 实证;但其组合在 2024-2026 holdout 上 SSE50/CSI300/CSI500 全正、CSI500 高达 +17.8%——小样本/regime 翻转的持续例证);value/低波信号获得**独立样本符号级确认**(P3-7 SSE50/CSI300 量级衰减;P3-8 CSI500 泛化成立且更强),组合级盈利能力仍未确立(排名跨 cell 翻转);subset 报告文件名已可配置(P3-8 起不再互覆盖)。**分钟因子侧新增披露**:十一个复现因子全部 EXPLORATORY、**封顶 Watch,无一到 Adopt**,1× 费率下净多空为正的只有 2 个(PR-F/PR-L,且 PR-L 扛不住 2×)——**IC 强 ≠ 可交易**;MMP 的唯一正面证据(I5d)在修正引擎上退化、I5e 负结果加强;I5f 的容量结论已按**真实 100 万名义**重写(1000 万口径的"约半数不足"作废);**日频除权审计已完成**(GENUINE、无 vintage 接缝、评估池暴露 1/603,258,不重述任何因子结果),**分钟路径的复权缺陷已在 PR #75 修**;exec-to-exec 改基准已随 **PR #79 合并**,close-to-close 与 exec 两套 artifact 并存;⚠️ **#79 的 code review 未返回、靠自查合并**,其"未独立核"清单(五条 mutation 声称 / 十一个 runner 改动是否机械等同 / `execution_capacity` 透传)仍待后续检查——若查出问题,重述 exec 基准数字而非 close-to-close;schema 守卫**仍未接** live `data-update` 暖跑路径;HTML compendium(`artifacts/reports/factor_compendium.html`)**还读 v0.8 结果,待按最终结果修订**。 - 路线图下一步:**MMP 暂缓推进——理由在修正引擎上比过去更强,不是不变**(I5e 独立 universe CSI300 同窗口机器冻结复跑 → 单调性 **FAILS/反转** 且**在修正引擎上更负**:Spearman **−0.5000 不变**、Q5 高 MMP 仍最差、合成 Q5−Q1 累计 **−26.77% → −31.82%**;而 I5d 的 CSI500 完美单调性在修正引擎上**退化**(Spearman **+1.0000 → +0.9000**、Q5−Q1 **+17.15% → +8.65%**、Q5 让位 Q4),四档归因实测**归到复权修正本身**——原结论实质是"把除权下跌记成亏损"的产物 → **在出现 disjoint 窗口 / 第三 universe 的正向证据前,不把 MMP 当稳健或可交易信号推进**;新 intraday 因子候选只作显式 EXPLORATORY 工作);**I5f 已落地 → I5g(可选,须单独显式 goal)**(I5f 已加 report-only 执行流动性/容量诊断;**按用户真实规模 100 万 RMB / 5% 参与率重测:below-1.0x 由 20 笔变 0 笔、最紧 1.22×——单分钟容量在该 universe/窗口不构成约束**;此前"10M RMB 下约半数不足"的说法**已作废**,它描述的组合比本项目会跑的大一个数量级;**强制 partial-fill / volume-cap(真正约束成交)= 可能的 I5g,仅在单独显式 goal 后启动**,本阶段未做,不声称已实现);**盘后全A auto-fetch**(数据正确性审计 PASS——D3 质量层实测 80GB 真实缓存结构干净、ann_date PIT 轴 100%、跨 shard schema 无漂移;PR #59 = PR-1 全A 增量 auto-warm + systemd @21:00 default-off 已落地;**PR #61 = PR-2 全A 历史回填**——分块/断点续跑/分钟全窗口 + per-symbol 失败隔离 + `start>today` 守卫,已落地。**建设侧完成,剩 live rollout = 用户 Stage-1**:先 `data-backfill` 补全A 历史(可续跑)→ 手动跑增量 warm 观察 → enable systemd timer);**P4-3 follow-up**(fina 按 ann_date 建披露日历 ledger 以去掉启发式 tail / data-update summary 落 cache-stats artifact);**数据层 D6**(D1+D2+D3+D3b+D4+D5 已 merge:契约文档化 + token 解析收敛 + `TushareCache` specs/parsers 拆分 + report-only `data/quality/` 质量层 + default-off `data-update` 质量报告钩子 + coverage ledger `record_many` 批量写 + 进程内查找缓存 + opt-in 有界并发 + 全局限频器 + **schema 注册表/default-off drift 守卫(PR #57)**;剩余数据层 follow-up:schema 守卫接 live `data-update` 暖跑路径(当前仅接 pipeline/回测) + 分钟 schema 守卫;**D6**=`PanelStore` append/partition / 可选物化派生面板存储,**仅当因子研究需要可复用派生面板时才启动**——均尚未实现);**因子层收尾**(① `feat/exec-to-exec-rebasis` 两基准对照已跑完并随 **PR #79 合并**——22 格中 20 格不变、2 格朝更保守走;**补一次 #79 的 code review**(原评审未返回,未独立核的三项见上);② HTML compendium 按最终结果修订;③ 十一因子**无一到 Adopt、净多空几乎全负** → 下一步优先做**换手/成本敏感**的组合层验证与更长 holdout,而不是继续堆新因子);研究侧:更长 holdout 滚动复检 / 成本模型细化。