The Moment We Realized Odds Weren't Enough
About six months into building our prediction models, we hit a wall. Our accuracy was decent, but we kept seeing matches where our models missed obvious factors that any football fan would consider. A team playing their fourth game in twelve days. A squad missing three key starters. Basic stuff.
The odds captured market sentiment well, but they compressed a lot of context into a single number. We needed to decompress that context and give our models access to the underlying factors.
Why xG Became Our First Non-Odds Feature
Expected Goals (xG) measures shot quality rather than actual goals. A team that generates 2.5 xG but only scores once is creating good chances; they've just been unlucky. Over time, xG tends to predict future goal output better than raw goal counts.
We started tracking rolling xG averages—how many expected goals a team creates and concedes over the last five matches. The home/away split turned out to be important too: some teams generate significantly better chances at home.
The tricky part was getting the timing right. You can only use xG data from matches that have already happened at the point you're making a prediction. It sounds obvious, but this kind of temporal leakage is a common mistake in sports modeling.
Injuries: More Nuanced Than We Expected
Our first attempt at injury features was crude: just count how many players are injured. It didn't help much. A team missing their third-choice goalkeeper and a reserve midfielder is very different from one missing their captain and starting striker.
What worked better:
- Position weighting: Missing a starting goalkeeper or center-forward has more impact than missing a backup winger
- Minutes played: Encoding how many minutes the missing players typically contribute
- Recency: When did the injury become public knowledge? This matters for model integrity
The timing issue was even more critical here. We timestamp our injury data carefully so we only use information that was publicly available before the match.
Schedule Congestion: The Simplest Feature That Works
This was almost embarrassingly simple, but it improved our models noticeably:
- Days since last match
- Matches played in the last 14 days
- Whether the team had a mid-week European fixture
Teams playing their third match in seven days show measurable performance drops, especially in the second half. It's not a huge effect, but it's consistent enough to be useful.
We also experimented with travel distance features for European competitions, but the signal was weaker than we expected. Rest days alone captured most of the congestion effect.
How We Combine Everything
The layered approach that emerged through experimentation:
Layer 1 - Baseline: Odds-derived probabilities provide the market's assessment. These are our starting point.
Layer 2 - Adjustments: xG, injuries, and schedule data can shift probabilities when they suggest the market might be missing something.
Layer 3 - Confidence: Odds movement patterns and bookmaker consensus help us gauge how confident we should be in our predictions.
Each layer adds a small amount of information. None of them are magic—xG alone won't make you a prediction expert. But combined systematically, they give our models a richer view of each match.
What We Learned
- 1Simple features often outperform complex ones if they're implemented correctly
- 2Timing and data hygiene matter as much as the features themselves
- 3Each data source adds incremental value—there's no single "secret signal"
- 4The best features are ones you can explain logically
We're still experimenting with new data sources, but these three—xG, injuries, and schedule—have proven their value consistently across multiple seasons.
*OddsFlow provides AI-powered sports analysis for educational and informational purposes.*

