Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ data → universe → factors(特征) → alpha(合成/预测) → portfolio(+ri
- **CSI500 verdict:SUPPORTED**(21 settled vs min 8):value_ep **+0.0083/+0.0145**(train/test)、value_bp **+0.0230/+0.0127**、volatility_20 **−0.0350/−0.0272**——三假设双子期全保持。**且衰减更小**:CSI500 test 子期量级(0.0127~0.0272)明显高于 SSE50/CSI300 holdout 的后段(0.003~0.016)——value/低波信号在中盘股上更强,**P3-7 结论泛化成立(GENERALIZES,未减弱)**。
- combo_ic_weighted CSI500 test IC 全 4 组正(0.0243/0.0294/0.0286/0.0285,为三个 holdout cells 最高);**组合净值 CSI500 base 全组正且 4× 高成本仍全正**(trio +17.80%→+10.57% / full_pack +7.83%→+2.95% / value_lowvol +4.19%→+1.14% / liq +4.02%→+0.84%)——首个全成本阶梯为正的 cell。⚠️ 仍然诚实:trio +17.80% 又是组间排名跨 cell 翻转的例证(中盘动量 regime);单窗口 ~21 调仓小样本;**非收益声明**。
- **报告标题配置化**(`output.subset_report_title`,沿 `subset_report_name` 先例):P3-8 报告 H1 自报 study 名「Phase 3-8 — CSI500 Independent Generalization Check」而非机器默认 P3-7 标签(review 同类 stale-wording 问题修复,测试断言首行非 P3-7;P3-6/P3-7 配置不设此项、保持 sample-aware 默认,回归锁定);文案/分区检查全过(无 stale P3-6 措辞、verdict 节只含独立 cells);secret scan 报告+日志 0 处;demo 0.96/0.84 不变。
- ✅ 质量门:`pytest` **383 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);`ruff` clean;`validate-config`(全部 11 配置)+ `run-phase0`(demo)均 OK。
- ✅ **Phase 4-1 持久化 Tushare 行情缓存**(分支 `data-persistent-market-cache`,daily + adj_factor):feed 之下的 **endpoint 级 raw 缓存**——真实 run 不再每次重抓全量日线+复权因子。**默认 disabled(向后兼容,旧配置行为一字不变)**;opt-in 后行情走 read-through,只有未覆盖的日期区间打 API。
- **缓存只存 raw**(未复权 OHLCV/amount + 原始 adj_factor),绝不存 qfq、绝不存任何 secret;`front_adjust` 仍在内存跑,公式/时机零改动;`PanelStore` 仍是 per-run artifact,**不是**缓存 SoT。
- `data/cache/`:`intervals`(闭区间日历算法做 gap 规划——按"日历区间是否抓过"而非"行是否存在"判定,正确区分"未抓"vs"源本无行")/ `parquet_store`(按 `(endpoint,symbol)` 分文件存 `symbol_prefix` 分片,原子 upsert 按 `(date,symbol)` 去重,latest 胜)/ `coverage`(append-only ledger,11 列含 status ok/empty/failed,**只 ok/empty 算覆盖**,failed 留待重试)/ `tushare_cache`(read-through:只抓 gap、upsert、记 coverage 含空返回、再从缓存读全区间;`refresh_recent_days` 重抓近端 tail;`force_refresh` 整端点重拉;per-run 抓取计数 stats 日志)。
- `TushareFeed.get_bars`:`cache=None` → 旧直抓路径**逐字不变**;`cache` 注入 → read-through 后用**与直抓完全相同的 join+select**,故 `front_adjust` 前面板逐字节一致(qfq 等价单测锁定);per-symbol 限流/重试仍留在 feed 的 `_call` 闭包里。`_build_market_cache` 仅在 `data.cache.enabled` 时接线。
- **配置**:`data.cache`(enabled=False / root_dir=`artifacts/cache/tushare/v1`(gitignored)/ refresh_recent_days=14 / force_refresh=[]);校验拒绝负刷新窗/空 root/未知键;全部旧配置仍 validate。
- **真实 smoke(phase2 baseline;非缓存参照 ref / 缓存冷 cold / 缓存暖 warm 三轮)**:
- **三轮 report 指标完全一致** ✓(IC 0.0083 / annual −10.19% / maxDD −16.52% / vol 16.59% / sharpe −0.5703 / turnover 1.0818 / cost 1.19%,REF==COLD==WARM)——qfq 等价单测已证 cached==direct 数据层逐字节一致,真实 run 三轮一致是端到端实证。
- **warm 轮零市场端点调用**:cold 后 coverage ledger = 68 market_daily + 68 adj_factor 行(68 成分全 ok);warm 后**仍 68+68 不变**(零新 coverage 行 = 零 gap-fetch = 零 daily/adj 调用)。
- wall:ref 998s / cold 1025s / warm **734s**(暖跑省 ~290s = 行情抓取部分;index_weight/财务/covariates 仍 live——P4-1 只缓存行情,诚实标注)。
- secret scan:缓存 parquet + ledger 0 处 token / `.config.json`;ledger 列只有端点元数据。
- **缓存命中直接可见(review follow-up)**:`TushareFeed.cache_stats()` 暴露各端点 gap-fetch 计数,`_load_panel` 经 run-scoped logger 打 `data cache: market_daily_gap_fetches=N adj_factor_gap_fetches=M`——冷跑非零、暖跑 0/0(warm rerun 实证 run_phase2_baseline.log 含 `0/0`,secret scan 0)。
- **不变量守住**:factor/alpha/portfolio/execution/OOS 切片/report 全不动;`artifacts/data/{output_name}.parquet` 不当 SoT。范围克制:P4-1 只 market_daily+adj_factor,其余端点(index_weight/daily_basic/fina_indicator/...)P4-2/P4-3 再缓存。
- ✅ 质量门:`pytest` **411 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);`ruff` clean;`validate-config`(全部 12 配置)+ `run-phase0`(demo)均 OK。
- ⚠️ 剩余(已显式披露):日线 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 起不再互覆盖)。
- 路线图下一步:更长 holdout 积累(2026-06 之后滚动复检)/ 中证1000 或全市场扩展 / 成本模型细化(印花税卖侧不对称、冲击成本),或分钟级(architecture.html §11)。
- 路线图下一步:**P4-2 universe/tradability 缓存**(index_weight/suspend_d/namechange/stk_limit/stock_basic)→ **P4-3 因子支撑端点缓存**(fina_indicator/daily_basic/index_member_all);研究侧:更长 holdout 滚动复检 / 中证1000 / 成本模型细化,或分钟级(architecture.html §11)。
16 changes: 14 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ data → universe → factors(特征) → alpha(合成/预测) → portfolio(+ri
- **CSI500 verdict:SUPPORTED**(21 settled vs min 8):value_ep **+0.0083/+0.0145**(train/test)、value_bp **+0.0230/+0.0127**、volatility_20 **−0.0350/−0.0272**——三假设双子期全保持。**且衰减更小**:CSI500 test 子期量级(0.0127~0.0272)明显高于 SSE50/CSI300 holdout 的后段(0.003~0.016)——value/低波信号在中盘股上更强,**P3-7 结论泛化成立(GENERALIZES,未减弱)**。
- combo_ic_weighted CSI500 test IC 全 4 组正(0.0243/0.0294/0.0286/0.0285,为三个 holdout cells 最高);**组合净值 CSI500 base 全组正且 4× 高成本仍全正**(trio +17.80%→+10.57% / full_pack +7.83%→+2.95% / value_lowvol +4.19%→+1.14% / liq +4.02%→+0.84%)——首个全成本阶梯为正的 cell。⚠️ 仍然诚实:trio +17.80% 又是组间排名跨 cell 翻转的例证(中盘动量 regime);单窗口 ~21 调仓小样本;**非收益声明**。
- **报告标题配置化**(`output.subset_report_title`,沿 `subset_report_name` 先例):P3-8 报告 H1 自报 study 名「Phase 3-8 — CSI500 Independent Generalization Check」而非机器默认 P3-7 标签(review 同类 stale-wording 问题修复,测试断言首行非 P3-7;P3-6/P3-7 配置不设此项、保持 sample-aware 默认,回归锁定);文案/分区检查全过(无 stale P3-6 措辞、verdict 节只含独立 cells);secret scan 报告+日志 0 处;demo 0.96/0.84 不变。
- ✅ 质量门:`pytest` **383 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);`ruff` clean;`validate-config`(全部 11 配置)+ `run-phase0`(demo)均 OK。
- ✅ **Phase 4-1 持久化 Tushare 行情缓存**(分支 `data-persistent-market-cache`,daily + adj_factor):feed 之下的 **endpoint 级 raw 缓存**——真实 run 不再每次重抓全量日线+复权因子。**默认 disabled(向后兼容,旧配置行为一字不变)**;opt-in 后行情走 read-through,只有未覆盖的日期区间打 API。
- **缓存只存 raw**(未复权 OHLCV/amount + 原始 adj_factor),绝不存 qfq、绝不存任何 secret;`front_adjust` 仍在内存跑,公式/时机零改动;`PanelStore` 仍是 per-run artifact,**不是**缓存 SoT。
- `data/cache/`:`intervals`(闭区间日历算法做 gap 规划——按"日历区间是否抓过"而非"行是否存在"判定,正确区分"未抓"vs"源本无行")/ `parquet_store`(按 `(endpoint,symbol)` 分文件存 `symbol_prefix` 分片,原子 upsert 按 `(date,symbol)` 去重,latest 胜)/ `coverage`(append-only ledger,11 列含 status ok/empty/failed,**只 ok/empty 算覆盖**,failed 留待重试)/ `tushare_cache`(read-through:只抓 gap、upsert、记 coverage 含空返回、再从缓存读全区间;`refresh_recent_days` 重抓近端 tail;`force_refresh` 整端点重拉;per-run 抓取计数 stats 日志)。
- `TushareFeed.get_bars`:`cache=None` → 旧直抓路径**逐字不变**;`cache` 注入 → read-through 后用**与直抓完全相同的 join+select**,故 `front_adjust` 前面板逐字节一致(qfq 等价单测锁定);per-symbol 限流/重试仍留在 feed 的 `_call` 闭包里。`_build_market_cache` 仅在 `data.cache.enabled` 时接线。
- **配置**:`data.cache`(enabled=False / root_dir=`artifacts/cache/tushare/v1`(gitignored)/ refresh_recent_days=14 / force_refresh=[]);校验拒绝负刷新窗/空 root/未知键;全部旧配置仍 validate。
- **真实 smoke(phase2 baseline;非缓存参照 ref / 缓存冷 cold / 缓存暖 warm 三轮)**:
- **三轮 report 指标完全一致** ✓(IC 0.0083 / annual −10.19% / maxDD −16.52% / vol 16.59% / sharpe −0.5703 / turnover 1.0818 / cost 1.19%,REF==COLD==WARM)——qfq 等价单测已证 cached==direct 数据层逐字节一致,真实 run 三轮一致是端到端实证。
- **warm 轮零市场端点调用**:cold 后 coverage ledger = 68 market_daily + 68 adj_factor 行(68 成分全 ok);warm 后**仍 68+68 不变**(零新 coverage 行 = 零 gap-fetch = 零 daily/adj 调用)。
- wall:ref 998s / cold 1025s / warm **734s**(暖跑省 ~290s = 行情抓取部分;index_weight/财务/covariates 仍 live——P4-1 只缓存行情,诚实标注)。
- secret scan:缓存 parquet + ledger 0 处 token / `.config.json`;ledger 列只有端点元数据。
- **缓存命中直接可见(review follow-up)**:`TushareFeed.cache_stats()` 暴露各端点 gap-fetch 计数,`_load_panel` 经 run-scoped logger 打 `data cache: market_daily_gap_fetches=N adj_factor_gap_fetches=M`——冷跑非零、暖跑 0/0(warm rerun 实证 run_phase2_baseline.log 含 `0/0`,secret scan 0)。
- **不变量守住**:factor/alpha/portfolio/execution/OOS 切片/report 全不动;`artifacts/data/{output_name}.parquet` 不当 SoT。范围克制:P4-1 只 market_daily+adj_factor,其余端点(index_weight/daily_basic/fina_indicator/...)P4-2/P4-3 再缓存。
- ✅ 质量门:`pytest` **411 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);`ruff` clean;`validate-config`(全部 12 配置)+ `run-phase0`(demo)均 OK。
- ⚠️ 剩余(已显式披露):日线 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 起不再互覆盖)。
- 路线图下一步:更长 holdout 积累(2026-06 之后滚动复检)/ 中证1000 或全市场扩展 / 成本模型细化(印花税卖侧不对称、冲击成本),或分钟级(architecture.html §11)。
- 路线图下一步:**P4-2 universe/tradability 缓存**(index_weight/suspend_d/namechange/stk_limit/stock_basic)→ **P4-3 因子支撑端点缓存**(fina_indicator/daily_basic/index_member_all);研究侧:更长 holdout 滚动复检 / 中证1000 / 成本模型细化,或分钟级(architecture.html §11)。
56 changes: 56 additions & 0 deletions RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,62 @@ Documented by `config/phase3_real_csi500_generalization.yaml`.
sample-aware default (P3-7 independent / P3-6 post-hoc), locked by tests.
CSI500|2022-2024 is skip_cells-listed (runtime budget) and disclosed.

## Phase 4-1 — persistent Tushare market-data cache (daily + adj_factor)

A persistent endpoint-level RAW cache below the feeds so real runs stop
refetching full daily bars + adj_factor every time. **Disabled by default**
(backward compatible — an existing config runs exactly as before). The cache
stores RAW rows only (unadjusted OHLCV/amount, raw adj_factor); `front_adjust`
still runs in memory downstream, unchanged. `PanelStore` stays a per-run
artifact, NOT the cache source of truth.

Opt in via `data.cache`:

```yaml
data:
cache:
enabled: true
root_dir: artifacts/cache/tushare/v1 # gitignored
refresh_recent_days: 14 # refetch a recent tail near today
force_refresh: [] # e.g. ["market_daily","adj_factor"]
```

Read-through behaviour (`TushareFeed.get_bars`, cache enabled):

- the coverage ledger (`<root>/manifest/coverage.parquet`) records which
`(endpoint, symbol, [start,end])` ranges were fetched (status ok/empty/failed)
— so the planner fetches ONLY uncovered date gaps;
- a second identical run over a historical window makes **zero** `daily` /
`adj_factor` API calls;
- a partial window extension fetches only the new tail;
- raw rows are upserted per `(symbol, date)` (latest wins; no duplicates);
- the cache + ledger never store a token or secret-file content;
- the run log shows the hit rate directly — `_load_panel` emits
`data cache: market_daily_gap_fetches=<N> adj_factor_gap_fetches=<M>`
(cold run nonzero; a warm historical rerun shows 0/0).

Cache layout (per-symbol parquet under a symbol_prefix shard):

```text
artifacts/cache/tushare/v1/
manifest/coverage.parquet
market_daily/symbol_prefix=600/600519.SH.parquet
adj_factor/symbol_prefix=600/600519.SH.parquet
```

Real smoke (small real config; metrics must equal the non-cached run):

```bash
# populate, then re-run: the second run hits cache for market bars
... -m qt.cli run-phase2-baseline --config config/phase2_real_baseline_cached.yaml
... -m qt.cli run-phase2-baseline --config config/phase2_real_baseline_cached.yaml
```

Scope note: P4-1 caches market bars ONLY. `index_weight`, `daily_basic`,
`fina_indicator`, `stk_limit`, `suspend_d`, `namechange`, `stock_basic`,
`index_member_all` are still fetched live (cached in P4-2/P4-3); the warm run
therefore saves the market-bar calls, not those.

## Quality gate

```bash
Expand Down
Loading