This is not a tip sheet.
It is a distribution,
written down first.
gridcast publishes a probability for every driver in every finishing position, before every Formula 1 race, and then scores itself in public. The commit history is the timestamp. Nothing here was written after the fact.
Two models run against each other all season. Neither is a favourite, both publish every weekend, and the one that loses stays on the page. The whole point is that the misses are as visible as the hits, which is the part of a forecasting project that is hard to fake.
Everything below is how it actually works, including what it cannot do.
Two models. No favourite.
The direct model
Plackett-Luce. Every driver gets a strength, a linear function of their features: grid position and season pace history. The probability of winning is the softmax over those strengths. Remove the winner, renormalise over who is left, and draw again for second, and so on down to twentieth.
That structure is the reason the output is coherent without any patching. A race is a ranking, so the win probabilities sum to one, and exactly one driver occupies each position. The weights are fit by maximum likelihood on the finishing orders actually observed, in plain numpy and scipy. Ordinal regression was the fallback. A boosted tree was rejected: it would overfit two hundred and forty outcome rows and nobody could read the result.
The simulator
Ten thousand runs of the race, one lap at a time. Each run draws a form deviation per driver, then plays the race out: tyre degradation, fuel burn, a pit strategy chosen by search, safety cars, virtual safety cars, red flags and retirements. Counting how often each driver finishes in each position gives the same matrix the direct model emits, from a completely different direction.
The two are not variations on one model. They fail differently, which is the point of running both.
- Direct, Saturday call
- 0.1340mean RPS, walk-forward over rounds 4 to 12
- Simulator, Saturday call
- 0.1220same races, same protocol
- Grid-order baseline
- 0.1655finish order equals grid order
- Real-strategy replay
- 0.1211control: the sim handed the strategies teams actually used
Two rules decide
what data counts.
Almost every modelling decision here follows from one question asked before any historical data is used: is this a property of the car, or a property of the track?
Car properties do not transfer
Pace, degradation and power-unit reliability are fit on 2026 data only. The 2026 regulation change means those older cars no longer exist in any useful sense, so their numbers are worthless here, however tempting the extra sample size is.
Track properties do transfer
Safety car rates, first-lap incident rates and how much grid position persists are functions of barriers, run-off and layout, so they use 2018 to 2026 history. That is what rescues the model from having exactly one observation per circuit in the current regulations.
Inside the sim,
one lap at a time.
Pace, per driver, never per team
Each driver carries their own pace term and their own degradation slope per dry compound; intermediates and wets are pooled because there are too few laps to do otherwise. There is no team term. Once every driver has a parameter a team term is collinear with them and buys nothing, and worse, it erases the thing the strategy search consumes: teammates diverge most in degradation, because setup and driving style are theirs alone.
Those parameters are fit on lap rows, roughly eleven thousand of them, not on race outcomes. Per-driver parameters are cheap at that scale.
The cleaning matters more than the model
Raw lap times are contaminated. In-laps, out-laps, the first two racing laps and every lap under a non-green track status come out before a single parameter is fit, and what is left is corrected for fuel load, compound and tyre age. A pace model fit on uncleaned laps is measuring traffic and safety cars, not cars.
Strategy is a search, not a rule
Every stint plan is chosen by enumerating one, two and three-stop plans over candidate pit laps and taking the minimum total race time. A threshold rule like "pit when the tyre is this old" is what produces a hundred stops in a race, because it never compares the roughly twenty-three second cost of a green-flag stop against what the stop is supposed to gain.
The plan is re-solved whenever race control changes the sums. Under a safety car the pit loss falls to about twelve seconds, and that flips the answer, which is why real races reshuffle behind one. Each car solves separately, or all twenty stop on the same lap and the Monte Carlo loses the variance that is the entire output.
Events are sampled, not assumed
Safety car rates come from each circuit's own 2018 to 2026 history, shrunk toward a street or permanent base rate so that one dramatic year does not dominate. Occurrence, lap and duration are all sampled, and virtual safety cars are modelled separately because they cost a different amount of time. Red flags get one pooled rate across all circuits: sixteen in ninety-eight races is not enough to estimate per circuit, and pretending otherwise would be fitting noise. A red flag gives a free tyre change, closes the gaps to zero and re-runs the first-lap incident draw at the standing restart.
Retirements are split by who owns them. A per-team per-lap hazard covers mechanical and mid-race collision, since 2026 timing data reports both as "Retired". First-lap incidents are pooled by circuit only: the rate turned out flat across grid quartiles, between 2.4 and 3.0 percent, so a grid-position term would have been invented rather than measured.
The one honest compromise
Cars do not interact. There is no dirty air, no DRS, no blocking. In place of an overtaking model there is a single calibration term: each grid slot costs a car a fixed amount of lap time over the race, re-issued by running order whenever a safety car bunches the field. Its job is narrow, to cancel the strategy search's optimism about undercuts, and it is marked in the code with its own ceiling. A rank blend was tried first and thrown out: it capped a fast car at its starting rank, which is the opposite of what a race does.
Ranked probability score.
Not accuracy.
Finishing position is ordered, so a miss has a size. Predicting P4 for a driver who finished twelfth should hurt more than predicting P4 for a driver who finished fifth. Brier score cannot tell those two apart. RPS can, because it scores the cumulative distribution rather than the individual cells.
Validation is walk-forward: fit on rounds one to k, predict round k+1, never the reverse. Scores are computed on driver-race rows, about two hundred and forty of them, not on races, because twelve numbers cannot separate two models. Every score is computed in Python from the committed JSON and printed next to the grid-order baseline, which is the honest floor: if a model cannot beat "everyone finishes where they started", it has produced nothing.
Two published numbers in this field are worth naming as anti-examples. An R-squared of 0.99 predicting finishing position means post-race features leaked into the inputs. And "78 percent accuracy predicting podiums" is worse than saying nobody gets a podium, which scores 85 percent. Accuracy on rare events is not a measurement.
Two calls
a weekend.
The Thursday call runs on each driver's season-average grid position, standing in for a predicted grid until a real qualifying model exists. One-lap pace is not race pace, and track evolution across a session is real, so that model is being built rather than guessed at.
The Saturday call runs on the grid that actually came out of qualifying. The race model always takes the grid as an input and never assumes it.
The Thursday model is fit as its own model on its own feature. Feeding a guessed grid into a model trained on known grids would be the subtle version of the leakage above: it learned the weight of a fact, so handing it a guess makes it overconfident by exactly that weight. The simulator does the same thing its own way, by drawing each driver's grid slot from their season scatter instead of a single average.
What it
cannot do.
The backtest is nine races. Two constants in the simulator, the track-position term and the pooled form scatter, were tuned on those same nine, so the backtest number flatters itself. This is why the live season is the real evidence and the backtest is only a smoke test.
Monaco is the race the simulator loses, which is exactly what a model with no overtaking should do at the circuit where overtaking does not happen.
Setup is unobservable. No wing level, differential, brake bias or ride height data exists publicly, and the effect is real. That is irreducible noise, so it widens the intervals rather than adding a parameter that would fit it spuriously.
And there is no qualifying model yet, no bookmaker baseline yet, and no pairwise overtaking model. Each is a known gap with a known cost, not something the page is quiet about.
Not a product.
Not a package.
Not a bet.
A personal pipeline in plain numpy and scipy, MIT licensed, nothing forked. The scorecard exists so the record is public whichever way it goes.
Start with the current probabilities, or read the record.