Full Factorial Signal Decomposition: 2,048 Combinations Tested
Ran every possible on/off combination of 11 signals (2^11 = 2,048 configs). Best portfolio: regime + crossBtts + leagueExcl + finishingLuck at +10.4% entry ROI. Four signals confirmed dead or harmful. Biggest surprise: leagueExcl and contextXg destructively interfere — pick one, not both.
Full Factorial Signal Decomposition: 2^11 = 2,048 Combinations Tested
We ran a complete factorial design across all 11 signals in the stack. Every possible on/off combination — 2,048 configs — backtested against the full historical dataset. The goal: figure out which signals actually earn their place, which are dead weight, and which actively hurt.
Setup
11 signals, two types:
- Filters (remove bets): variance, regime, gkChange, finishing, crossBtts, crossOu, leagueExcl
- Lambdas (modify model parameters): squadStrength, hfaOverride, finishingLuck, contextXg
Each combination ran through the full loadAllData → evaluateBets pipeline with walk-forward solver caches. 32 chunks of 64 configs each, processed locally on Apr 8.
Main Effects
The main effect measures the average impact of turning a signal ON vs OFF across all other combinations.
| Signal | Type | ROI Effect | CLV Effect | N Change | Verdict |
|---|---|---|---|---|---|
| **leagueExcl** | filter | **+0.087%** | **+0.063%** | -1,406 | Best signal — positive on both |
| **contextXg** | lambda | **+0.121%** | **+0.262%** | +961 | Strong — but see interactions |
| **regime** | filter | +0.080% | ~0% | -55 | Clean positive, surgical filter |
| **finishing** | filter | +0.057% | -0.018% | -225 | Marginal positive ROI |
| **crossBtts** | filter | +0.051% | -0.022% | -490 | Marginal positive ROI |
| variance | filter | -0.130% | ~0% | -448 | Slight drag |
| finishingLuck | lambda | -0.184% | +0.123% | -1 | Hurts ROI, helps CLV |
| hfaOverride | lambda | **-0.618%** | +0.309% | +266 | Harmful to ROI despite CLV boost |
| **crossOu** | filter | **-0.745%** | -0.073% | -1,912 | Worst signal — negative on everything |
| gkChange | filter | 0 | 0 | 0 | Dead — zero effect |
| squadStrength | lambda | 0 | 0 | 0 | Dead — zero effect |
Key Interactions
Most signal pairs are independent. Three interactions matter:
| Pair | Interaction (ROI) | Meaning |
|---|---|---|
| **leagueExcl x contextXg** | **-0.703%** | Destructive interference — don't use both |
| hfaOverride x finishingLuck | -0.421% | Both modify lambdas in conflicting ways |
| crossOu x contextXg | +0.332% | crossOu rescues some contextXg value |
The leagueExcl x contextXg interaction is the biggest finding. Both are individually strong, but together they cancel out. The league exclusion list (segunda, la-liga, ligue-2) removes exactly the matches where contextXg adds value.
Best Combination
Combo 594: regime + crossBtts + leagueExcl + finishingLuck
| Metric | Value |
|---|---|
| Entry ROI | **+10.4%** |
| CLV | **+13.7%** |
| Closing ROI | -1.6% |
| N | 6,546 |
This is the best closing ROI of all 2,048 combinations. The core insight: the optimal portfolio is lean. Four signals, two of which (regime, crossBtts) are surgical filters removing <500 bets combined.
What to Kill
- gkChange — zero effect across all combinations. Remove from the codebase.
- squadStrength — zero effect across all combinations. Remove.
- crossOu — actively harmful (-0.75% ROI, -0.07% CLV). Already suspected, now confirmed.
- hfaOverride — looked promising in partial analysis but full factorial shows -0.62% ROI. The CLV boost doesn't translate to returns.
Stepwise Portfolio Build
Greedy forward selection confirms the lean portfolio:
- +leagueExcl → closing ROI = -1.93% (+0.47pp vs baseline)
- +regime → closing ROI = -1.72% (+0.21pp marginal)
After step 2, no additional signal improves closing ROI enough to justify the complexity. The best combo adds crossBtts + finishingLuck for another ~0.1pp, which the full factorial confirms but the greedy algorithm doesn't reach.
Shadow Configs
Three configs generated for live comparison on the gauntlet:
- best: regime + crossBtts + leagueExcl + finishingLuck
- portfolio-1: leagueExcl only (simplest)
- portfolio-2: leagueExcl + regime (middle ground)
These will run in shadow mode alongside production to validate the factorial findings on live data before any deployment changes.
Bottom Line
The stack was carrying 4 dead or harmful signals. The optimal portfolio is 4 signals — regime, crossBtts, leagueExcl, and finishingLuck — achieving +10.4% entry ROI on 6,546 bets. The biggest surprise: contextXg is individually strong but destructively interferes with leagueExcl, making it a "pick one" choice. We picked leagueExcl.