Full disclosure up front: the project lives at racestrategist.com as an F1 Fantasy analytics tool. This post is about how it works under the skin, not a pitch — mods, if the link crosses any line, feel free to strip it and I'll keep the thread purely technical.
What it produces
For every race weekend: per-driver expected fantasy points (xPts), average finishing position, DNF probability and variance — plus derived outputs like value-per-cost rankings, price-change forecasts, and optimal team combinations under the game's budget cap (which is a fun little constrained-optimisation problem in itself).
The simulation core
The heart is a Monte Carlo race simulator. Each run samples:
- a pace distribution per driver, built from recent form (weighted recency — last 3 races vs season average, with trend detection) and team baseline;
- incident/DNF probabilities per driver and per circuit;
- track characteristics as parameters: overtaking difficulty, power vs aero sensitivity, tyre degradation, street vs permanent circuit;
- the weather forecast for the session, which shifts both variance and individual driver distributions (some drivers are measurably different in the wet).
The ML layer
On top of the raw sim sit gradient-boosted models (XGBoost) trained on historical race + fantasy data. The final prediction is a blend of sources: raw simulation, and two generations of the ML model. The interesting engineering finding: the blended prediction diverges significantly from raw sim xPts. The sim is better at capturing variance and tail risk; the ML models are better at central tendency. Neither alone beats the blend in backtesting.
Price modelling
The fantasy game's price changes are driven by transfer behaviour, so I built a separate model for it — forward (given current state, predict tonight's price moves) and inverted (given a driver and a target price, what has to happen for them to get there). The inverse mode turned out to be the more useful one for actual decision-making.
Two problems I'd love this forum's input on
1. Mean vs median. Monte Carlo means get badly skewed by DNF tails. A driver who finishes P8 in 80% of sims but DNFs in 20% shows a "mean finish" of ~P12 — which every user reads as "the model thinks he finishes 12th". Showing median + range is more honest but harder to rank by. How would you present a skewed finishing distribution as a single sortable number?
2. The 2026 reset. New regulations, a new team, new PU suppliers — my entire historical training set became questionable overnight. I started the season on heavy priors with aggressive decay of pre-2026 data, and I'm now blending in actual 2026 results with recency weighting. It worked better than expected — happy to share the prediction-error curve across the first races if there's interest. Curious how people here would have handled the cold start, especially with the new active-aero overtaking dynamics making old track-difficulty parameters unreliable.
Stack, briefly, for those who care: Next.js/TypeScript front end, Postgres, data and model pipelines orchestrated with n8n on a small VPS. Nothing exotic — the interesting parts are all in the modelling.
If anyone wants to poke at the predictions over a race weekend and tell me where they're wrong, that's honestly the most useful feedback there is. Happy to go deeper on any section above.
