📊 Scoring every player
Every player gets a single number each week: a blend of five signals, weighted by how much each one actually predicts real points (tested against two seasons of historical gameweek data, not guessed). Goalkeepers use a slightly different mix, noted below.
FPL's own official next-gameweek projection, floored by recent points-per-game so a single lucky/unlucky projection can't dominate. Points-per-game itself is ignored below 900 minutes played — too small a sample to trust.
FPL's own rolling recent-form stat — how hot or cold a player has been lately, independent of their season-long average.
How favourable the upcoming run of fixtures is, decayed so nearer matches matter more. Position-aware: defenders/keepers care about the opponent's attacking threat (clean-sheet odds); attackers care about the opponent's defensive solidity (scoring odds) — two different things a single 1–5 difficulty rating conflates.
Over/under-performance vs expected goal involvement (xG + xA). A player consistently outperforming their underlying chances is more likely to regress; one underperforming is more likely to catch up.
FPL's Influence/Creativity/Threat stat. Zeroed for goalkeepers — it's driven by save volume, which tracks facing more shots (often on a worse team), not the clean sheets that actually earn a keeper points.
Each team's underlying expected goals conceded per 90, while this player was on the pitch. A stronger defense genuinely means more clean sheets, beyond what season stats alone capture. Zero weight for MID/FWD, where clean sheets barely matter for points.
+0.75 for the confirmed primary penalty taker (+0.25 for backup), +0.15 for primary direct free-kicks, +0.10 for primary corners. A different evidence tier from the rest — not backtested, since its real value is catching a player who just inherited duty, which a season-long backtest can't see (an established taker's value already shows up in their normal scoring stats).
⚖️ Picking the squad
Not a greedy "best single upgrade" search — a proper integer program (PuLP/CBC solver) that considers the whole 15-player squad and starting XI jointly, so it can see combinations a step-by-step search would miss (sell a little here, upgrade a lot there, all in one move).
What it's actually maximizing:
Valued by a horizon score: each of the next 5 gameweeks scored individually using that week's specific fixture, discounted 15% per week further out (HORIZON_DECAY = 0.85) to reflect growing uncertainty. A rough week ahead of a strong run isn't undervalued, and vice versa.
BENCH_WEIGHT = 0.15. A bench player only scores when actually subbed in, which is rare — especially a backup keeper. Without this discount, the optimizer once priced a strong second-choice keeper as if he were a nailed-on starter (see change feed below).
TACTICAL_WEIGHT = 0.5. Controls how much "hot right now" matters relative to "good over the next few weeks" when deciding who starts vs sits.
HIT_COST = 4 — FPL's actual point deduction. A hit is only taken when the projected gain clearly clears that bar, not a hand-picked "feels worth it" threshold.
Hard constraints (not tunable — just FPL's actual rules): budget, 2/5/5/3 squad composition, max 3 players per real club, legal formation (1 GKP, 3–5 DEF, 2–5 MID, 1–3 FWD in the starting XI).
🏆 Picking the captain
Deliberately not the same scoring as squad selection. Squad choices are a multi-week commitment (transfers cost money or a hit), so they use the horizon score above. Captaincy can be changed for free every single week, so it's scored purely on that week's projection only — same five weights, but with zero look-ahead beyond the immediate match. Highest scorer is captain, second-highest is vice.
🧪 Kept honest by data
Every weight above was arrived at by backtesting against two real seasons of gameweek-by-gameweek FPL data (2024–25 and 2025–26), not intuition. Two different tests are used, because they answer different questions:
Prediction accuracy — does a signal correlate with a player's actual points? The current formula scores r = 0.571 correlation with real outcomes. When a signal's own isolated correlation looked backwards (the underlying-stats delta correlates at −0.14 alone) it got tested in the full combined formula rather than flipped on a hunch — flipping it actually scored worse (r = 0.563) than leaving it alone.
Decision quality — for a ranking problem like captaincy, correlating well in general isn't the same as picking the single best candidate from a pool. A dedicated simulation replays every historical gameweek and compares what each rule would actually have earned as captain: the current formula outscored the alternative by roughly 0.46 points per gameweek, averaged over 56 simulated gameweeks.