Appearance
CPI / NFP Pre-Release Drift (Phase 2g)
The one new backtest hypothesis of Phase 2g (workstream 4): does the pre-announcement drift that the FOMC hourly sleeve found extend to the two other top-tier US macro releases — CPI and the Employment Situation (nonfarm payrolls / NFP)? Two candidates, cpi_hourly and nfp_hourly, each its own pre-registration and its own ledger row. Code: macro_predrift_hourly.py.
Pre-registration (written before data contact)
Frozen in the module docstring before the first real-data run, per the binding protocol.
Template. The fomc_hourly module, ported. Enter long at the first hourly bar K trading days before the scheduled release; exit at the close of the last hourly bar strictly before the 08:30 ET release — the 07:00–08:00 ET bar (closes 08:00 ET), the CPI/NFP analog of the FOMC sleeve's 13:00 ET exit before the 14:00 statement. Long/flat, 1 contract, one cycle per release, no gate, no stops.
Mechanism prior — deliberately weaker than FOMC. Lucca & Moench (2015)'s pre-announcement drift is FOMC-specific; do not borrow its strength. The relevant literature for CPI/NFP is the macro-announcement premium (Savor & Wilson 2013, JFQA): elevated average returns on announcement days as compensation for announcement risk — earned largely around/through the release, not as a clean pre-release drift. Flattening before 08:30 tests specifically whether any of that premium accrues before the print. A fail is informative, not a surprise — it bounds where the premium lives (through the release, not before), exactly as the failed daily-FOMC variant bounded the pre-FOMC drift to sub-daily resolution.
Grid (2 combos, re-fit per walk-forward window): K ∈ {1, 2}. Nothing else fitted.
Fixed priors (never fitted): exit boundary 08:00 ET; long-only; 1 contract; no trend gate (the premium is unconditional in the literature); scheduled releases only; MES + MNQ (no deep-parent leg — parents are daily-only today).
Calendars (frozen, exogenous). CPI_RELEASE_DAYS and NFP_RELEASE_DAYS, 2010–2026, are committed verbatim in the module. Source: the BLS per-year schedule archives (bls.gov/schedule/<year>/home.htm) cross-checked date-by-date against the BLS actual-publication archives (bls.gov/bls/news-release/cpi.htm and .../empsit.htm); 2025–2026 from the BLS forward schedule plus confirmed actuals. Both government-shutdown reschedules are encoded: Oct 2013 (CPI 10-30 / 11-20; NFP 10-22 / 11-08) and 2025 (Sep data delayed to CPI 10-24 / NFP 11-20, and no October-data release in either series, so those months are intentionally absent). Dates after 2026-07-14 are the BLS forward schedule, flagged NEEDS VERIFICATION (post-cutoff), mirroring the fomc_hourly convention. Per protocol 7, cross-check a sample against a second calendar (ForexFactory archive) before any live trade.
Why run it — power, and overlap honesty
- Power is the draw. ~12 events/yr each → roughly 60+ stitched-OOS events per family on the micro span (~1.5× FOMC's 42), and ~170 on the ES/NQ parents if the workstream-3b hourly backfill lands.
- Overlap accounting (pre-registered). A K≥1 cycle holds overnight, so it overlaps
overnight_drifton the sessions it spans. The judged run must report correlation to H1 and to the registered sleeves, plus the count of shared position-hours withovernight_drift, so a "new stream" claim is defensible rather than the same overnight premium wearing an event mask.
Pass bar
The standard ledger protocol: stitched walk-forward OOS Sharpe beating both buy-and-hold and the re-fit swing_breakout baseline on both symbols, bootstrap 90% CI excluding 0, drawdown not materially worse — plus the Phase 2f corr-to-champion addition and the overlap accounting above. run_protocol(name ∈ {cpi_hourly, nfp_hourly}, symbol ∈ {MES, MNQ}, bars='1h', slippage_ticks=1.0, run_baseline=False); two calls per family; one disclosed bug-fix mulligan max.
Results — both fail (as the prior anticipated)
Run through run_protocol on MES/MNQ hourly (R2 cache), 1-tick slippage, ~62 OOS events per family, OOS span 2020-12 → 2026-03:
| Candidate | Symbol | OOS Sharpe | 90% CI | vs baseline / bh | PF | corr-H1 | Verdict |
|---|---|---|---|---|---|---|---|
cpi_hourly | MES | 0.33 | [−0.12, 0.83] | < 0.47 / ≈ 0.47 | 1.55 | 0.06 | ❌ |
cpi_hourly | MNQ | 0.42 | [−0.05, 0.89] | < 0.66 / ≈ 0.42 | 1.72 | 0.08 | ❌ |
nfp_hourly | MES | −0.28 | [−0.93, 0.43] | negative | 0.79 | 0.18 | ❌ |
nfp_hourly | MNQ | −0.40 | [−1.05, 0.33] | negative | 0.72 | 0.15 | ❌ |
CPI is direction-only: positive and near-orthogonal to H1 (corr ~0.06/0.08), PF > 1, slippage-robust — but the CI includes 0 and it sits below the re-fit baseline, so it clears no bar. NFP is outright negative (PF < 1). This is the pre-registered expectation borne out: the announcement premium (Savor–Wilson) is an on-day effect, not a clean pre-release drift — so flattening before 08:30 harvests nothing durable. An informative fail: it bounds where the premium lives (through the release, not before it), and the event-research budget is better spent on the FOMC sleeve, whose pre-drift is CI-verified once the parent-hourly backfill supplies the events.
Both land ❌ fail rows in the ledger, per protocol 1 (every judged run recorded, pass or fail). Offline tests (tests/test_macro_predrift.py) pin the entry/exit bar selection and calendar shape; the 2026-H2 calendar tail stays flagged NEEDS VERIFICATION.