Football Player Market Value Prediction
Final-year major project (2025–2026)
Architecture
Case Study
The problem
A footballer's market value is the most argued-over number in sport, yet the reference values on Transfermarkt are crowd-sourced opinions rather than measurements. This project asks how well market value can be predicted from observable data — and which signals actually drive it.
Data
Three sources are fused into one player-level dataset covering the top five European leagues:
- Transfermarkt — market valuations (the target) plus contract context
- FBref — detailed on-pitch performance statistics, collected with a custom Playwright-based Python scraper
- EA Sports FC (SoFIFA) — expert-assessed attributes such as overall rating and potential
The hardest engineering problems were entity resolution across inconsistent player names, temporal alignment so valuations never see future performance data, and per-90 normalisation of performance statistics.
Method
The final model is a hybrid stacked ensemble: base learners trained on different feature views (performance statistics vs. expert attributes, including CatBoost for categorical handling), whose predictions — and their agreement statistics (mean, median, min, max) — feed a meta-learner alongside high-value raw features. The target is log-transformed to tame its heavy skew.
Results
| Metric | Value |
|---|---|
| R² | 0.958 |
| MAE | ≈ €0.96M |
| RMSE | ≈ €2.22M |
| Predictions within ±20% | 56.5% |
SHAP analysis shows scouting consensus (EA-derived predictions, potential, overall rating) and wage context dominate, with an engineered age-curve feature (abs_years_from_27) capturing peak-value dynamics. Raw per-90 performance statistics rank below all of these — the market prices judgement, not last season's numbers.
Reflections
The residual error concentrates exactly where the market argues most: young high-potential players and superstars. A follow-up direction is modelling valuation uncertainty rather than point estimates, flagging the players whose value the data genuinely cannot pin down.
A three-part write-up of this project is on the blog, starting with part 1: building the dataset.