Method
How this is built and how it is measured
Short version: a Dixon-Coles goals model, refitted every day on matches played before that day, published before kickoff, and scored with log loss against Pinnacle's closing line. The code is public.
The model
One model is fitted per division, on that division's matches only. Ratings are not comparable across countries — a Championship attack rating of 1.1 says nothing about a Bundesliga one — which is also why this site does not price European competitions: there is no common scale on which to compare the two teams.
Every team carries an attack rating and a defence rating, and there is one league-wide home advantage. Expected goals for a fixture are
λ_home = α_home × β_away × γ × league_mean
λ_away = α_away × β_home × league_mean
Goals are then modelled as Poisson counts. Two Poissons multiply into a grid of scorelines; below the diagonal is a home win, the diagonal is a draw, above it an away win. Nothing in the model knows anything about football — the ratings are chosen by maximum penalised likelihood, which is to say the numbers are pushed around until the observed results stop being surprising.
Dixon and Coles (1997) add two things to that, and both matter:
- A low-score correction. Independent Poissons say the two teams' goals are unrelated. They are not: 0-0 and 1-1 happen more often than the maths predicts and 1-0 and 0-1 less often, because a level game is played differently from a decided one. Four cells of the grid get a multiplier.
- Exponential time decay. A match from 2016 says nothing about this
season. Each match enters with weight
exp(−ξ · days_ago). We use ξ = 0.002 per day, a half-life of 347 days.
The rule that makes the numbers mean anything
To price a match on date D, the model may only see matches played strictly before D. No exceptions. Fit on everything and then "predict" the past and the results come out spectacular and worthless — that is lookahead bias, and it invalidates most amateur backtests you will ever be shown.
Here it is enforced structurally rather than by care: matches are sorted by date once and the training slice is taken by binary search, so a future match physically cannot enter a fit. The same discipline applies to the two hyperparameters — the decay rate and the prior width were chosen on 2017/18–2020/21 and then frozen, because picking them on the data you report is a slower form of the same bias.
How the score is computed
Log loss: take the probability we gave to the outcome that actually happened, apply
−log(p), average over matches. Say 90% and be right, you pay 0.105. Say 10%
and be right, you pay 2.303. Lower is better, and unlike ROI it cannot be flattered by
picking which bets to count.
Closing odds are turned into probabilities by inverting the three prices and dividing each by their total, which removes the bookmaker's margin proportionally. Two reference points frame every number on this site: predicting 1/3 each time scores 1.0986, and the closing line scores about 0.9484. The whole of football knowledge is the gap between those two.
The backtest — and why it is on this page, not the scorecard
Before a single live prediction existed, this exact model was run walk-forward over nine seasons. That result is a useful prior and it is not a track record, so it lives here and never appears next to the live numbers.
What this site will not do
- Sell picks, "sure things" or accumulator tips.
- Put an "AI" badge on a number without the score beside it.
- Quote a return on investment as evidence of skill.
- Start the record over after a bad run.
Limitations, plainly
- Goals only. No shot data, no expected goals, no lineups. The model has never heard of a suspension.
- Promoted teams start at league average and shrink towards their real level as matches arrive. Their first few weeks are the model's worst.
- De-vigging is proportional, the simplest method. It slightly overstates longshot probabilities, which if anything flatters us.
- Closing odds are not available for every match. Matches without them stay out of the score rather than being quietly counted.
Where these numbers come from. The backtest above is not a
claim you have to take on faith: the research repository holds the model, the
walk-forward harness, the hyperparameter search and the tests, along with the
raw CSVs, so python run.py reproduces every figure on this page —
https://github.com/GitSimaao/pl-dixon-coles.
The code running this site is at
https://github.com/GitSimaao/proofodds.
Results and closing prices come from
football-data.co.uk.