Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Soccer Match Predictor

Machine-learning starter project for predicting football match outcomes: home win, draw, or away win.

The model is designed for modern football features such as xG, xA, PPDA, deep completions, progressive passes, shots, possession, market odds, and rolling team form. It is leakage-safe: for each match it only uses team statistics from matches before that date.

Best Resources

Free/Open Data

  • StatsBomb Open Data: event-level JSON with xG, lineups, freeze frames, and some 360 data. Great for learning and feature engineering, but limited competition coverage and not ideal for current top-five-league weekly predictions.
  • soccerdata docs: Python package that wraps sources such as ClubElo, FBref, Understat, Football-Data.co.uk, Sofascore, and WhoScored. Useful as a research layer, but source availability can change.
  • Football-Data.co.uk: free historical results, basic match stats, and betting odds CSVs. Good baseline data, but it does not provide xG/xA/PPDA.
  • Understat Python package: async access to Understat-style xG/xA data. Good for top European leagues, but scraping-dependent and should be used respectfully.
  • StatsBomb shot dataset on Hugging Face: shot-level xG training set extracted from StatsBomb Open Data. Useful if you want to build your own xG model.

Paid or Semi-Commercial Sources

  • TheStatsAPI: historical xG, npxG, xA and other underlying metrics. A practical option if you want reliable current-season predictions.
  • Opta, StatsBomb paid API, Wyscout, SkillCorner, Sportmonks, API-Football, FootyStats: worth comparing if you need live fixtures, injuries, lineups, odds, and consistent PPDA/pressure data.

Research To Read

Data Schema

The training code accepts a CSV with at least:

date,home_team,away_team,home_goals,away_goals

Optional modern features are automatically used when present:

home_xg,away_xg
home_xa,away_xa
home_ppda,away_ppda
home_deep_completions,away_deep_completions
home_progressive_passes,away_progressive_passes
home_passes_into_box,away_passes_into_box
home_shots,away_shots
home_shots_on_target,away_shots_on_target
home_possession,away_possession
odds_home,odds_draw,odds_away

Lower PPDA usually means stronger pressing, so inspect PPDA feature importance carefully. Tree models can learn the direction, but linear models may benefit from explicit transforms later.

Quick Start

Create an environment:

python -m venv .venv
source .venv/bin/activate
pip install -e .

Fetch baseline EPL results, shots, shots on target, and odds from Football-Data.co.uk:

python scripts/fetch_football_data.py --league EPL --start-season 2017 --end-season 2025

Train:

python scripts/train.py --data data/raw/football_data.csv

Predict upcoming fixtures:

python scripts/predict.py \
  --history data/raw/football_data.csv \
  --fixtures examples/upcoming_fixtures.csv \
  --model-path models/outcome_model.joblib

Recommended Build Path

  1. Start with Football-Data.co.uk plus odds to verify the pipeline works.
  2. Add Understat match xG/xA by date/team for the top-five leagues.
  3. Add ClubElo ratings, rest days, travel distance, manager changes, injuries, suspensions, and lineup strength.
  4. Add PPDA/deep/progressive metrics from a reliable provider. If you cannot get PPDA, use proxy features: opponent passes allowed, defensive third actions, high turnovers, field tilt, or possession-adjusted defensive actions.
  5. Compare models by log loss and calibration, not only accuracy. Football has many draws and noisy low-scoring outcomes, so calibrated probabilities matter.
  6. Keep a strict chronological validation split. Random train/test splits leak future team strength into the past.

Model Notes

The starter model uses:

  • rolling team form features over the previous 8 matches
  • home-vs-away differences for each rolling metric
  • attack-vs-defense matchup edges, for example home rolling xG for minus away rolling xG against
  • an ensemble of logistic regression, random forest, and gradient boosting
  • probability calibration
  • time-series cross-validation

The most important comparisons are matchup features:

home_xg_edge = home rolling xG for - away rolling xG against
away_xg_edge = away rolling xG for - home rolling xG against
net_xg_edge = home_xg_edge - away_xg_edge

home_xa_edge = home rolling xA for - away rolling xA against
away_xa_edge = away rolling xA for - home rolling xA against
net_xa_edge = home_xa_edge - away_xa_edge

For PPDA, lower is better, so the comparison is inverted:

home_pressing_edge = away rolling PPDA - home rolling PPDA

For a stronger next version, add LightGBM or XGBoost, tune with Optuna, and evaluate against bookmaker implied probabilities.

See advanced_feature_schema.md for the full professional-style metric list supported by the feature builder.

Current Best Historical Result

On the real StatsBomb Premier League 2015/16 xG/xA match dataset:

Model: selected_logistic
Rolling window: 4 matches
Selected features: 35
Training rows: 350
Available features: 82
Time-series CV accuracy: 48.6%
Time-series CV log loss: 1.067

Reproduce with:

PYTHONPATH=src /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 scripts/train_model.py \
  --data data/raw/statsbomb_xg_xa_matches.csv \
  --model-out models/pro_feature_selected_logistic_model.joblib \
  --rolling-window 4 \
  --min-periods 3 \
  --model-type selected_logistic

Benchmark files:

data/processed/pre_match_benchmark.csv
data/processed/feature_selection_benchmark_tuned.csv

Rolling Next-Match Predictor

Use this when you want the system to keep taking recent completed league matches, train on everything before the fixture, and predict the next match.

Example: train from recent Premier League results and predict Arsenal vs Crystal Palace:

PYTHONPATH=src /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 scripts/predict_next_match.py \
  --league EPL \
  --seasons-back 6 \
  --home Arsenal \
  --away "Crystal Palace" \
  --rolling-window 4 \
  --min-periods 3 \
  --model-type selected_logistic

If --date is omitted, the script predicts for the day after the latest completed match in the fetched history. To predict a specific fixture date:

PYTHONPATH=src /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 scripts/predict_next_match.py \
  --home Arsenal \
  --away "Crystal Palace" \
  --date 2026-08-15

For multiple upcoming matches, pass a CSV:

date,home_team,away_team
2026-08-15,Arsenal,Crystal Palace
2026-08-16,Liverpool,Chelsea
PYTHONPATH=src /Library/Frameworks/Python.framework/Versions/3.11/bin/python3 scripts/predict_next_match.py \
  --fixtures fixtures.csv

About

Machine learning soccer match outcome predictor with xG/xA and advanced football analytics features.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages