I Built a Football Data Analysis Pipeline From 220,000 Matches
DEV Community

I Built a Football Data Analysis Pipeline From 220,000 Matches

For academic research only. Not betting advice. GitHub: MENG-COOLMAN/PitchQuant - MIT license, free to use. The TL;DR Six months. 227,000 matches. One question: what can you actually learn from football odds? This is not a "prediction oracle." Football is chaotic and markets are efficient - I put that on the front page. But if you've ever stared at a line of odds and wondered if there's a signal hiding in the noise, this post is for you. Why I built this It started with a simple frustration. Two teams both at 1.50. One wins every time. The other doesn't. Why? I started pulling data - de-vigging, ELO ratings, Poisson models. At first it felt like I was onto something. Then reality hit: after a few hundred matches, no single indicator beats the market. De-vigged probabilities land within ±5pp. ELO gaps explain maybe 10% of variance. Standard Poisson is basically guessing on 3:2 vs 2:1. But here's the thing: when you combine weak signals with proper weights and strict backtesting gates, directional accuracy climbs from 48% to around 55%. That number isn't flashy. But in the odds world, every single percentage point is real money. What it looks like It's an LLM-as-runtime architecture. Think of it as a very picky analyst who refuses to skip steps: - Python scripts do the math - de-vigging, Kelly criterion, Dixon-Coles Poisson, score lookup tables. The LLM doesn't question what the numbers say. - 34 knowledge files tell the LLM what to look for - league quirks, European two-leg adjustments, halftime patterns, water-level traps. Every rule comes from backtests, not gut feel. - 238 automated checks catch mistakes. If a rule says one thing and the data says another, it fails loudly. "No number = not done" is a hard rule. For a single match, the pipeline runs like this: Odds input → De-vig probabilities → 12 hardcore checks → Odds movement morphology → Scenario rules → 26 inference checks → League calibration → Home/away factor → Direction → Goals magnitude → Score spectrum → Totals → Self-check The magic isn't the number of steps. It's that each step's output feeds directly into the next. No skipping. No "these two steps just happened to both be in the report." Five things I learned 1. The weak-consensus trap When the market isn't strongly convinced (skew < 150%), the "favorite" wins only 36-43% of the time. That's a coin flip. Betting the favorite here loses over time. The draw is consistently underpriced and must be listed as a co-primary outcome. I learned this the expensive way. 2. Deep odds don't always mean safe Home odds below 1.30 look like a lock. But if the Asian handicap only moves 0.5-0.75 (shallow at low odds), actual home wins drop from 74% to 56%. Low odds + shallow handicap = trap. You can't see this from European odds alone - you need the Asian water level. This one signal alone was worth the entire project. 3. Over 2.5 and exact score are different games Everyone assumes "over 2.5 means big scores." Wrong. When the O2.5 line is low (strong over expectation), the Top-2 score hit rate actually drops. Why? Because high-scoring matches have more dispersed scorelines. 2:1, 3:1, 2:2, 3:2 all go over 2.5. You nail the direction but can't pin the exact score. Took me weeks to accept this trade-off. 4. More machine learning isn't always better I was excited about online ML (River's ADWIN drift detection). It sounded cutting-edge. Backtesting showed only a 0.68pp improvement - and only on matches where the market was already weak. Forcing it onto strong-signal matches just added noise. So I built a gate: strong market signal → learning stays out. Weak signal → learning gets a co-reference slot. Sometimes the best ML model is the one you don't use. 5. Poisson isn't a crystal ball Dixon-Coles correction helps, but it still assumes goals are independent events. In reality, a leading team sits back, the trailing team throws everyone forward. Poisson can't model that. So it's just one engine - you also need to fuse with the actual score odds distribution from the market. The market already did the hard work; your job is to read it, not replace it. The honest numbers Before anyone gets too excited: - Direction accuracy: ~50% baseline, ~55-58% with model optimization - Top-2 score hit rate: ~25% baseline, ~30% on strong-signal matches - These are time-split backtest numbers from 227k matches. They do not predict the future. - Long-term EV on sports lottery is structurally negative. No model changes that. The point isn't "how to win." It's building a data analysis methodology. If you're into quant/data science, these ideas (de-vig calibration, anti-overfitting gates, evidence-bound self-checks) generalize to any prediction project. Open source The project is on GitHub under MIT license: - All core scripts are Python, stdlib-heavy, no heavy frameworks - Distilled JSON lookup tables (backtest artifacts) are included - Raw 227k-match CSV not included (size + source terms) - You need your own API keys (odds-api and api-football both have free tiers) - Calibrated for top-5 European leagues + Champions League / Europa League One last thing: this project is for academic research and technical exchange only. It does not constitute investment advice. Football is round. Data reduces mistakes; it doesn't beat probability. If you found this interesting, star it on GitHub and drop an issue. Happy to discuss architecture, backtesting, or anything in between. A student studying for grad school in financial math. Built this bit by bit in my spare time. All the data, rules, and code are on GitHub. Come build with me. Top comments (0)

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.