- Python 83%
- JavaScript 9.6%
- CSS 6.6%
- HTML 0.5%
- PowerShell 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
It pinned the line inside pipeline._defcon_minutes, which went in
|
||
| .claude | ||
| .forgejo/workflows | ||
| backend | ||
| data | ||
| docs | ||
| frontend | ||
| scripts | ||
| tests | ||
| wiki | ||
| .gitignore | ||
| build-to-gw1.md | ||
| CLAUDE.md | ||
| constraints.txt | ||
| pyproject.toml | ||
| README.md | ||
| run.bat | ||
| run.ps1 | ||
FPL squad optimiser
An evidence-conscious Fantasy Premier League projection and squad-planning app. It combines official FPL data, historical match and player data, simulations, availability estimates, optional market data, and FPL squad rules to produce a rolling six-gameweek view.
The application includes:
- player and fixture projections;
- best-XI, captain and vice-captain analysis;
- squad building and automatic squad optimisation;
- transfer, ownership and fixture views;
- explicit provenance and refusal states for weak or missing inputs;
- backtesting and calibration utilities;
- point-in-time snapshots for auditable decisions.
The project is intended primarily as a local decision-support tool. It is not a hosted multi-user service, and its write/rebuild endpoints currently have no authentication.
Repository map
| Path | Purpose |
|---|---|
backend/data/ |
Official FPL client, typed data models, SQLite store and sanity checks |
backend/model/ |
Minutes, team strength, finishing, lineup and suspension models |
backend/scoring/ |
FPL scoring, BPS and defensive-contribution rules |
backend/simulator/ |
Match and fixture Monte Carlo simulation |
backend/odds/ |
Odds ingestion, name matching and market comparison |
backend/web/ |
Projection pipeline, optimiser, cache and FastAPI application |
frontend/ |
Static HTML, CSS and JavaScript client |
scripts/ |
Cache builds, data acquisition, diagnostics and backtests |
tests/ |
Unit, integration, API and browser tests |
data/ |
Committed inputs and evidence artifacts; generated local state is ignored |
docs/ |
Designs, methodology notes and maintainer documentation |
See Architecture, Development, and API reference for more detail. Contributors should read CLAUDE.md before changing model or data-flow code; it records the failure modes and invariants that shape this repository.
Quick start
Python 3.11 or newer is required. One command creates an environment and runs the suite from a clean checkout:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[test]" -c constraints.txt
constraints.txt records the versions this suite has actually been run
against. It is not a lockfile - it does not pin transitive dependencies - but it
makes a failure attributable rather than a guess, given that pyproject.toml
declares only lower bounds.
Build the projection cache, then start the app from the repository root:
python scripts/build_cache.py
python -m uvicorn backend.web.main:app --host 127.0.0.1 --port 8000
Open http://127.0.0.1:8000/. Keep the server on loopback: administrative routes can rebuild projections and write local state.
On Windows, use the included launcher:
run.bat
run.bat -Restart
run.bat -Port 8123
The server can start in cache-only mode if live dependencies cannot be loaded.
In that state, read-only cached views remain available, while operations that
need the complete dependency graph return 503.
Common workflows
Rebuild projections after model or source-data changes:
python scripts/build_cache.py
Verify that a change moved no projections (the golden master):
python scripts/golden_master.py
It exits 0 when every frozen field matches, 1 when something moved, and
2 when the cache predates the current code — in which case rebuild first,
because a comparison against a stale cache proves nothing.
Refresh only live prices, ownership and availability while the server runs:
curl -X POST http://127.0.0.1:8000/api/refresh-live
Take an immutable point-in-time snapshot before a deadline:
python scripts/snapshot_pit.py
Ingest a finished gameweek so the model learns from the current season:
python scripts/fetch_current_season.py
Run the principal walk-forward evaluations:
python scripts/backtest_rps.py
python scripts/backtest_minutes.py
Run tests after installing the test dependencies:
python -m pytest
Browser tests use Playwright and are skipped when it is unavailable. CI does not
install it, so these run locally only: run them before pushing a change to
rendered frontend behavior, from the versions pinned in constraints.txt:
python -m pip install -e ".[test,browser]" -c constraints.txt
python -m playwright install chromium
python -m pytest tests/web
Generated local state
The application writes these ignored files:
data/cache/projections.json— projection cache;data/fpl.db— SQLite store, rebuildable withscripts/import_archive.py;data/squad.json— current squad selection;data/overrides.json— explicit start-probability overrides;data/pit/— point-in-time snapshots.
data/golden/baseline.json is committed rather than ignored: it is the
reference the golden master compares against, and it is only useful if it
travels with the code it describes.
Projection artifacts carry build time and source-code provenance. Missing or invalid provenance is intentionally treated as an error rather than silently replaced with a plausible default.
Data sources
The official FPL API is the primary live source. The repository also contains
historical archive data and optional pipelines for Transfermarkt, Understat,
Solio and bookmaker player props. Some acquisition scripts may require external
credentials, metered APIs or pre-registered run scopes. Read their help and the
data-source rules in CLAUDE.md before running them.
Current maintenance notes
The repository review is tracked in Forgejo issues. Known improvement areas include clean-checkout dependency locking and CI, package discovery, persistence concurrency, API request models and module decomposition.
HTML interpolation (issue #1) is partially addressed: the frontend's esc()
helper was a JavaScript-string escaper carrying an HTML escaper's name, and now
escapes &, <, >, " and ' in a single pass. Remaining interpolation
sites have not been audited.
This documentation describes the current implementation rather than implying those items are resolved.