Wins-First CFB Top-25 (Kai Morales)

Methodology, Formulas & Algorithm

An objective, reproducible ranking model: week-of-game Coaches Poll ranks, Power-4 weighting, schedule strength, and diminishing returns on unranked wins.

This is Disclaimer: This model isn’t meant to predict game outcomes — it’s a dynamic, unbiased ranking system. However, its results often highlight inconsistencies or biases in the AP Poll.

View Past Rankings

Model vs. AP Poll

Github

Abstract

I noticed that traditional media polls can be reputation-driven. This model replaces subjective ranking with a wins-first, transparent scoring system. Each game receives a value based on (i) opponent quality at the week of play via the Coaches Poll, (ii) location and capped margin, (iii) Power-4 vs. Group-of-5 tier for unranked opponents, and (iv) a season-level strength-of-schedule multiplier.


Core Ideas

  • Week-of-game ranks: Upsets valued when they occurred, not retroactively.
  • Conference tiering: Unranked P4 wins > unranked G5 wins; G5 losses sting more.
  • SOS scaling: Season-level multiplier based on average opponent quality.
  • Parsimony: Few transparent knobs; no black box.
Bias note: Journalist voting can overweight legacy brands. We rely on the Coaches Poll only as a signal, and only at week-of-game.

Tooling

Python, Pandas, cfbd API, python-dotenv, Certifi


Formulas

Opponent Rank Quality (week-of-game)

Rank quality \(rq\) (1 is best) maps to \([0,1]\):

$$ rq(r) = 1 - \frac{r - 1}{24}, \quad r \in \{1,\dots,25\}. $$

Location & Margin

Location multiplier: $$ L=\begin{cases} 1.04 & \text{Road} \\ 1.00 & \text{Neutral} \\ 0.985 & \text{Home} \end{cases} $$ Margin is capped: \( m'=\min(m,\,14) \).

Win margin bonus: $$ \text{WinMargin}(m') = 1 + \gamma \cdot m', \quad \gamma = 0.008. $$

Close-loss relief (with floor \(\rho\)): $$ \text{Relief}(m') = \max\!\bigl(\rho,\; 1 - \lambda \cdot m'\bigr), \quad \rho=0.70,\;\lambda=0.01. $$

Ranked Scaling

Ranked wins get a multiplicative boost; ranked losses get a discount: $$ \text{WinBoost} = 1 + \beta \cdot rq, \qquad \text{LossDiscount} = 1 - \delta \cdot rq, $$ with \(\beta=0.70\), \(\delta=0.40\).

Unranked Opponent Tiering (P4 vs G5/Ind)

For unranked opponents, apply conference-tier multipliers: $$ s_{\text{win}}=\begin{cases} 0.85 & \text{P4 opponent} \\ 0.40 & \text{G5/Independent} \end{cases} \qquad k_{\text{loss}}=\begin{cases} 1.05 & \text{P4 opponent} \\ 1.30 & \text{G5/Independent} \end{cases} $$

Per-Game Value

Win:

$$ \text{base}_W = \bigl(1 + \alpha_W \, Q_{\text{opp}}\bigr)\, L \,\text{WinMargin}(m'), \quad \alpha_W=0.6 $$ $$ \text{value}_W = \begin{cases} \text{base}_W \cdot s_{\text{win}} \cdot \text{WinBoost}, & \text{opp unranked (tiered)} \\\\ \text{base}_W \cdot \text{WinBoost}, & \text{opp ranked} \end{cases} $$

Loss:

$$ \text{base}_L = \bigl(B + \alpha_L \, Q_{\text{opp}}\bigr)\, L \,\text{Relief}(m'), \quad B=0.90,\; \alpha_L=0.3 $$ $$ \text{value}_L = \begin{cases} -\text{base}_L \cdot k_{\text{loss}} \cdot \text{LossDiscount}, & \text{opp unranked (tiered)} \\\\ -\text{base}_L \cdot \text{LossDiscount}, & \text{opp ranked} \end{cases} $$

Season-Level Adjustments

Soft clamp (schedule + no ranked wins): $$ \text{WinsScore} \leftarrow \begin{cases} 0.90 \cdot \text{WinsScore}, & \text{if } \text{RankedWins}=0 \text{ and } \overline{Q}_t < 0.55 \\ \text{WinsScore}, & \text{otherwise} \end{cases} $$

Strength-of-schedule multiplier (with average opponent quality \(\overline{Q}\)): $$ \text{SOS}(\overline{Q}) = 0.50 + 1.10 \cdot \overline{Q}. $$

Diminishing returns on unranked wins (starting after the first): $$ d(n) = 0.93^{\max(0,\,n-1)}. $$

Ranked-win additive kicker (scaled by ranked-opponent quality): $$ \text{Kicker} = \kappa \cdot \text{RankedWins} \cdot \bigl(0.7 + 0.3\,\overline{Q}_{\text{ranked}}\bigr), \quad \kappa=0.30. $$

Final Score

$$ \text{WinsScore}(t) = \Bigl(\sum_{g \in \text{games}(t)} \text{value}_g\Bigr)\cdot \text{SOS}\!\left(\overline{Q}_{t}\right)\cdot d\!\left(\text{UnrankedWins}_{t}\right) + \text{Kicker}. $$


Algorithm Design (Step-By-Step)

  1. Data: Pull FBS games for weeks 1..W; duplicate rows home/away with result, location, margin.
  2. Week-specific Coaches Poll: For each week, map team → \(Q\) and rank → \(rq\).
  3. Fallbacks: If no poll for opponent that week, use normalized SRS; else shrunk win% through week.
  4. Per-game scoring: Apply equations above; special handling for unranked P4 vs G5/Ind.
  5. Season aggregation: Sum game values; multiply by SOS; apply unranked-win decay; add ranked-win kicker.
  6. Ranking: Sort by WinsScore (tie-break by tougher average opponent quality).

My current rankings going into week 11:

This table contains the latest Top-25 output from the model after week 10.

#TeamWinsScoreAvgOppQRanked WinsUnranked Wins
1Alabama10.4791440.52881343
2Indiana9.9990910.61929726
3Ohio State9.6304810.61168725
4Texas A&M8.3382390.56640526
5Ole Miss7.8674050.58650826
6Georgia7.5895070.57207024
7Vanderbilt6.5179600.44159133
8Oregon6.5055370.54402615
9BYU6.1869300.51003916
10Miami6.0876740.52377832
11Georgia Tech6.0577420.54256916
12Texas5.9743550.54187125
13Louisville5.8328590.58202315
14Virginia5.3212190.50468416
15Oklahoma5.3119190.39543533
16Texas Tech4.7906800.44798316
17Utah4.7877230.40382424
18Notre Dame4.2680550.47769615
19Cincinnati4.1692180.43719715
20LSU3.6520560.62022313
21USC3.5732290.38800515
22Arizona State3.3552790.57720223
23Michigan3.3374980.51458107
24Washington3.0446780.55319314
25Pittsburgh3.0441210.50915806

Reproducibility

Parameters (Current)