The matchup oracle

Pick a league, a home side and an away side. The page computes the scoring rates λ (home) and μ (away) from the fitted ratings, builds the full score matrix with the Dixon–Coles low-score correction, and reads off the odds — all client-side, from the same ~40 parameters per league (two per team, plus a league intercept, home advantage and ρ) the model actually learned. The goals model was trained on what happened; the xG blend on what deserved to happen (85% xG, 15% goals).

Score probability matrix

P(exact scoreline), % · rows = home goals, columns = away goals

Source: Understat, 2023/24–2025/26 · computed by pipeline/model_dixon-coles.py · matrix truncated at 7 goals and renormalised

Match odds (1X2)

probability of home win / draw / away win, %

    λ · home rate
    –
    expected home goals
    μ · away rate
    –
    expected away goals
    Over 2.5 goals
    –
    –
    Most likely score
    –
    –
    Derived from the score matrix at left · lower panel: model rates

    Five most likely scorelines

    home–away · % of all outcomes

      Same matrix, sorted · keyboard-readable twin of the heatmap

      Ratings are frozen at each league's final 2025/26 matchday (2026-05-16 to 2026-05-24); the oracle prices a hypothetical fixture played then.1 Team names follow Understat spellings.

      Every team, reduced to two numbers

      Under the hood each team is just an attack rating and a defence rating on the log scale. Exponentiate them against a league-average opponent at a neutral venue and you get something legible: expected goals scored and conceded per match. Plotted together, the league sorts itself into quadrants — and the outliers write the season's story. Bayern Munich sit further from the pack than any side in Europe at +1.86 expected goal difference per match (3.00 scored, 1.14 conceded); PSG (+1.44), Barcelona (+1.43) and Inter (+1.30) rule their islands, while Arsenal (+1.23) hold off Manchester City (+1.04) on defence, not attack.

      Attack vs defence, xG-blend ratings

      expected goals for and against per match vs a league-average opponent, neutral venue · up = meaner defence · axes fixed across leagues

      Source: Understat, 2023/24–2025/26 · computed by pipeline/model_dixon-coles.py
      Team ratings — click a header to sort
      Team xG for / match xG against / match Strength (diff) Finishing Δ

      Finishing Δ is the gap between the goals-model attack rating and the xG-blend attack rating: positive means the side has been scoring above what its chances predict. Pisa (−0.31) and Real Oviedo (−0.29) are the biggest underperformers in Europe; Bayern (+0.15) the biggest overperformer.

      The τ correction: fixing the model's one lie

      Two independent Poisson processes underestimate how often football produces 0–0 and 1–1, and overestimate 1–0 and 0–1. Dixon and Coles patched exactly those four cells with a single dependence parameter ρ, leaving the rest of the matrix untouched. Our fits reproduce their 1997 finding: ρ comes out negative in four of five leagues — Premier League −0.102, Bundesliga −0.071, Ligue 1 −0.046, Serie A −0.033 — which raises the goalless and 1–1 draws. La Liga is the mild exception at +0.012, essentially independence.

      Drag ρ and watch the four cells move. Everything else only shifts through renormalisation.

      −0.102 typical fixture: λ = 1.40, μ = 1.10

      Independent Poisson

      P(scoreline), % · scores 0–3 shown

      λ = 1.40, μ = 1.10 · ρ = 0

      With the τ correction

      P(scoreline), % · scores 0–3 shown

      same rates · four low-score cells perturbed, matrix renormalised

      What moved

        τ multiplies P(0,0) by 1 − λμρ, P(0,1) by 1 + λρ, P(1,0) by 1 + μρ and P(1,1) by 1 − ρ. The slider stays inside the validity range max(−1/λ, −1/μ) ≤ ρ ≤ min(1/λμ, 1).

        Tuning: how fast to forget, how much to trust xG

        Old matches lie about current strength, so each match is down-weighted by e^(−ξ·days). But ξ can't be estimated by maximum likelihood — a bigger ξ always "fits" recent data better — so it is grid-searched on out-of-sample forecast skill over an 828-match validation window. The curve bottoms out at ξ* = 0.002 per day: a match from 347 days ago counts half as much. That lands dead-centre of the 0.0018–0.0033/day range modern Dixon–Coles replications report.

        The same protocol chooses how much of the training target should be xG rather than goals. The answer: most of it. w* = 0.85 edges out every alternative, beating the pure-goals target on validation RPS 0.20066 vs 0.20276 — finishing luck, integrated out.

        The memory curve: skill vs decay rate ξ

        validation RPS (lower = better) · y-axis zoomed to the decision band

        Source: 126-day pooled validation window, weekly refits · n = 828 matches

        The trust curve: skill vs xG share w

        validation RPS (lower = better) · target g* = w·xG + (1−w)·goals

        Source: 126-day pooled validation window, weekly refits · n = 828 matches

        The honesty table

        A forecaster earns its keep out of sample. Protocol: rolling-origin backtest over each league's final 60 days — refit before every matchweek on all prior matches (time-decayed), predict that week, score it, move on. 385 matches, never seen at fit time. Both models beat both naive baselines by 9–11% on RPS — squarely in the 0.19–0.21 band the literature calls a good Dixon–Coles fit. The fine print: on this holdout the goals model (0.2097) and the xG blend (0.2101) are statistically tied, even though validation preferred the blend. Honest models keep both numbers on the table.

        Pooled holdout skill: model vs baselines

        ranked probability score, lower = better · n = 385 matches, spring 2026

        • Dixon–Coles models
        • Naive baselines
        Source: rolling weekly-refit backtest, final 60 days per league · computed by pipeline/model_dixon-coles.py
        By league — RPS and log-loss, holdout (best RPS per row in bold) · click headers to sort
        League n Goals RPS xG-blend RPS Base-rate RPS Uniform RPS Goals log-loss xG-blend log-loss

        RPS treats a draw as "closer" to a home win than an away win is. Wheatcroft (2021) argues that ordinality premise is shaky and log-loss suffices — so both are reported; the ranking of models is the same under either.2

        1 Reference dates: EPL, La Liga, Serie A 2026-05-24; Ligue 1 2026-05-17; Bundesliga 2026-05-16. 2 Wheatcroft, E. (2021), JQAS. Full derivations in the research brief: Dixon & Coles (1997), Karlis & Ntzoufras (2003) for the bivariate extension, Ley et al. (2019) for the half-life reparametrisation.