Josh BaileyIT Desktop Analyst
←Gaffer IQcontents

Gaffer IQ · Section 10 of 22

CalculationsMatchups

Five weighted sub-metrics, each with its own maturity curve, combined into one CompositeScore and mapped to five colour bands.

Read time
~4 min
Revised
3 Sep 2026
Status
Published

The five sub-metrics

Five weighted sub-metrics result in the overall score, known as the CompositeScore, as one number:

  • Base difficultyThe difficulty metric from FPL directly, to use as a base. A fetch from api/fpl.js reaches fantasy.premierleague.com/api/fixtures through Vercel, and harvests the team_h_difficulty and team_a_difficulty. This is then converted to a 10 if 1, 30 if 2, 50 if 3, 70 if 4, and 90 if 5.
  • Counter-matchupThis is the heaviest calculation. What this does, rather than defining a team’s strength, it instead calculates where a team is strongest and weakest. For example, if a team who concedes mostly set pieces plays against a team that scores mostly from set pieces, the counter-matchup score will swing strongly one way, but if two teams’ strengths and weaknesses don’t counter each other, then this will remain balanced. The relevant data is fetched from understat.com’s getTeamData endpoint. It will fetch shots, goals, situation, and total predicted goals from those shots, and calculate the average predicted goals for that situation, known as shared xG – otherwise where the biggest threat comes from for that team. It will then do this for all situations such as transition play (fast play) and box occupation (close up slow play) as well as set pieces, both for attacking and conceding, calculate a score out of 100 based on how good or bad the counter is, and then calculate the average of all the counters, resulting in Counter-matchup. This also has a maturity value of 10 fixtures, elevating to a max 0.25 weight towards the total CompositeScore in a linear ascent, to weaken the contribution of wild early-season data.
  • Team formRecent trajectory, opponent-adjusted. It exists to correct base difficulty’s preseason-seeded priors in-season. It’s simply fetched from fantasy.premierleague.com/api through Vercel, pulls the bootstrap-static/ endpoint, and harvests the strength_overall_home and strength_overall_away values. It then runs oppAdj = oppStrength / leagueAvgStrength in order to swing form one way or the other based on whether the form is against stronger teams or weaker teams. This also has a maturity, but of 5 fixtures, to reach a max weight of 0.2 – to account for teams that appear in perfect form after only 1 or 2 games.
  • Head-to-headReal cross-season meetings from the Understat history window. Kept deliberately small, as testing revealed that this weakens prediction accuracy if this holds more than a weight of 0.1. The last 10 meetings (or last 5 seasons) are fetched from understat.com through their getLeagueData/EPL/{season} endpoint, looping until 5 seasons are fetched. The head-to-head score is then calculated by 100 × (TotalPointsWon / TotalPossiblePoints), to account for the validity of all wins, losses, and draws.
  • Home and awayHome advantage strength compares how many points a team wins per game at home versus away, then measures how big that gap is compared to every other team in the league. A bigger gap means venue matters more for that team. Both teams’ gaps get averaged and turned into a boost for the home side and an equal penalty for the away side, centred around a neutral 50.

The stacking penalty

A stacking penalty then applies across the sub-metrics. It and the base-difficulty weight are a matched pair – a weight that large would otherwise make a strong favourite’s score nearly immovable, and the penalty is what lets several poor secondary signals tip a fixture without letting any single one do it alone. Neither should be tuned without re-checking the other.

The five bands

The result maps to five bands, symmetric around a neutral midpoint, which drive colour everywhere in the app:

  1. Great75–100
  2. Good60–74
  3. Neutral41–59
  4. Tough26–40
  5. Brutal0–25

Thresholds are config rather than literals, so the palette can be recalibrated after a season’s distribution has actually been observed.

The signal that was removed

There used to be a sixth signal, style clash, and removing it is the most instructive decision in the model. It substantially restated the counter-matchup – two of its three rules described the same interaction, read from the same source data, at half the weight – and two correlated weak predictors are worse than one, because the shared noise gets counted twice while the dependable metrics are diluted. It also assumed season-average pressing is a stable team property, which it is not: a side presses a promoted team and sits off against a title contender. And its observed spread was narrow enough that it could move the composite by roughly half a point – too small to change a decision, large enough to add noise whenever it read a fixture wrong.

Terms used here

Base difficulty
FPL’s own 1–5 rating, rescaled to 10/30/50/70/90 and used only as a starting point. It is preseason-seeded, which is why team form exists to correct it in-season.
Counter-matchup
The heaviest calculation. Rather than asking how strong a team is, it asks where each side is strongest and weakest, and whether those shapes counter each other. Matures over 10 fixtures to a maximum 0.25 weight.
Team form
Recent trajectory, adjusted for the strength of the opponents it was earned against. Matures over 5 fixtures to a maximum 0.2 weight, so two good games in August cannot read as perfect form.
Head-to-head
The real cross-season record between two clubs. Deliberately capped at a 0.1 weight – testing showed anything larger made predictions worse, because a rivalry from three managers ago is weak evidence.Explained in Head-to-head
Home and away
How much venue matters to each side, measured as their home-versus-away points gap relative to the rest of the league, then applied as a boost to the home team and an equal penalty to the away team.
CompositeScore
The single number every fixture is reduced to. Five weighted sub-metrics are scored separately and combined into it, and it is what drives the colour band shown anywhere in the app.
Maturity curve
A per-sub-metric ramp that holds a signal’s weight down until enough fixtures have been played to trust it, rising linearly to its cap. It is what stops wild early-season data driving the score.
Stacking penalty
Applied across the sub-metrics so several weak secondary signals can tip a fixture without any single one doing it alone. It and the base-difficulty weight are a matched pair; neither is tuned alone.
FPL API
Supplies the spine – squads, prices, positions, fixtures, per-player histories and live gameweek scoring. It has no standings endpoint and no access to any season but the current one.Explained in The data
Understat
Not an API, but it fills the gaps the FPL feed leaves: expected goals, six seasons of fixture lists, chronological match timelines, and rosters with position codes and substitution linkage.Explained in The data

Linked from

Next in Gaffer IQIdea and approachFixtures
→
© Josh Bailey · jayjaybee.comSet in Geist.