LeetCode Rating Predictor

Predicts how much a LeetCode contest placement moves a user's rating, from the rank and the user's contest history. It is a small Dense network (15 -> 128 -> 64 -> 32 -> 1, 12,417 parameters) that runs on plain NumPy or JavaScript; no ML framework is needed to serve it.

It powers the web app and the browser extension, which loads model.json from this repo and adds a Predicted column to LeetCode ranking pages. Source, training and feature code: GitHub.

Files

File What
model.json Network and scaler in one JSON file (what the extension loads)
weights.npz, scaler.json The same network as NumPy arrays (what the API loads)
model.keras The trained Keras model these are exported from
metrics.json Split sizes and held-out scores of this model
backtest.md Accuracy on recent contests of users outside the training data

Results

Held-out users (818 users, 27,698 contest records never seen in training), trained 2026-10-18:

Metric Value
MAE 14.14
RMSE 22.01
MAE of always predicting 0 27.79

Backtest on recent real contests of users outside the training data (scripts/backtest.py, 2026-10-19):

Metric Value
Contests scored 173
MAE 9.11
Direction right (gain vs loss) 90.8%
MAE of always predicting 0 29.34

Inputs

One row per contest result, in this order. scaler in model.json maps them to the network's range (x * scale + min).

Feature
f1 rating_before
f2 rank
f3 participants
f4 rank_percent
f5 contest_index
f6 avg_solve_rate
f7 avg_finish_time
f8 recent_solve_rate
f9 recent_finish_time
f10 rating_trend
f11 max_rating
f12 log_rank
f13 rating_x_rank_share
f14 solve_rate_x_rating
f15 finish_time_share

participants is max(rank * 1.5, 10000), the form the training data was built with, not the registered count. See app/services/features.py on GitHub for the exact definitions.

Use

import json, numpy as np
from huggingface_hub import hf_hub_download

model = json.load(open(hf_hub_download("Sagargupta1610/leetcode-rating-predictor", "model.json")))
x = np.asarray(features, dtype=float)  # the 15 inputs above
x = x * np.asarray(model["scaler"]["scale"]) + np.asarray(model["scaler"]["min"])
for layer in model["layers"]:
    x = x @ np.asarray(layer["kernel"]) + np.asarray(layer["bias"])
    if layer["activation"] == "relu":
        x = np.maximum(x, 0)
rating_change = float(x[0])

Training

Trained on the contest records collected by scripts/update_data.py: 295,899 records from 8,182 users, split by user so no user is on both sides (242,108 train, 26,093 validation, 27,698 test), huber loss, seed 42, 62 epochs with early stopping.

Limits

  • A user's first few contests can move the rating by 100+ points; those are the largest errors.
  • The participant count is synthetic (see Inputs), so the model does not know how large a contest really was.
  • LeetCode can change its rating formula; the backtest is re-run weekly to catch drift.
Downloads last month
10
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support