Tour de France race outcome prediction: rider archetypes, a top-10 model, and Tissot fantasy team picks by exact knapsack.
Project description
wraith-tdf-predictor 🚴💨
Who cracks the top 10 in July? Six seasons of ProCyclingStats results, a from-scratch model, and a Tissot fantasy team picked by exact knapsack — all in pure Python. No torch, no sklearn, no 200 MB wheel for a model with nine weights.
The 2026 hot take so far: Pogačar. Groundbreaking stuff.
What it does
- Scrapes grand tours, one-week stage races, monuments and top classics (men's elite, 2021–2026) into SQLite
- Clusters riders into archetypes with hand-rolled k-means
- Predicts top-10 finishes with class-weighted logistic regression (test AUC ~0.89 — form really is everything in cycling)
- Picks a 10-rider, 120-star Tissot fantasy team via 0/1 knapsack with cardinality, backtested leave-one-year-out against the hindsight-optimal team
- Publishes a static predictions page (
data.json) with per-stage teams, jersey picks, and pred-vs-actual grading during the Tour
Install
pip install wraith-tdf-predictor # or: uv add wraith-tdf-predictor
Runtime dependencies: procyclingstats and cloudscraper. That's it.
Python 3.12+.
Run it
From a clone:
git clone https://git.thomaspeoples.com/thomaspeoples/wraith-tdf-predictor.git
cd wraith-tdf-predictor
uv run poe setup # deps, hooks, .env from template — once
uv run poe ingest # scrape PCS -> data/raw/*.json (polite, cached)
uv run poe db # load -> data/races.db (SQLite)
uv run poe model # archetypes + top-10 model
uv run poe fantasy # backtest + 2026 team pick
uv run poe publish # data.json for the static predictions page
docker compose up -d # browse the DB at :8080
uv run poe update # daily during the Tour: ingest new stage if
# raced, retrain, republish -- no-op otherwise
Outputs land in data/: archetypes.json, model.json, and a
printed team that will absolutely not win your league.
Configuration
Everything configurable lives in .env (copied from
.env.example by poe setup, never committed):
| Variable | Default | Purpose |
|---|---|---|
DATA_DIR |
./data |
Scraped data, SQLite DB, model outputs |
WWW_DIR |
$DATA_DIR/www |
Where poe publish writes data.json |
TISSOT_USER / TISSOT_PASS |
— | Only for scraping the Tissot game yourself |
Poe tasks load .env automatically; direct python -m runs want
uv run --env-file .env ....
Where the data comes from (and how politely it's scraped): docs/SOURCES.md.
Hacking on it
uv run poe test # pytest, coverage >80% enforced
uv run poe tidy # ruff lint + format
uv run poe docs-serve # MkDocs on :8000
uv run cz commit # commits go through commitizen, no exceptions
Full workflow in CONTRIBUTING.md.
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wraith_tdf_predictor-0.8.0.tar.gz.
File metadata
- Download URL: wraith_tdf_predictor-0.8.0.tar.gz
- Upload date:
- Size: 160.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75ed329d9f267ac76b0bc2c8002218a7be8c9bdde2a847771dc357769c9057cb
|
|
| MD5 |
955368a10f1f9204b63a036b3c8cf258
|
|
| BLAKE2b-256 |
acc8fd3823fd8a7a4e27a61201b6c0289e3c273b6b09b44a071e9f7983cff563
|
File details
Details for the file wraith_tdf_predictor-0.8.0-py3-none-any.whl.
File metadata
- Download URL: wraith_tdf_predictor-0.8.0-py3-none-any.whl
- Upload date:
- Size: 31.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e21db531f5cde286d404e126f82570d2b6241773a54089f96943f99bb31f624
|
|
| MD5 |
2a871ea28012b884fbd702238a6b2cad
|
|
| BLAKE2b-256 |
a8ba2c7c104b9779fb23131e92e0dd7e0e6d35cdaa6ece9f272cff04c93f5836
|