Rating systems

The research line: whole-density beliefs, exact full-order updates.

Rating systems for multi-entrant contests: races, tournaments, leaderboards. Beliefs are whole densities on a lattice, free to be skewed or multimodal; each event updates them with the exact likelihood of the full finish order, computed by an O(N) chain; predictions are exact winner-of-many probabilities, dead-heat aware. Every claim is benchmarked against TrueSkill, OpenSkill, Glicko-2 and Elo on twelve datasets with market ceilings where they exist.

Beliefs are densities, not (mu, sigma) pairs. Each contestant's ability lives on a lattice with no parametric family imposed, and the race noise is an arbitrary kernel you choose: Gaussian, Student-t, or a retirement "slab" for contests where entrants can fail outright. A single update matches exact Bayes to lattice precision, and the update uses the whole finish order rather than pairwise or stagewise approximations. How →
Benchmarked on twelve datasets, prequentially. Formula 1, ATP and WTA tennis, chess, sumo, football, Halo 2, horse racing and synthetic worlds with known truth. Each event is predicted before it is observed, and the tables carry honest baselines: a uniform floor, an oracle where truth is known, and the bookmaker market as a ceiling where odds exist. The tables →
Non-Gaussian noise pays where the physics demands it. Formula 1 retirements make finishing order a mixture: pace when the car survives, a lottery when it does not. A density with a uniform "disaster" component beats every Gaussian system on 1,158 grands prix, and the same estimator, handed qualifying sessions where no retirement process exists, drives its own disaster mass to near zero. The paper (PDF) →
And an efficiency result. Against 88 races with genuinely pre-qualifying bookmaker odds, no rating system built on public results and grid data adds statistical power to the market: the optimal pool puts zero weight on every one of the six systems tested. The market's edge is knowledge of current car form. Section 5 →

Quick start

Status: this API is the research line, developed in the repository's src/ and shipping with a future release; the results above are from BENCHMARKS.md. The shipped race engine is on the home page.

from winning import ThurstoneRating

tr = ThurstoneRating()
tr.observe(names=["ada", "ben", "cid", "dot"], ranks=[1, 2, 3, 4])
tr.observe(names=["ada", "cid", "eve"], ranks=[1, 2, 3])

tr.win_probabilities(["ada", "cid", "eve"])   # exact field win probabilities
tr.rating("ada")                              # Rating(mu=..., sigma=...)
tr.leaderboard()                              # best-first, conservative

Every system in the package speaks the same three verbs: observe(names, ranks), win_probabilities(names), rating(name). That includes the shims around third-party comparators, so a benchmark row is a one-line swap. Install with pip install winning (core depends only on numpy) or pip install winning[benchmarks] for the comparators.