How soccer match win probabilities are calculated - Roni Kobrosly's Personal Site
Roni Kobrosly Ph.D.'s Website
written by
Roni Kobrosly
on<br>2026-07-23
| tags:
engineering
statistics
human data
The GitHub repo for this implementation can found here
Yeah I know the World Cup is already over, I was a little late to writing this up.
I'm not sure what it is about soccer, but I tend to see wayyyy more match win probabilities for it then for other sports. I don't mean on sports betting sites with odds, I mean everywhere: Google, the NYTimes, Nate Silver's blog (yes he's still alive), even dusty, old Yahoo. What is it about soccer specifically? Sincerely don't know 🤷🏻♂️
Many of these predictions are updated minute-by-minute, where the "Win" probability ticks up or down with each goal, and slowly converge towards 0% or 100% as the game ends. I wanted to investigate this. Robberecht et al.'s 2021 paper "A Bayesian Approach to In-Game Win Probability in Soccer" is a good starting point. It seems like many of these models are slight variations on the following approach...
1. Chop the Game Into 100 Equal-Paced Frames<br>The first problem is that time in soccer isn't uniform. Two matches that both read "30:00" on the clock have had different fractions of the actual game elapse, because stoppage time varies from match to match. Robberecht handles this by splitting every match into T = 100 frames , each covering a fixed fraction of the game's real duration, meaning wall-clock time including stoppage. Each half is divided into 50 frames of equal real length, so halftime is always exactly frame 50, whether the first half ran 45:00 or 47:30. I didn't fully get that detail for a bit.
For each frame we compute a set of game-state features for each team. The prediction target for a team at frame t is deliberately not "who wins." It is: how many more goals will this team score between now and the end of regulation?
If you get the above, the rest is easier to wrap your head around.
2. Model Future Goals as Independent Poissons<br>A Poisson distribution is the standard way to model "how many times will a random event happen in a fixed window?" In this case, how many goals a team scores in the time that's left.
At frame t, we model each team's remaining goals like this:
y_{>t, \text{home}} \sim \text{Poisson}\big((T - t) \cdot \theta_{t, \text{home}}\big)<br>y_{>t, \text{away}} \sim \text{Poisson}\big((T - t) \cdot \theta_{t, \text{away}}\big)<br>θ is a per-frame scoring intensity , roughly the team's goal rate right now. (T − t) is the number of frames still to play. Multiply them and you get the expected number of goals the team has left in it: rate × time remaining.
Robberecht treats the two teams' goal counts as independent. And yeah that's a pretty big simplification.
3. Game State Drives Scoring Intensity via the Logit<br>Where does that scoring intensity θ come from? It's computed from the current game state. Since θ has to stay between 0 and 1, so Robberecht passes the game state through a logistic function , which squashes any number into that range:
\begin{aligned}<br>\theta{t,\,\text{home}} &= \operatorname{invlogit}\big(a_t \cdot x_{t,\,\text{home}} + \beta + Ha\big)<br>\end{aligned}
\begin{aligned}<br>\theta_{t,\,\text{away}} &= \operatorname{invlogit}\big(a_t \cdot x_{t,\,\text{away}} + \beta\big)<br>\end{aligned}
Reading the pieces:
x_t is the feature vector describing the game from one team's point of view (section 4).
a_t is the matching vector of weights (how much each feature counts).
β is the intercept, and Ha is a home-advantage term added only for the home team.
In the trained model β ≈ −4.49. On its own that sets a baseline intensity of invlogit(−4.49) ≈ 0.011 goals per frame, about one goal per team over a full match before any features come into play.
The important part is that the weights a_t carry a t subscript. They're allowed to change as the game goes on, which is how the model can learn that a feature matters more in the 85th minute than in the 10th.
4. Ten Features Describe the Game State (Per Team)<br>Every feature is computed at every frame:
Feature<br>What it captures
game_time<br>Time step t/T
score_diff<br>Current goal differential
rating_diff<br>Elo rating difference between the teams
team_goals<br>Goals this team has scored so far
reds<br>Red-card differential
yellows<br>Opponent's accumulated yellow cards
opportunities<br>Goal-scoring chances per frame
attacking_passes<br>10-frame rolling mean of completed passes into the final third
xt<br>12-frame rolling mean of positive expected-threat (xT) gains from passes and carries
10<br>duel_strength<br>10-frame rolling win rate in ground and aerial duels
Features 7-10 need detailed event-stream data (locations, pass outcomes, duel results). That's tricky to get in real time. So the paper also defines a live variant model that keeps only features 1-6. THAT'S THE MODEL THAT WE TEND TO SEE ONLINE.
5. The Coefficients Follow a Gaussian Random Walk<br>This is the core of the...