Updated

The maths

Mathematical Methodology

The equations, the constants and the fitted parameters behind every number on the site, written out rather than alluded to.

1. From match history to a scoring rate

Every constant below is a constant in the code, not a rounded description of one.

Take a club’s last 15 completed matches, newest first, indexed i = 0, 1, 2 … Each match carries weight 0.9i, so the most recent counts 1, the one before it 0.9, and the fifteenth about 0.23. An attacking figure and a defensive figure come out of that weighted window:

att = Σ wi·fi / Σ wi · def = Σ wi·ai / Σ wi · wi = 0.9i

where f and a are goals for and against — or expected goals for and against, when at least 60% of the window’s weight is covered by an expected-goals record. Share of weight rather than share of matches is the point: a missing record in last weekend’s match matters more than a missing one from December.

Both figures are then shrunk toward the competition’s own scoring mean μ, taken from its two most recent seasons as they stood before this kick-off:

shrunk = (n·v + 5·μ) / (n + 5)

with n the number of matches actually in the window. Five is the strength of the prior: a club with fifteen matches keeps three quarters of its own figure, a club with three keeps a bit over a third. This is what stops a promoted side’s two-match start from producing a confident rating.

2. The two expected-goal rates

The shrunk figures combine into one rate per side, relative to the competition’s home and away means (mh, ma), with μ their midpoint:

λhome = mh · (atthome / μ) · (defaway / μ)
λaway = ma · (attaway / μ) · (defhome / μ)

Home advantage is not a bonus we add — it is already inside mh and ma, measured from that competition’s own results. Where a per-league fit exists, λhome also carries a fitted home scaling factor. Both rates are finally clamped to the range 0.2 to 4.5 goals, so a broken input cannot produce a fixture the model thinks will finish 9-0.

3. Dixon-Coles: the scoreline grid

Goals for each side are modelled as independent Poisson counts, and then the four lowest-scoring cells are corrected — plain Poisson gets 0-0, 1-0, 0-1 and 1-1 measurably wrong, which is the observation the Dixon-Coles adjustment exists to fix. For a scoreline i-j:

P(i, j) ∝ Pois(i; λhome) · Pois(j; λaway) · τ(i, j)

τ(0,0) = 1 − λhomeλawayρ
τ(0,1) = 1 + λhomeρ
τ(1,0) = 1 + λawayρ
τ(1,1) = 1 − ρ
τ(i,j) = 1 everywhere else

The grid runs from 0 to 10 goals per side — 121 cells — and is normalised so it sums to one. ρ is fitted per competition where we have a fit for it; where we do not, it falls back to −0.10, and the prediction is stamped with the fallback model version rather than the fitted one.

4. Reading the markets off the grid

Every market on the site is a sum over the same 121 cells.

How each published market is computed from the scoreline grid
MarketDefinition on the grid
Home / Draw / AwayΣ cells with i > j · Σ cells with i = j · Σ cells with i < j
Over/under 2.5Σ cells with i + j ≥ 3; under is the remainder
Both teams to scoreΣ cells with i ≥ 1 and j ≥ 1
Correct scorea single cell
Double chance, draw, handicap, goalssums of the same cells, grouped differently

Because they are all the same object viewed from different angles, our over/under number can never disagree with our correct-score grid, and our double chance can never disagree with our 1X2. Sites that price each market separately have no such guarantee. Market by market, with the fixtures behind each: prediction types.

5. De-vigging the price, and the blend

First the three selections are reduced to one price each: the median across every bookmaker quoting the full three-way market on that fixture. Those three prices imply probabilities that sum to more than one — the excess is the margin — and removing it is a normalisation:

qk = (1 / pricek) / Σj (1 / pricej)

and the published 1X2 probability is a fixed-weight mixture of that and ours:

final = 0.25 · model + 0.75 · market

Where no price is recorded, final is the model probability unchanged, and the prediction is marked as model-only. This step touches 1X2 and nothing else — over/under, both teams to score and correct score are stored with no market component at all, so the number you read on those is the raw output of the grid above.

The gap between the two inputs is what the site calls an edge: model − market, in percentage points, on the same selection. It is a claim about the price being wrong, not about the result being certain. The fixtures where that gap is largest are listed on value bets.

How well any of this works

Equations are cheap. The measurement is on the accuracy page: the hold-out season, the baseline comparison, the calibration error per league — including the league that missed our own tolerance.