This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
AIrsenal is a machine learning package for optimizing Fantasy Premier League (FPL) team selection and transfer decisions. It uses Bayesian statistical models to predict player/team performance, a greedy/brute-force approach to optimize transfers, and a DEAP genetic algorithm for initial whole-squad selection — all under FPL constraints (budget, squad size, position limits, chips, etc.).
Always run Python with uv run or inside the virtual environment (source .venv/bin/activate).
Install (including dev tools):
uv sync --extra devRun tests:
uv run pytest airsenal/tests
# Single test file:
uv run pytest airsenal/tests/test_utils.py
# Single test:
uv run pytest airsenal/tests/test_utils.py::test_function_nameLint and format:
uv run ruff check --fix .
uv run ruff format .Type checking:
uv run mypy airsenal/framework airsenal/scriptsPre-commit hooks:
pre-commit install
pre-commit run --all-filesRun the full pipeline (typical usage):
uv run airsenal_run_pipelineairsenal/framework/— all core logic; statistical models, database schema, optimization, squad/player classes, data fetchingairsenal/scripts/— CLI entry points; ideally just parse args and call framework functionsairsenal/tests/— pytest tests for framework codeairsenal/data/— static historical FPL data (multiple seasons, used to seed the database)airsenal/api/— optional Flask API (work in progress)
- Database init (
fill_db_init.py) — loads historical season data fromairsenal/data/into a local SQLite database - Database update (
update_db.py) — fetches current-season fixtures, results, and player attributes from the FPL API viacurl_cffi - Prediction (
fill_predictedscore_table.py) — runs BPL (Bayesian Premier League) team models and player-level models to predict points; writes toPlayerPredictiontable - Optimization (
fill_transfersuggestion_table.py) — uses a greedy/brute-force search to find optimal transfers; writes toTransferSuggestiontable - Apply (
make_transfers.py,set_lineup.py) — optionally posts transfers and lineup to the FPL API. NEVER runmake_transfers.pyyourself whilst testing changes as this leads to irreversible changes to the actual AIrsenal FPL team entry.
airsenal_run_pipeline is the top-level orchestrator for steps 1–5.
| File | Purpose |
|---|---|
schema.py |
SQLAlchemy ORM models (Player, Fixture, PlayerScore, PlayerPrediction, Squad, etc.) |
data_fetcher.py |
FPL API client (uses curl_cffi); handles auth and data fetching |
prediction_utils.py |
BPL team-level match score predictions |
player_model.py |
Conjugate Bayesian and Numpyro player performance models |
optimization_utils.py |
Transfer optimization logic (greedy/brute-force) |
optimization_squad.py |
Initial whole-squad optimization (DEAP genetic algorithm) |
squad.py |
Squad class: 15 players, formation/budget constraint checking |
transaction_utils.py |
Transfer transaction management |
utils.py |
Shared utilities and default database session |
SQLite, default location: $AIRSENAL_HOME/data.db (configurable via AIRSENAL_DB_FILE env var). SQLAlchemy v2.0+ ORM. The dbsession argument (defaulting to the session created in schema.py) is threaded through most framework functions.
Required env var: FPL_TEAM_ID. Optional: FPL_LOGIN, FPL_PASSWORD, FPL_LEAGUE_ID, AIRSENAL_DB_FILE. Use airsenal_env set to persist these under AIRSENAL_HOME.
fill_predictedscore_table.py used to parallelize player predictions with a thread/process pool; this was removed because jax deadlocks under multi-threading, and prediction is fast enough without it. Don't reintroduce multi-threading/multiprocessing there (or in code that calls jax-based models) unless the deadlock issue is independently resolved.
- Branch naming:
feature/<issue>-<description>orbugfix/<issue>-<description>; all new branches should be made fromdevelop, and all pull requests should be made to merge intodevelop - Function argument order (where applicable): other args →
player/player_id→position→team→tag→gameweek→season→fpl_team_id→dbsession→apifetcher→verbose - Season strings:
"2122"for the 2021/22 season - Position strings:
"GK","DEF","MID","FWD", or"all" - Docstrings should follow numpydoc convention; type hints are encouraged