New in allocator.py:
- vix_pead mode: boosts sizing at VIX>18 (PEAD favorable), penalizes VIX 15-18
- Fixed vix_scaler application to support boost (scaler > 1.0)
Results:
- v6new.19 (spy_qqq risk_off=0.55): SQS 62.8 — reduces size in favorable PEAD regime
- v6new.21 (vix_pead boost+penalty): SQS 46.9 — penalty too aggressive, kills trades
VIX signal is real (62.3% vs 48.2% WR) but sizing alone can't capture it:
- Boosting doesn't help because same trades just get bigger
- Penalizing shrinks or drops trades, losing count
- Need the signal in TRADE SELECTION, not just sizing
v6new.9 (SQS 63.3) remains best.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New features:
- _enrich_macro_features() in snapshot_export: adds macro_vix, macro_hy_spread from FRED
- compute_return_max_long_score_v10: +12% bonus in favorable regime (VIX>18+HY>3.25)
- _macro_regime_score(): regime-aware scoring component
Findings:
- VIX signal is statistically strong: 62.3% WR (VIX>18+HY>3.25) vs 50.8% (other)
- But scoring bonus promotes marginal trades, diluting OOS quality
- Same pattern as eps_growth, drift bonus: raw signal ≠ scoring improvement
- v6new.17 SQS 59.1 < v6new.9 SQS 63.3
v6new.9 remains best at SQS 63.3 after 17 experiments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v6new.14 (warmup 9d, hold 30d): SQS 61.2 — 100% WFV positive but DD 8.6% hurts RQS
v6new.15 (tighter early exit d2/r0.20): SQS 58.6 — too aggressive cutting
v6new.16 (warmup 8d, hold 27d): SQS 59.8 — RM worst -4.11% degrades
Execution parameters are already optimal in v6new.9. 16 experiments done.
v6new.9 confirmed as final best at SQS 63.3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Widened mixed_notprovided gap cap 0.08->0.15, tightened bullish_raised_strong
(mcap>10B, reaction<15%). WFV gap improved to 9.85% but lost 2 trades vs v6new.9.
Final standings: v6new.9 (SQS 63.3) > v6.29 (62.8) > v6new.11 (62.7)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pruned bullish_raised_strong (OOS -16.7%) and unknown_ome (OOS 0% WR) from v6new.9.
Result: trade count dropped 118->105, activity penalty offset the quality gain.
v6new.9 (SQS 63.3) confirmed as new #1 over v6.29 (62.8).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove 5 non-alpha features (earnings surprise, risk penalty, parse confidence,
direction clarity, LM sentiment) from composite score to eliminate double-counting
with hard gates and noise sources. Redistribute weights to 5 alpha features.
Add default-deny for unknown event types, no-follow-through early exit (D+1),
kill switch log-only mode, macro regime size scaler. Remove SUE gate (Gate 8).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace naive abs(reaction_day_return) fallback with a composite score
from 4 market microstructure features available at entry time:
1. Reaction quality (35%) — moderate positive return (PEAD zone) is
ideal; extreme positives penalized as "priced in"
2. Close strength (30%) — close near session high = buyers won
3. Volume conviction (20%) — 1.2-2x is healthy; >3x is exhaustion
4. Gap quality (15%) — small positive gap = orderly strength
Real data results (14 events, b1868603 snapshot):
- Score filters out 6 of 10 losers (DDOG -11.7%, META -9.1%, etc.)
- With threshold 0.5: return -2.63% → +0.27%, drawdown 4.24% → 0.86%
- Profit factor 0.44 → 1.16 (turns profitable)
- MSFT loss (-8.7%) is macro-driven, not predictable from stock features
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- BacktestRunner.run() now iterates all NYSE trading days (not just candidate
days) via SnapshotStore.all_trading_days() so stop/target/time exits are
checked every day, not only on days with new candidates
- Record initial DailyPortfolioState before simulation loop starts so
total_return_pct is computed relative to the true initial equity (100k),
not the first post-entry equity snapshot
- SnapshotStore._fetch_event_metadata() now synthesises event_timestamp from
event_date + 21:00 UTC when filed_at_utc is NULL (transparent enrichment at
loader boundary, not silent substitution in selector)
- SnapshotStore._async_load() maps event_close → entry_price_est when the
column is absent, and derives score from abs(reaction_day_return) when the
Parquet snapshot has no score column
- Add --snapshot-dir CLI flag to BacktestRunner to override the default
parquet_dir base path (needed for non-standard snapshot locations)
- Fix integration test assertion: total_trading_days >= 2 (was == 2)
- Add configs/experiments/realdata_test_v1.json for real Phase 3 snapshot runs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>