Sample Code for College Basketball Ratings System
This page contains the sample code for my college basketball ratings system that I used during the 2025-2026 season. There are obvious flaws (asymmetric k factors, etc.) and some of the code is not very clean, but the model is still functional. I will keep the dataset private, but the dataset can be nearly replicated with the information found at https://kenpom.com/cbbga24.txt. I will also keep the ML-style hyperparameter fitting private, but this too is fairly straightforward to replicate.
elo_engine.py
import math
import pandas as pd
def expected_score(r_i, r_j):
diff = max(-1000, min(1000, r_j - r_i))
return 1.0 / (1.0 + 10.0 ** (diff / 400.0))
def mov_multiplier(margin, rating_diff, mov_k, mov_divisor):
if margin <= 0: return 1.0
# fivethirtyeight standard: denominator = (0.001 * elo_diff) + 2.2
denominator = (mov_divisor * rating_diff) + mov_k
# safety check to prevent division by zero or negative values
if denominator <= 0.0001: denominator = 0.0001
return math.log(margin + 1) * (mov_k / denominator)
def compute_team_elo(long_df, priors=None, **params):
# parameter extraction (with defaults)
base_k = params.get('base_k', 24.0)
start_rating = params.get('start_rating', 1500.0)
home_adv = params.get('home_adv', 65.0)
k_threshold_1 = params.get('k_threshold_1', 8)
k_mult_1 = params.get('k_mult_1', 2.0)
k_threshold_2 = params.get('k_threshold_2', 15)
k_mult_2 = params.get('k_mult_2', 1.5)
mov_k = params.get('mov_k', 2.2)
mov_divisor = params.get('mov_divisor', 0.001)
regress_weight = params.get('regress_weight', 0.75)
ratings = {}
games_played = {}
history_rows = []
for seq, group in long_df.groupby("game_seq", sort=False):
if len(group) != 2: continue
away_data, home_data = group.iloc[0], group.iloc[1]
t_a, t_h = away_data["Team"], home_data["Team"]
for team in [t_a, t_h]:
if team not in ratings:
if priors and team in priors:
# Regression to the mean
ratings[team] = (priors[team] * regress_weight) + (start_rating * (1 - regress_weight))
else:
ratings[team] = start_rating
games_played[team] = 0
r_a_base, r_h_base = ratings[t_a], ratings[t_h]
hca_effect = 0 if home_data["is_neutral"] else home_adv
# expected probs
exp_a = expected_score(r_a_base, r_h_base + hca_effect)
s_a, s_h = away_data["Score"], home_data["Score"]
act_a = 1.0 if s_a > s_h else (0.0 if s_a < s_h else 0.5)
margin = abs(s_a - s_h)
# rating difference: winner - loser
if s_a > s_h:
r_diff = r_a_base - (r_h_base + hca_effect)
else:
r_diff = (r_h_base + hca_effect) - r_a_base
multiplier = mov_multiplier(margin, r_diff, mov_k, mov_divisor)
# k-factor logic
def get_k(count):
if count < k_threshold_1: return base_k * k_mult_1
if count < k_threshold_2: return base_k * k_mult_2
return base_k
k_a, k_h = get_k(games_played[t_a]), get_k(games_played[t_h])
# update ratings
delta = multiplier * (act_a - exp_a)
ratings[t_a] += k_a * delta
ratings[t_h] -= k_h * delta
games_played[t_a] += 1
games_played[t_h] += 1
history_rows.append({
"game_seq": seq, "actual": act_a, "expected": exp_a,
"team": t_a, "opp": t_h, "date": away_data.get('date', None)
})
final_df = pd.DataFrame([{"team": t, "rating": r} for t, r in ratings.items()])
return final_df.sort_values("rating", ascending=False), pd.DataFrame(history_rows)
test_optimized_matchups.py
import pandas as pd
import json
# 1. load your optimized parameters
with open("yourfile.json", "r") as f:
best_params = json.load(f)
# 2. load the rankings
final_ratings = pd.read_csv("yourfile.csv")
hca = best_params.get('home_adv', 65.0)
print(f"=== MATCHUP PREDICTIONS (HCA: {hca:.1f}) ===\n")
# 3. store results for csv
matchup_results = []
# define matchups
matchups = [
("TCU", "Ohio St."),
("Idaho", "Houston")
]
# 4. process each matchup and collect results
for team_a, team_b in matchups:
try:
# extract ratings
r_a = final_ratings.loc[final_ratings['team'] == team_a, 'rating'].values[0]
r_b = final_ratings.loc[final_ratings['team'] == team_b, 'rating'].values[0]
# calculate probabilities and spread
hca_effect = best_params.get('home_adv', 65.0)
prob_a = 1.0 / (1.0 + 10.0 ** ((r_b + hca_effect - r_a) / 400.0))
prob_b = 1.0 - prob_a
div = best_params.get('spread_divisor', 17.5)
elo_diff = (r_a - (r_b + hca_effect))
spread = elo_diff / div
# determine favorite
favorite = team_a if spread > 0 else team_b
# store results
matchup_results.append({
'team_a': team_a,
'team_b': team_b,
'team_a_rating': r_a,
'team_b_rating': r_b,
'team_a_win_prob': prob_a,
'team_b_win_prob': prob_b,
'spread': spread,
'favorite': favorite
})
# print individual matchup
print(f"##############################")
print(f"{team_a} @ {team_b}")
print(f"##############################")
print(f"Win Probability: {team_a} {prob_a:.1%}")
print(f"Projected Line: {team_a} {spread:+.1f}")
print(f"Projected Favorite: {favorite}")
print(f"##############################\n")
except IndexError:
print(f"Error: Could not find ratings for {team_a} or {team_b}")
# 5. save to csv
matchups_df = pd.DataFrame(matchup_results)
matchups_df.to_csv("results.csv", index=False)
print(f"Saved {len(matchup_results)} matchup predictions to results.csv")