Elo — the rating system invented for chess and borrowed by every sport since — is the friendliest way to build your own power ranking. There's no matrix algebra, no optimizer, just one update rule applied after every game: the winner takes points from the loser, and the size of the transfer depends on how surprising the result was. In about 30 lines of Python we'll rate every FBS team from real results, then check how often it would have called the winner. Full code is in scripts/cfb-elo-rating-python.py.

The one formula you need

Before a game, each team has a rating (everyone starts at 1500). The favorite's expected win probability is:

def expected(a, b):
    return 1 / (1 + 10 ** ((b - a) / 400))
A 400-point edge means ~91% expected; equal ratings mean 50%.

After the game, you nudge each rating toward what actually happened:

new_rating = old_rating + K * (actual - expected)

actual is 1 for a win, 0 for a loss. K is the step size — bigger K means ratings move faster. We use K = 40 and give the home team a +65 Elo bump for home-field before computing the expectation.

Curve of the Elo expected score plotted against rating difference on the standard 400-point logistic scale, rising from near zero through 50 percent at an even rating difference to near one, with marked points showing a plus 100 edge worth about 64 percent and a plus 200 edge worth about 76 percent.
The one formula above, drawn out: the Elo expected score on the standard 400-point scale. A 100-point rating edge is worth about 64% expected score and a 200-point edge about 76%. Computed from the Elo expected-score formula E = 1 / (1 + 10^(-dR/400)).

Run it over a season

Pull the season's games in order (we use the shared _cfb_season helper over ESPN's public results), then loop:

elo = {}
for g in games:                       # games sorted by week
    rh = elo.get(g["home"], 1500)
    ra = elo.get(g["away"], 1500)
    eh = expected(rh + 65, ra)        # home-field in the expectation
    home_won = 1 if g["hs"] > g["as"] else 0
    elo[g["home"]] = rh + 40 * (home_won - eh)
    elo[g["away"]] = ra + 40 * ((1 - home_won) - (1 - eh))
That's the entire engine. Everything else is sorting and printing.

The result, and a back-test

While we loop, we can also check the rating's predictive value: did the higher pre-game rating (plus home-field) win? On the 2024 season:

Back-test: predicted the winner in 604/873 = 69.2% of games.

Top 15 by Elo:
 1  ORE    1698     6  IU     1639     11  CLEM  1619
 2  BOIS   1669     7  SMU    1633     12  OSU   1618
 3  ND     1664     8  PSU    1631     13  ARMY  1617
 4  ASU    1649     9  TEX    1630     14  SC    1617
 5  UGA    1645    10  MRSH   1629     15  OHIO  1616
Actual output, 2024 season (ESPN results), retrieved June 2026.
Horizontal bar chart of end-of-2024 college football Elo ratings, led by Oregon near 1700.
Your from-scratch Elo, end of 2024. Data: ESPN public API; Elo by the code above. Retrieved June 2026.

Predicting ~69% of games from one number is genuinely good — and it exposes Elo's nature. Notice Ohio State sits 12th, even though the Buckeyes won the national championship. Pure Elo only knows results, and Ohio State lost two regular-season games (to Oregon and Michigan) before getting hot in the playoff. Elo doesn't reward "peaking at the right time"; it rewards a body of work. That's a feature for prediction and a quirk for crowning champions — exactly the tension we explore in choosing a champion.

Make it better

  • Margin of victory. Multiply K by a function of the score margin (with diminishing returns) so blowouts move ratings more than one-point wins — but cap it to avoid rewarding running up the score.
  • Preseason priors. Instead of starting everyone at 1500, carry over a fraction of last year's rating. Early-season predictions improve a lot.
  • Tune K and home-field. Re-run the back-test with different values and keep what predicts best. That's a one-line experiment now.
  • Regress to the mean between seasons so a great team doesn't start the next year overrated.

Elo is the gateway drug of sports analytics: simple enough to understand fully, powerful enough to beat the polls at prediction. Once it's working, the logistic-regression tutorial shows how to turn that Elo edge into a calibrated win probability.

Try it yourself

Put the formula from this piece to work on your own numbers. It runs entirely in your browser — nothing is sent anywhere. For the full set, see the Calculators hub.

Calculator

Elo win probability

Give the rating gap between two teams and get the favorite's win probability from the Elo logistic. Add any home-field bonus to the home rating before you take the difference.

Pure math on the numbers you enter. Nothing is fetched and no real team, player, or result is named or invented.

Sources & further reading

C. B. Zakarian

C. B. Zakarian is an independent analyst who writes about what he can measure: ball sports and the player-run economies inside Roblox. He builds every model, chart, and calculator here himself from public data, shows the working, and never invents a number. When the data can't answer a question, he says so. On CollegeAthleteInsider, that means college football and basketball by the numbers, plus a plain-English read on the NIL-era rules. More about the methodology →