How the ranking algorithm works
An in-depth, code-faithful description of the FBS ranking system: iterative
game points, opponent-adjusted offense/defense, schedule strength, and how weekly
revaluation works. Weights below match the current late-season-tuned config.yaml.
1. Motivation and design goals
Human polls (AP, Coaches) blend résumé, eye test, and narrative. This model aims to be comparable to those polls late in the season while remaining fully algorithmic and reproducible. Design priorities:
- Opponent quality matters — a win over a strong team should be worth more than a win over a weak one, and that value should update as the season clarifies who is actually strong.
- Efficiency is schedule-aware — scoring and stopping points only look impressive relative to the defenses/offenses faced.
- Cupcakes and blowouts are discounted — FCS wins and huge margins cannot dominate the ranking.
- Late-season agreement with AP was used to fine-tune weights (2016–2025), without inventing games or poll results.
2. Data sources
All inputs come from ESPN public APIs (site + core), cached under
data/{year}/:
- FBS team list and conference membership
- Weekly scoreboards (regular season + postseason bowls / playoff)
- Team season statistics (points, yards, etc.) when the endpoint exists
- AP Top 25 (and Coaches when available) for comparison — not used as model inputs except the preseason AP prior
No CollegeFootballData API key is required. If ESPN lacks team-stats for a season/team (notably parts of 2020), the model falls back to empty YPP metrics and game-derived scoring averages.
3. Weekly snapshot and retroactive revaluation
For any week W, the model uses only games completed through week W (plus postseason games for the final ranking). It then runs an iterative fixed-point procedure: opponent strengths are re-estimated until ratings stabilize. That means a Week 3 win over a team that later proves elite is re-valued upward as the season progresses — the same game contributes more once the opponent’s rating rises.
Games = { g | week(g) ≤ W } (final: also postseason)
Repeat until max |rnew − rold| < 1e-06:
1. Normalize current ratings → z-scores r̂
2. Score every game using opponents’ current r̂
3. Recompute opponent-adjusted O/D using current r̂
4. Blend components → new rating; damp with λ = 0.82
Iteration cap: 100. Damping prevents oscillation.
4. Iterative game points
Each decided FBS-involved game adds points to the winner and (negative) points to the loser. Base values from config:
- Win base = 32.0
- Loss base = -12.0
Opponent multiplier (centered near 1.0):
where σ is the logistic sigmoid, opp_strength_scale = 1.15
Additional multipliers:
- Margin of victory — capped at 24 points; weight 0.3 so blowouts help but do not explode rankings.
- Home / away — small location bumps on the multiplier; home advantage also shifts the opponent’s perceived rating by home_adv/10 (home_adv = 2.5).
- FCS — wins vs FCS × 0.15; losses vs FCS × 1.85.
- Quality win bonus — extra points when beating an FBS foe at/above the 75% rating percentile (bonus base 10.0).
- T25 win boost (wins only) — if the opponent’s current model rank is in the top 25, multiply win points by a linear factor from 1+0.18 at #1 down to 1+0.01 at #25. Losses are never scaled up by this factor.
- Quality-loss discount — when losing to a Top-25 or top-quartile foe, multiply the (negative) loss points by 0.7 so tough losses hurt less than ugly losses.
5. Opponent-adjusted offense and defense
For each team-game observation, points scored/allowed are compared to both league averages and the opponent’s season averages, then scaled by opponent unit quality:
off_val = off_base · clip(1 + k·opp_def_z, 0.35, 2.0)
def_base = wL·(league_PAPG − allowed) + wO·(opp_PPG − allowed)
def_val = def_base · clip(1 + k·opp_off_z, 0.35, 2.0)
wL = 0.45, wO = 0.55, k = 0.35
Opponent unit z blends season PPG/PAPG z-scores with the opponent’s current overall rating (rating_blend = 0.4). FCS opponents use fixed weak unit z (-1.0 / -1.0) and inflated/deflated average lines.
Schedule-weighted season O/D
Game O/D values are aggregated with weights that favor tougher opposing units:
floor = 0.12, exp = 1.3
season_off = weighted_mean(off_val) · (1 + od_sos_weight · mean_opp_def_z)
od_sos_weight = 0.22
Offense then mixes a small raw YPP z-score (weight 0.12). Defense is the z-score of schedule-adjusted defensive values. Soft schedules cannot pad O/D ranks the way they can pad raw scoring averages.
6. Strength of schedule, record, H2H, preseason
- SOS — mean of opponents’ ratings faced, blended with conference-mean rating: (1 − α)·opp_mean + α·conf_mean, α = conference_blend = 0.45. SOS block weight in final blend: 0.17.
- Record blend — z-score of 1.0·W + (-0.9)·L, weight 0.07.
- Head-to-head — after ratings converge, if two FBS teams are within 10.0 score points and have played, the winner gets a small bump (3.0).
- Preseason prior — preseason AP ranks seed a decaying prior: decay = max(0, 1 − week / 12), scaled by weight 14.0 (relative to a baseline of 12). Material early; near zero late.
7. Final score blend (current weights)
Component z-scores are blended into a rating, then mapped to a display score ≈ 50 + 15·z(rating) (+ H2H):
| Block | Weight | Notes |
|---|---|---|
| Game points (iterative) | 0.55 | Residual so weights sum to 1.0 among GP+Eff+SOS+Record |
| Efficiency (off + def) | 0.21 | Split evenly across O and D |
| SOS (conf-blended) | 0.17 | |
| Record | 0.07 | |
| Preseason prior | 0.05×prior | Additive; decays by week |
8. Tuning history (brief)
- Levers 1–4 — raised SOS/conference role, stronger preseason prior through midseason,
softened quality losses, tightened FCS cupcake wins. Snapshot:
config_before_levers_1_4.yaml. - Late-season tune (2016–2025) — optimized final + end-regular AP Spearman/overlap
(train 2016–19 + 2021–23; holdout 2024–25), protecting a high final #1 match rate.
Snapshot before:
config_before_late_tune.yaml. Details inoutput/decade/late_tune_results.md.
Weights were kept modest to reduce single-year overfitting. Early-season agreement was not the optimization target.
9. Preseason = Coaches Poll
The preseason board sets ranks 1–25 equal to the AFCA Coaches Poll. After Week 1 games, the full model runs with Coaches as a decaying prior. Post–Week-1 boards are not forced to any poll.
10. Biggest Win (current season)
On the 2026 Top 25, Biggest Win is the win vs the highest-quality opponent (best/lowest converged model rank that week). Ties → higher game_points, then later week.
11. Limitations
- Early season noise — few games, large prior influence, volatile opponent estimates. Week 1–4 Spearman vs AP can be low or negative even when late-season agreement is strong.
- 2020 — pandemic schedules and missing ESPN team-stats; treat midseason metrics with caution.
- Poll ≠ algorithm — AP voters weigh narrative, “looked good,” and conference reputation. Systematic disagreements (model higher on tough-schedule teams, lower on soft undefeateds) are expected, not bugs.
- No injury / market / advanced tracking inputs — only box scores, standings-derived averages, and preseason AP.
- FCS and G5/P4 — handled via multipliers and SOS, not a separate power-conference prior.