Projects
Built alone.
Shipped in public.
Every one has a live link, a public repository, and a written record of what failed. The numbers here are reproducible — the code that produced them is one click away.
Player Scout
Python · Principal component analysis · GitHub Actions
Name a player you already have a view on and it finds who else plays the same way, then tells you where the two differ — or start from a need instead of a name. It now spans three sports: football across the big five leagues, men's and women's T20 cricket, and the Pro Kabaddi League. The figures below are the football pool.
The decision I'd defend
Pressing intensity is one number describing eleven people. Attach it to an individual and everyone at the same club starts to look alike. It's shown as context on the profile and kept out of the similarity model entirely — and the panel says so, rather than letting you assume otherwise.
What it cannot see, stated on the page itself
Goalkeeper rankings57th of 160 against a chance of 80 — close to random, so they aren't ranked at allDefensive actionsNo tackles, interceptions or duels in the data, so defenders are compared going forward onlyKeeping 80% of varianceCost real accuracy; right wingers went from 23rd to 34th. The threshold was set by testing, not convention
Every player is split into his early and later seasons and handed to the tool as two strangers. If it can't recognise a man as his own closest match, it can't recognise anything.
Premier League
Supercomputer
Python · Dixon-Coles · Monte Carlo · GitHub Actions
Predicts the Premier League table, refits itself after every gameweek, and grades its own past predictions against the closing market. Nobody presses anything, and it costs nothing to run.
Switched off, and still in the repository
Market odds blendMarginal error gain, worse ranking and calibrationManager effects, all changesWorse on every metricForeign-league manager valuesPlayer conversion already weak at n≈200Fixture congestionEffect reverses sign between eras, fails out-of-sample
Version one penalised Liverpool for hiring Slot, who then won the league — it couldn't tell "worse manager" from "manager we have never seen". It ships only where both managers have a Premier League record. Congestion was the one I most wanted to be true: two datasets, seven seasons, net effect +0.00002.
Cricket AI Digest
Node · ESPN Cricket API · Claude · WebSocket
One live cricket feed in, two briefings out — the facts behind the moment for an analyst, why it matters for a fan. Both generated from the same raw match state.
What I got wrong
Two traps in ESPN's schema, both caught by reading the raw payload rather than trusting the field names. A team that hasn't batted can appear as 0/0 (73 ov), because bowling overs are mirrored into the wrong side's linescore. A digest built from all-null fields still looks like a success in the logs — so a debug endpoint returns the exact object handed to the model.
Hosted on a free tier that sleeps when idle, so the first load can take a minute to wake, and digests appear once a live match updates.
FPL Auto Manager
Python · AWS Lambda · Linear programming · Gemini
Rates every player, plans transfers, picks the eleven and the captain, submits them, then publishes its projection before kickoff so it can be marked against a human. Unattended, every week.
onlyWhat the language model may do to a projectionA wrong call costs one player, not the team
The mistake that rebuilt the model
The first version ranked players on FPL's own projection field. Before a season it's a placeholder — about 24 distinct values across 570 players — so a backup goalkeeper and the best striker scored identically. It sold three of the squad's best players and bought a forward who had started three games all season.
The code ran perfectly. The thinking was wrong.
The short version
Every claim on this page has code behind it.
The detail lives in the repositories, where the backtest scripts sit next to the results and the rejected versions are still in the history. That's deliberate — a number you can't reproduce isn't evidence.