feat(alpha): P3-2 walk-forward IC-weighted alpha (RollingICWeightAlpha) - #11
Merged
Conversation
Adds the first LEARNED factor combination while keeping the lookahead boundary auditable. EqualWeightAlpha stays the default and the regression baseline; alpha.model: ic_weighted opts into walk-forward rolling-IC weights. No parameter search; not a performance claim. Lookahead boundary (locked by tests): - Training is walk-forward: at each date d, a (factor[t], fwd_h[t]) pair is admissible only once REALIZED -- t + h <= d in trading-day positions (the h-day forward return of factor date t realizes at t+h). A perturbation test proves unrealized forward returns cannot change any date's weights; a boundary test locks the exact t+h cutoff via min_periods. - Forward returns are computed at the alpha boundary and handed ONLY to alpha.fit; the factor layer never sees them (invariant #1 unchanged). Model: - weights = mean per-factor cross-sectional rank IC over the trailing window of realized observations (rolling, conservative default; expanding optional), L1-normalized and SIGN-PRESERVING (negative-IC factor gets a negative weight). - Fallback: any factor with < min_periods valid realized ICs (or degenerate ICs) -> that date uses the EQUAL-WEIGHT mean (bitwise EqualWeightAlpha) and is logged; the report discloses coverage + per-rebalance effective weights (fallback rows flagged). Wiring: - _build_alpha dispatch (alpha.model now a validated Literal); horizon tied to the first forward-return period; alpha_summary/alpha_weights on Phase0Result/Phase2Result; 'Alpha model' is a REQUIRED report section; combo-score labels name the active alpha. - config/phase3_real_ic_weighted.yaml: identical to phase3_real_multifactor except alpha.model (directly comparable). Real runs (not in CI): - Equal-weight phase3 regression rerun: annual -9.05% / IC 0.0083 unchanged. - ic_weighted: annual -3.57%, maxDD -12.93%, training coverage 201/221 (20 fallbacks, all before the window filled). Beating equal weight here is NOT a performance claim: one-year window + per-period weight sign flips (e.g. momentum_20 -0.58 -> +0.36) show small-sample instability, disclosed. Tests: 269 passed (was 249): +12 alpha unit (incl. perturbation + cutoff boundary), +6 pipeline wiring, +2 report disclosure. Demo equal-weight numbers locked unchanged (ic 0.96 / annual 0.84).
StackOverFlow11
added a commit
that referenced
this pull request
Jun 10, 2026
…er review Doc-only sync in CLAUDE.md + AGENTS.md (both files, same drift): - merged-PR lists: add #10 (progress docs) and #11 (P3-2). - P3-2 entry: 'branch, awaiting acceptance' -> 'PR #11 merged to main'. - P3-3 entry: 'branch, awaiting acceptance' -> 'PR #12 OPEN, review HIGHs+LOW fixed, awaiting acceptance/merge'.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
P3-2 — walk-forward IC-weighted alpha. Adds the first LEARNED factor combination (
alpha/ic_weight.py::RollingICWeightAlpha,alpha.model: ic_weighted) without touching the factor / portfolio / runtime boundaries.EqualWeightAlphastays the default and the regression baseline. No parameter search; NOT a performance claim.Implementation
rolling(trailingwindowtrading days — conservative default, 60d / min_periods 20) orexpanding._build_alphadispatches byalpha.model(now a validatedLiteral); the IC horizon is tied to the first configured forward-return period, so training and the reported IC share one definition.alpha_summary+ the full per-date effective-weights log land onPhase0Result/Phase2Result; "Alpha model" is a required report section; combo-score labels name the active alpha.config/phase3_real_ic_weighted.yamlis identical tophase3_real_multifactor.yamlexceptalpha.model— the two real baselines are directly comparable.Lookahead boundary (the point of this PR)
t + h <= din trading-day positions (the h-day forward return of factor date t realizes at t+h). The full IC series is stored, but consumption is strictly the realized-at-d slice.t + hcutoff (min_periods straddling 6-vs-7 available rows)._alpha_forward_returns) and handed ONLY toalpha.fit. The factor layer never sees them (invariant feat: Phase 0 MVP + Phase 1 bias-boundary (PIT · ann_date · tradability · neutralization) #1).Fallback contract
Any factor with
< min_periodsvalid realized ICs in the window (or degenerate all-zero/NaN ICs) → that date falls back to the equal-weight mean (bitwise identical toEqualWeightAlpha), is logged, and the report disclosed: training coverage, fallback count, and the per-rebalance effective weights table with fallback rows flagged.Real-data results (manual, not in CI; SSE50 2023-07 ~ 2024-06, same window/factors/neutralization)
Quality gates
pytest -p no:cacheproviderruff check .validate-config×5 (incl. phase3_real_ic_weighted)run-phase0demo (equal-weight default)Tests (network-free)
test_ic_weight_alpha.py(12): future-perturbation invariance, exact t+h cutoff, equal-weight fallback equivalence, single-factor ±1 degeneration, L1+sign stability, degenerate-IC fallback, rolling vs expanding, fit-requires-forward-returns, dated-cross-section contract, immutability, weights/fallback log.test_ic_alpha_pipeline.py(6): config dispatch + unknown-model ConfigError, equal-weight demo numbers locked, ic_weighted demo e2e disclosure + no-secret, ic-vs-equal divergence on opposed-rank synthetic factors.test_phase2_baseline.py(+2): report shows model/weights/fallback for both models.Docs
RUNBOOK (P3-2 section), TEST_REPORT (269 + breakdown), BIAS_AUDIT (walk-forward alpha boundary added to the 未来函数 section, regenerated from the renderer), CLAUDE.md / AGENTS.md progress.