Sports Dashboard

MI Bivariate Poisson + Dixon-Coles + Elo

← Back to Blog
|Signal Test|REJECTED

Cross-Market Surprise: The Strongest Signal That Couldn't Filter

BTTS and O/U specialist markets diverge from our Poisson model by +8.4pp and +12.3pp (both p<0.0001, 41K OOS). But when wired as binary filters on AH/O/U bets, marginal ROI is +0.1% (BTTS) and +0.2% (O/U) — both fail bootstrap significance. The signal predicts match texture, not who wins. Pivoting to direct BTTS value betting where the hit-rate edge translates directly to profit.

BTTS Spread
+8.4pp
Q5 vs Q1, p<0.0001
O/U Spread
+12.3pp
Q5 vs Q1, p<0.0001
As Filter
REJECTED
+0.1% / +0.2% marginal
Matches
36,772
26 leagues

Cross-Market Surprise: The Strongest Signal That Couldn't Filter

The BTTS and O/U specialist markets know something our bivariate Poisson model doesn't. We proved it with 41,000 out-of-sample matches. Then we tried to use that knowledge to improve our existing bets, and it failed. Here's why — and what we're doing about it.

The Question

Our Poisson model derives everything from 1X2 odds: home win probability, draw probability, away win probability. From those three numbers, it computes P(BTTS Yes) and P(Over 2.5). But specialist bookmakers who *only* price BTTS and O/U markets use additional information — attacking patterns, defensive vulnerabilities, GK form, match context, goal correlation.

Hypothesis: When the specialist market disagrees with our model about BTTS/O/U probabilities, the specialist is right. We call this disagreement the "surprise" signal. Can we use it to filter out bad bets from our existing AH and O/U pipeline?

What We Found

The raw signal is massive. On 41,000 out-of-sample matches:

SignalQ5 Hit RateQ1 Hit RateSpreadp-value
BTTS Surprise55.2%46.8%**+8.4pp**<0.0001
O/U Surprise54.2%41.9%**+12.3pp**<0.0001

Perfect quintile monotonicity on both. This is real — not an artifact.

But when we wired these as binary filters on our existing bet stack:

SignalAs Filter OnMarginal ROIp-valueBets Removed
BTTS SurpriseAH + 1X2+0.1%0.47187 / 8,792
O/U SurpriseO/U bets+0.2% (entry-adj)0.453,771 / 8,792

Neither passes bootstrap significance. The BTTS filter barely removes any bets. The O/U filter removes 43% of bets but the improvement is statistically indistinguishable from noise.

The Nuance

Both signals passed the *structural* gates with flying colors:

  • Walk-forward (Gate 10): 12/12 season folds positive for both signals
  • Regime stratification (Gate 7): All regime phases (early/mid/late) positive for both
  • Odds quality tiers: Signal works across sharp, medium, and soft leagues

What killed them was the *marginal contribution* gates:

  • Gate 5 (Bootstrap significance): p=0.47 and p=0.45 — both pure noise after bootstrap
  • Gate 9 (Practical significance): +0.1% and +0.2% marginal — below the +0.5pp threshold

The O/U filter had a more interesting closing-odds marginal (+0.7%) but the entry-adjusted number collapses to +0.2%. And its IS/OOS gap (3.2pp) fails Gate 6.

What Didn't Work

The core design flaw: we used a *texture signal* to filter *side-market bets*.

The cross-market surprise tells you "this match will be chaotic" or "this match will be controlled." That's useful for betting BTTS Yes/No. It's nearly useless for betting "Home -0.75" or "Over 2.5" through our existing pipeline, because:

  1. Our pipeline already filters for high-CLV matches (minEdge >= 7%, maxOdds <= 2.0)
  2. Those matches are already selected for scoring texture — the edge comes from model calibration, not match texture
  3. A binary Q1/Q5 threshold throws away the continuous information in the signal

The BTTS filter at Q1 threshold (-0.08) only catches 187 bets because most of our high-CLV AH bets happen in "normal" matches that aren't in the extreme bearish quintile.

What This Means

The signal is not deployed as a filter. Both entries are marked "rejected" in the registry.

But the signal is real, and the infrastructure is built:

  • lib/cross-market/surprise.ts computes btts_surprise and ou_surprise for every match
  • Every PrecomputedMatch now carries cross-market data
  • The filter config flags exist in EvalConfig (disabled by default)

What's Next

Path 1: Direct BTTS Value Betting. Instead of using the surprise signal to filter AH bets, test it as a direct BTTS market bet:

  • Compute our model's P(BTTS Yes) from the Poisson grid
  • Compare to devigged BTTS market odds
  • When our model finds CLV in the BTTS market, the surprise signal validates the direction
  • The +8.4pp hit-rate edge translates directly to profit in this market

This is the right way to use a texture signal — bet the texture market directly.

REJECTEDSignal: cross-market-btts-surprise|2026-04-01