Real F1 telemetry. Real tyre physics. Real strategy decisions.
A full-stack race strategy simulator built on FastF1 data, polynomial degradation models, and vectorised Monte Carlo simulation — with a live Streamlit dashboard inspired by F1 broadcast telemetry design.
F1 race strategy is one of the most complex real-time optimisation problems in sport. Every pit stop decision involves noisy tyre data, uncertain rival behaviour, probabilistic lap time models, and thousands of possible strategy combinations.
This project builds a full strategy decision-support system from the data up:
- Loads real race data via FastF1 (lap times, sector data, tyre compounds, pit stops)
- Cleans and engineers features — removes SC laps, inaccurate laps, fuel-corrects lap times
- Fits a per-compound polynomial degradation model (sklearn Pipeline) to quantify tyre wear
- Generates all valid 1-stop and 2-stop strategies under F1 compound rules
- Runs vectorised Monte Carlo simulation — 252 strategies × 1,000 iterations in ~10 seconds
- Models rival undercut/overcut windows probabilistically
- Visualises everything in a production-grade dark-mode dashboard
┌─ F1 STRATEGY AI ────────────────────────────────────────────────────────┐
│ Strategy Comparison │ Undercut Window │ Tyre Model │ Full Results │ Rival │
├──────────────────────────────────────────────────────────────────────────┤
│ OPTIMAL STRATEGY MEAN RACE TIME UNCERTAINTY WIN RATE │
│ SOFT(20) → HARD(37) 5,847.3s ± 8.4s 34.2% │
├──────────────────────────────────────────────────────────────────────────┤
│ [Strategy comparison bar chart] [Top 5 leaderboard] │
│ [Lap-by-lap trace — top 3 strategies] │
└──────────────────────────────────────────────────────────────────────────┘
Full terminal-style dark UI with F1 official fonts (Antonio, Titillium Web), all 20 driver profiles with real team colours, animated F1 car silhouette, and live background image injection.
f1-strategy-ai/
├── src/
│ ├── pipeline/
│ │ ├── loader.py # FastF1 session loading + caching
│ │ ├── cleaner.py # SC laps, inaccurate laps, outlier removal
│ │ └── features.py # Fuel correction, tyre age, stint deltas
│ ├── models/
│ │ ├── tyre_deg.py # Polynomial degradation model (sklearn)
│ │ └── pit_loss.py # Team-specific pit stop time loss
│ ├── simulator/
│ │ ├── strategy.py # Strategy / Stint dataclasses + generation
│ │ ├── race_sim.py # Deterministic race simulation
│ │ ├── monte_carlo.py # Vectorised MC engine (NumPy matrix ops)
│ │ └── rival.py # Undercut / overcut EV scanning
│ ├── visualisation/
│ │ ├── dashboard.py # Streamlit app (main entry point)
│ │ ├── tyre_plots.py # Degradation curves, stint scatter
│ │ ├── strategy_plots.py # Comparison bars, lap traces
│ │ └── rival_plots.py # Undercut EV timeline, H2H MC
│ └── utils/
│ ├── config.py # Central config (race, drivers, params)
│ └── logger.py # Structured logging
├── tests/
│ ├── test_pipeline.py
│ ├── test_tyre_deg.py
│ ├── test_simulator.py
│ └── test_rival.py
├── docs/
│ ├── architecture.md
│ ├── model_notes.md
│ └── improvements.md
└── data/cache/ # FastF1 HTTP cache (auto-populated)
The original implementation called simulate_race() 252,000 times in a Python loop. The rewrite pre-computes deterministic lap time vectors per strategy, then generates noise as a (n_laps × n_simulations) NumPy matrix — eliminating 14 million Python function calls.
# Per strategy: one matrix op instead of 1000 Python calls
base = build_lap_time_vector(strategy, deg_model, pit_loss) # (57,)
noise = rng.normal(0, noise_std, size=(57, 1000)) # (57, 1000)
times = (base[:, None] + noise).sum(axis=0) # (1000,)Result: 252 strategies × 1,000 iterations in ~10 seconds on a laptop CPU.
Per-compound polynomial regression (degree 2) fit on fuel-corrected lap times vs. tyre age. Baseline subtracted for relative degradation rate. Outliers removed via IQR per compound group.
SOFT: MAE = 0.73s | baseline = 95.22s
HARD: MAE = 0.61s | baseline = 94.71s
The model rejects MEDIUM if fewer than a minimum sample threshold exists — common in Bahrain 2024 where no drivers used MEDIUM in clean conditions.
The rival.py module scans an undercut window lap-by-lap, computing the expected value (EV) of pitting on each lap relative to a specific rival's current tyre state and track gap:
EV(lap_n) = rival_total_remaining(lap_n) − our_total_remaining(lap_n)
Win rate is computed as the fraction of Monte Carlo simulations where EV > 0, giving a probabilistic confidence interval over the decision.
- Python 3.11+
- ~500MB disk (FastF1 cache)
git clone https://github.com/Trixx4191/F1-Strategy-AI.git
cd f1-strategy-ai
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtPYTHONPATH=$(pwd) streamlit run src/visualisation/dashboard.pyOpen http://localhost:8501 — select a race, driver, and strategy type in the sidebar, then hit RUN SIMULATION.
First run: FastF1 will download session data (~50-200MB depending on race). Subsequent runs use the local cache.
pytest tests/ -vAll parameters are in src/utils/config.py:
YEAR = 2024
RACE = "Bahrain" # Any FastF1 event name
PRIMARY_DRIVER = "VER"
N_SIMULATIONS = 1000 # MC iterations (100–2000 via dashboard slider)
LAP_TIME_NOISE_STD = 0.3 # Gaussian noise per lap (seconds)
RACE_LAPS = 57
PIT_LOSS = { # Team-calibrated pit stop loss
"RBR": 21.5,
"MCL": 22.1,
"DEFAULT": 22.5,
}Supported races: any event in the FastF1 database (2018–2024).
| Package | Version | Purpose |
|---|---|---|
fastf1 |
≥ 3.3 | Official F1 timing & telemetry data |
pandas |
≥ 2.0 | Lap data manipulation |
numpy |
≥ 1.26 | Vectorised Monte Carlo |
scikit-learn |
≥ 1.4 | Polynomial degradation model |
streamlit |
≥ 1.32 | Interactive dashboard |
matplotlib |
≥ 3.8 | Strategy & tyre visualisations |
scipy |
≥ 1.12 | Statistical distributions |
- Tyre cliff model — detect and model the abrupt degradation cliff using changepoint detection
- Weather probability layer — integrate forecast data to weight wet/intermediate strategy branches
- Multi-driver undercut scanning — simultaneous rival modelling for multi-car battles
- Qualifying pace delta — normalise lap times to qualifying reference for cross-team comparison
- Deploy to Streamlit Cloud — live public demo with 2024 season data
- Backtest mode — compare AI-recommended strategy against actual team decisions race-by-race
Strategy is where championships are won and lost. The 2023 Las Vegas GP, the 2021 Abu Dhabi finale, Leclerc's Monaco 2022 undercut — these moments hinge on decisions made in seconds with imperfect information.
This project exists to explore whether a well-engineered data pipeline and probabilistic simulation engine can replicate the kind of reasoning that happens on an F1 pit wall — and make it open, inspectable, and reproducible.
MIT — see LICENSE.
Built by a data engineer and F1 enthusiast. If you work in motorsport data, simulation, or performance engineering — let's talk.