← All methods

Shot-Quality Expected Points (SQI xPts)

Summary

A second, independent expected-points model. Where Expected Points (xPts) asks "how many points should a team of this strength have?", SQI xPts asks "how many points did the chances this team created and conceded deserve?" For every regular-season match, both sides' per-match Shot Quality Index values (each already a goals-scale expected-goals number) are fed into an independent double-Poisson goals model, the win/draw/loss probabilities are read off the joint distribution, and expected points follow as 3 * P(win) + 1 * P(draw).

Together with actual points and Elo xPts this gives a three-way read per team: what happened, what pre-match strength predicted, and what the shot mix deserved. Surfaced on the Performance vs Expectation view (/analysis/expected).

Data

  • Source: team_match_shot_quality (model v1) joined to matches and standings. No new data is collected; this is purely a transformation of the frozen SQI model's per-match outputs.
  • Time range: every CPL season from 2019 onward, regular season only, same exclusions as Elo xPts.
  • Coverage: SQI needs a teamstats row for both sides of a match. Any match missing one drops out of this metric (it keeps its Elo xPts row, which only needs the result); the compute script logs a warning when that happens.

Choices

Independent double Poisson

Each side's goals are modeled as a Poisson draw at its per-match SQI:

home_goals ~ Poisson(sqi_home)
away_goals ~ Poisson(sqi_away)
p_win  = P(home_goals > away_goals)
p_draw = P(home_goals = away_goals)
p_loss = P(home_goals < away_goals)

This is the natural reading of SQI, which is itself the output of a Poisson regression predicting goals. The joint distribution is computed exactly on a truncated grid (0–25 goals per side, then renormalized so the three probabilities sum to exactly 1). Truncation that high matters: per-match SQI reaches 7.4 in the data, where a short tail would lose several percent of probability mass.

No home-advantage term

Elo xPts adds +100 to the home side's effective rating. SQI xPts adds nothing, deliberately: SQI is computed from the shot mix a team actually produced, and home advantage is already in that shot mix. Empirically, across all stored matches the home side's mean win probability under this model is 0.41 vs 0.34 for the away side — the advantage shows up without being put in.

The draw rate is a model output, not a fitted constant

Elo xPts holds the draw probability at the empirical league rate (~0.26) because Elo alone says nothing about draws. Here the draw probability falls out of the joint distribution per match: low-SQI matches draw more, high-SQI matches less. Across 2023–2025 the model's mean draw probability is 0.255 against an empirical draw rate of 0.280 over the same matches — the known bias of independent-Poisson models (they slightly under-rate low-scoring draws). A Dixon-Coles low-score correction could close the gap and is noted under future work; at a ~2.5-point gap it would mostly redistribute hundredths of a point per match.

Same storage and aggregation as Elo xPts

One row per (team, match) in team_match_sqi_expectations, same shape as team_match_expectations. The API (/api/sqi-expected-points) and the luck-interval machinery (exact season-points convolution, see the xPts doc) are shared code, so every property documented there — interval conventions, conservative coverage, single-season-only intervals — holds here identically.

Relationship to the SQI standings

The shot-quality page already ships an "SQI standings" that decides each match by SQI differential with a hard draw threshold (±0.25). This metric is the principled version of the same idea: instead of a binary call per match, the full outcome distribution is kept, so a narrow SQI edge counts as a narrow edge rather than a coin-flip win. Both views stay; the standings table is easier to read, this one is the one to trust.

Validation

Computed on the current pipeline (regular season, 28 matches per team):

  • Probabilities sum to exactly 1 per match, by construction (renormalization) and pinned by tests.
  • Conservation: per match, xpts_home + xpts_away = 3 - p_draw, so a season's total SQI xPts must equal 3 * matches - sum(p_draw). Holds to the rounding digit in every season.
  • Coverage parity: every match with an Elo xPts row also has an SQI xPts row in the current data (224 match-team rows per full season, both tables).
  • 2025 eyeball (points / Elo xPts / SQI xPts): Forge 58 / 46.4 / 48.9, Atlético Ottawa 56 / 46.0 / 45.6, Cavalry 42 / 46.1 / 42.9, Pacific 23 / 31.6 / 33.3. The two models broadly agree on the ordering while disagreeing in instructive places: Cavalry's strength rating (Elo) thought more of them than their actual chance creation did, which matches a season of underwhelming attacking output from a reputed side.

Reading divergence between the two models

  • SQI xPts > Elo xPts: the team created/conceded better chances than its strength rating implied — underlying play outrunning reputation.
  • Elo xPts > SQI xPts: results-by-reputation; the rating is carried by results (or history) that the shot mix doesn't back up.
  • Points above both: finishing, goalkeeping, or game-state luck — the Δ attribution view splits that further.

Failure modes

  • Everything that limits SQI limits this metric. SQI sees bucketed shot counts, not shot locations (details). A team whose chances are systematically better than their buckets look will be underrated here, match after match.
  • Frozen-model drift. SQI model v1 under-predicts 2025 goals by ~7% league-wide. Lower mus inflate the draw probability slightly and compress the 2025 xPts spread. The effect is roughly uniform across teams, so rankings survive, but cross-season comparisons of SQI xPts levels inherit the drift until a model v2 retrain.
  • Independence assumption. Goals in a match are not independent (teams chase, park the bus, trade punches). Independent Poisson is the standard first-order model and its known draw bias is quantified above, but it is a simplification.
  • Post-hoc, not predictive. Per-match SQI is computed from the shots that actually happened, so unlike Elo xPts this is strictly a retrospective metric. It cannot forecast a future match.

What would make it better

  1. Dixon-Coles correction — a one-parameter adjustment to the low-score cells of the joint distribution, fitted on historical data, would close the 0.255-vs-0.280 draw gap.
  2. SQI model v2 — retraining on data through 2025 removes the drift term; this metric picks the fix up automatically since it recomputes from stored SQI every sync.
  3. Bivariate Poisson — model the goal correlation explicitly instead of correcting for it.