June 2026. A research write-up, not betting advice.
I wanted to know whether a simple soccer model could actually make money. So I built one, put it on the internet so you can poke at it, and then spent three weeks trying to prove it had an edge. It doesn’t. I’m writing it up anyway, because most posts like this go quiet the moment the numbers stop cooperating, and the way this one fails is more interesting than another win would have been.

Try the live version → Pick any two nations and see the model’s odds, or play out the rest of the 2026 World Cup twenty thousand times. The predictor runs the actual model in your browser.
The test that actually matters
Most soccer models get graded on whether they pick winners. That’s the wrong bar. A model can pick winners every weekend and still go broke, because the bookmaker’s price already knows who’s likely to win — and then charges you a fee on top.
The only test I cared about was whether the model could beat the closing line: the last price before kickoff. By then the price has soaked up every public model and all the sharp money, so it’s about as good as a number gets. Beat it and you have an edge. Fail to beat it and you bleed out at roughly the rate of the bookmaker’s cut, however many games you call right.
I wrote the pass/fail criteria down before I ran anything and committed them to git, so I couldn’t move the goalposts once the results came in. The headline metric is closing line value, or CLV: did the price drift toward my bet after I placed it? CLV beats raw profit here because it doesn’t hang on whether one match happened to bounce my way.
The model
A plain Elo system, nothing clever. Every team is a single number that ticks up or down after each game. No player data, no injuries, no xG — just who beat whom, and where. A small adjustment handles draws. It only ever sees matches in date order, so it can’t peek at a result before it predicts it.
That was the whole point: find out how far an honest, boring model gets.
What I ran it against
First, the five big European leagues: about 24,000 matches with real Pinnacle closing odds from football-data.co.uk. When the World Cup came around I rebuilt the same thing for national teams off martj42/international_results, which has every international going back to 1872, and checked it against live prices on Kalshi and Polymarket.
What happened
The clubs went first, and they went badly. Over 4,099 bets the closing line value came in at −3.4%, with the entire confidence interval below zero. ROI was −9.8%. The model didn’t blow up; it lost slowly and steadily, almost exactly the size of the bookmaker’s margin. That’s what losing to an efficient market looks like — not a crash, a slow leak.
So I checked how good the market actually is, directly. Bucket every game by the price the market gave it, then look at what really happened. Across 73,000 predictions the prices tracked reality to within about half a percent: when the market says 30%, it comes in around 30% of the time. There is one known wrinkle — the favourite-longshot bias, where heavy favourites are priced a touch too long — but even that gets eaten by the margin. Bet the favourites at the close and you land near break-even, not profit.
The World Cup was the same lesson in a tuxedo. On historical knockout games the model called about 69% correctly and was well calibrated — a perfectly respectable forecaster. Measured against Kalshi’s prices, though, its CLV was indistinguishable from zero. For a giddy moment the profit-and-loss looked incredible, +51%, until I realised it was noise from a tiny sample: betting at random “won” over those same games too.
The twelve disagreements
Then the part I didn’t see coming. I took the twelve bets where my model disagreed most violently with the market and had a set of agents research each one, expecting to find at least a couple of spots where the model had spotted something the crowd missed. Every single disagreement turned out to be the model being wrong. Sometimes it was a known flaw — the model overrates teams from weaker confederations. More often the market simply knew something the model couldn’t: a star out injured (Japan’s Mitoma), a side already through and resting players (Mexico), two red cards carried over from the last game (Qatar), even Iran’s awkward travel-and-recovery situation at the tournament. Twelve times I went looking for the market’s mistake, and twelve times I found my own.
What it means
You can’t out-guess an efficient market with a simple model, because the price is already the best guess in the room. Bet against it and you’re usually the one who’s wrong. Bet with it and you just pay the fee. Both roads end at negative expected value.
If there’s an edge anywhere, it isn’t at the World Cup — one of the most heavily traded events in all of sport. It’s in the small, ignored markets, and only with information the price doesn’t already hold. For soccer that mostly means player-level data, gathered carefully enough that you’re not just fooling yourself with hindsight. I haven’t done that yet. It’s the obvious next move, and it would have to clear the exact same bar: positive CLV on data the model never saw.
And, for what it’s worth
Make the model play out the rest of the 2026 tournament 20,000 times and it likes Argentina (around 22%), then Spain and France. The favourite is still only a one-in-five shot — and it’s a pick I’ve just spent a whole page telling you not to bet on.
Model: plain Elo, draws handled, strictly date-ordered so it can’t peek ahead. Data: ~24,000 club matches with Pinnacle closing odds, every international since 1872, live prices from Kalshi and Polymarket. Method: success criteria pre-registered in git, CLV as the headline metric, 73,000 calibration checks, agent research on the twelve biggest disagreements. The predictor on the live site runs the actual model in your browser.