Glicko-Boost Rating System¶
See How to choose a rating system for a decision guide across every system, or Glicko for the system Glicko-Boost extends.
Overview¶
Glicko-Boost is Mark Glickman’s extension of Glicko, written for the Deloitte/FIDE Chess Rating Challenge and described in Glicko-Boost. It adds four things to Glicko: an advantage for playing white, a second pass that re-rates every player against the opponents’ first-pass ratings, an RD “boost” for players whose results were exceptional, and a rating-dependent model for how RD grows between periods.
It is the first system in Elote whose published update is defined over a rating period rather than over one game. The two-pass structure cannot be expressed as a stream of pairwise updates – a player’s rating change depends on how their opponents did in the same period – so GlickoBoostCompetitor overrides apply_rating_period() and does the whole population at once. The pairwise API is unchanged: beat/lost_to/tied run the same algorithm over a one-game period.
How It Works¶
Every step is built on Glicko updating with a white-advantage term. For a player with rating \(r\) and rating deviation \(RD\) who played \(J\) games with scores \(s_j\) against opponents \((r_j, RD_j)\):
Here \(w_j\) is \(+1\) when the player had white in game \(j\), \(-1\) when black, and \(\eta\) is the rating advantage of playing white. With \(\eta = 0\) the term disappears and this is ordinary Glicko updating.
The period update¶
Given every player’s pre-period rating and RD, one period is five steps:
Update every player from the pre-period ratings and RDs.
Update every player again from the pre-period ratings and RDs, but against the opponents’ step 1 ratings and RDs. A player who lost to opponents who all had a strong month is now judged against those better ratings.
Compute each player’s performance z-score and boost their pre-period RD if it is large (below).
Repeat step 1 with the boosted RDs.
Repeat step 2 on the step 4 results. These are the period’s final ratings and RDs.
If no RD was boosted in step 3, steps 4 and 5 reproduce steps 1 and 2 exactly, and Elote skips them.
The RD boost¶
The boost catches an improving player whose RD is too small for their rating to move fast enough. The z-score measures how far the period’s results ran ahead of what the player’s pre-period rating predicted, against the step 2 population:
\(k = 1.96\) is the point beyond which a z-score would occur by chance 2.5% of the time, so on an ordinary month nobody is boosted and steps 4 and 5 never run.
RD increase over time¶
Instead of Glicko’s constant \(c\), the increase depends on both the RD and the rating, so that higher-rated players – whose strength is more stable – gain slightly less uncertainty per idle period:
Elote applies this when a competitor next takes part, for the rating periods it sat out, the way GlickoCompetitor handles inactivity. Results that share a period end therefore never inflate each other’s RD.
Prediction¶
expected_score uses the paper’s own approximation, which combines the two rating deviations rather than using only the opponent’s:
With the default \(\eta = 0\) this is exactly complementary: a.expected_score(b) + b.expected_score(a) == 1.
The colour convention¶
Elote’s result API carries no colour argument, and Glicko-Boost does not get one added for it. Instead argument order is the colour: in a rating-period row (a, b, outcome, scores), and in a.beat(b), a is white and b is black.
Callers who have no colour information should leave _eta at its default of 0.0, which is why that – rather than the paper’s 30.0 – is Elote’s default. The update then depends only on rating differences and is invariant to the order in which each row is written.
One exception is worth knowing about: matchup() dispatches a loss by calling beat on the winner, so a losing row driven through the arena’s streaming path has its colours reversed relative to the same row handed to apply_rating_period. With the default _eta = 0.0 nothing changes; if you have set a white advantage, group your results into periods and use rating_period(), which preserves each row’s order.
Key Parameters¶
All of these are class-level, so they are settable through configure_class and reachable from walk_forward() and tune() through competitor_params.
Parameter |
Elote default |
Meaning (Glickman’s optimized value) |
|---|---|---|
|
0.0 |
Rating advantage for playing white (30.0) |
|
0.20139 |
RD boost multiplicative factor (0.20139) |
|
17.5 |
RD boost additive factor (17.5) |
|
1.96 |
z-score threshold above which the RD is boosted (1.96) |
|
5.83733, -1.75374e-04, -7.080124e-05, 0.001733792, 0.00026706 |
RD-increase-over-time coefficients (same values) |
|
250.0 |
RD cap, \(RD_{unr}\) (250.0) |
|
30.0 |
Days in one rating period, used to turn elapsed time into RD increases |
initial_rating (default 1500) and initial_rd (default 250) are constructor arguments, so they travel through base_competitor_kwargs rather than competitor_params. Glickman’s default rating for an unrated player is 1946.25, which is specific to the FIDE population the system was fitted on; Elote keeps its house default of 1500.
How It Differs From Glicko and Glicko-2¶
Glicko |
Glicko-2 |
Glicko-Boost |
|
|---|---|---|---|
Update granularity |
One game (Elote replays a period pairwise) |
One game |
The whole period, twice over |
Colour |
Not modelled |
Not modelled |
\(\eta\) inside \(E()\), from argument order |
Reacting to a surprising month |
Nothing beyond the ordinary update |
A per-player volatility \(\sigma\) updated every period |
A one-off RD boost when \(Z > k\), then a re-rate |
RD growth while idle |
\(\sqrt{RD^2 + c^2 t}\) |
Driven by volatility |
\(\exp\) of a rating-and-RD-dependent polynomial |
Expected score |
\(g(RD_B)\) only |
Transformed scale |
\(g\!\left(\sqrt{RD_A^2 + RD_B^2}\right)\), so it is complementary |
Glicko-2’s volatility is a persistent per-player parameter that is re-estimated every period; Glicko-Boost’s boost is a one-shot widening of the prior for the period being rated, discarded afterwards. Glicko-Boost is therefore cheaper and has no iterative solve, at the cost of not tracking volatility over time.
Implementation in Elote¶
from datetime import datetime
from elote import GlickoBoostCompetitor
# The paper's white advantage; leave it at 0.0 if your data has no colour.
GlickoBoostCompetitor.configure_class(eta=30.0)
alice = GlickoBoostCompetitor(initial_rating=2300, initial_rd=140)
bob = GlickoBoostCompetitor(initial_rating=2295, initial_rd=80)
cara = GlickoBoostCompetitor(initial_rating=2280, initial_rd=150)
# One rating period, applied to the whole population at once. The first
# competitor of each row had white.
GlickoBoostCompetitor.apply_rating_period(
[
(alice, bob, 0.0, None),
(cara, alice, 1.0, None),
(bob, cara, 0.5, None),
],
period_end=datetime(2026, 1, 31),
)
print(alice.rating, alice.rd)
print(alice.expected_score(bob))
Through an arena, the same period is one call:
from elote import LambdaArena, GlickoBoostCompetitor
arena = LambdaArena(lambda a, b: True, base_competitor=GlickoBoostCompetitor)
arena.rating_period(
[("alice", "bob", 0.0, None), ("cara", "alice", 1.0, None)],
period_end=datetime(2026, 1, 31),
)
print(arena.leaderboard())
beat, lost_to and tied are supported and run the same algorithm over a one-game period, so a single result is never a different formula from a batch. Because of the two-pass structure a lone pairwise call is not identical to a Glicko update, and applying a period one row at a time does not give the period’s ratings – group results into periods when you have them.
Advantages¶
Period-native: rates a whole month the way the published system does, with no within-period ordering effect
Opponent-aware: the second pass judges a player against how their opponents actually did, not just their pre-period ratings
Catches improvers: the RD boost widens the prior for a player whose results were far ahead of their rating, so the re-rate can move them further
Colour: models the advantage of moving first, at no cost to the uniform API
Flexible RD growth: uncertainty grows at a rate that depends on the player’s rating and current RD
Limitations¶
Needs periods: the value is in the batch; a stream of one-game periods loses the second pass’s information
Many parameters: twelve system constants, fitted by Glickman on FIDE data and not necessarily right for another domain
No volatility tracking: unlike Glicko-2, nothing persists between periods about how erratic a player is
Colour is positional: rows have to be written white-first for \(\eta\) to mean anything
Cost: four Glicko passes over the population per period instead of one
Numeric reference¶
tests/test_GlickoBoostCompetitor_known_values.py checks this implementation against the worked example in Section 4 of Glickman’s paper. Two of that table’s columns are not reproducible from the paper’s own formulas – the module’s docstring records the measured deviations and the sweep that establishes it – so they are pinned as regression values while the rest of the table is asserted as the published oracle.