Decomposing Every Source of Edge: What's Actually Making Money
Systematic leave-one-out analysis of every production feature. maxEdge=15% cap is +0.99pp (biggest improvement available). minEdge=7% is optimal. Variance filter slightly hurts. Defiance filter genuinely helps. CLV bug found and fixed (1X2 proxy inflated AH CLV by ~11pp). ROI (+29.69%) is real.
Decomposing Every Source of Edge: What's Actually Making Money
Production reports +29.69% ROI on 27 bets. The backtest shows -2.37% on 3,201 bets. Before we can trust either number, we need to know: which features contribute to the edge, and how much?
This post documents a systematic leave-one-out decomposition of every production feature. For each one, we measure: what happens to ROI when we turn it off?
The Production Stack
The production picks engine (picks-engine.ts, 1,702 lines) has 15+ features that affect which bets are taken. They fall into categories:
Core Filters (change bet selection)
- minEdge >= 7% — only bet when model sees 7%+ edge
- maxOdds <= 2.0 — no longshots
- No draws — 1X2 draw bets excluded
- AH-only portfolio — only Asian Handicap bets risked
- Variance filter — only bet regression candidates (xG divergence >= 3 goals)
- Congestion filter — skip teams playing 3+ times in 8 days
- Defiance filter — skip teams defying model 10+ times
- Skip early matchdays — no bets GW 1-5
- Pass rate filter — skip markets with <50% historical hit rate
- disableAHHome — Championship: no home AH (known bias)
Lambda Adjustments (modify predictions)
- GK PSxG adjustment — elite GK reduces opponent's expected goals ±15%
- Per-league AH home discount — reduces home inflation in specific leagues
- Per-league totals overdispersion — Negative Binomial for O/U (AH-only, so indirect)
Regime/Context (gate or size bets)
- Regime skipBet — skip home bets in late season when HFA collapsed
- Regime stake multiplier — 0.25x/0.5x/1.0x based on context
- Motivation — relegation trap reduces confidence by -40
- Promoted team penalty — -25 confidence GW 1-12
The Test
For each feature: run the full evaluator with ALL features on, then with THIS feature off. Marginal = difference. Positive marginal means the feature helps. Negative means it hurts.
Results to follow.