Glicko-2 Rating System

Summary

The Glicko rating system was invented by Mark Glickman in 1995 as an improvement on the Elo rating system. Its principal innovation is the introduction of a "rating deviation" (RD) — a measure of a player's rating accuracy. A player's RD is one standard deviation of their true strength estimate.

Glicko-2 further improves on Glicko by introducing a rating volatility (σ, sigma) that measures the degree of expected fluctuation in a player's rating based on how erratic their performances are. A consistent player has low volatility; an unpredictable player who has extreme results has high volatility.

The RD is central to the system's value: after a game, the rating change is smaller when the player's RD is already low (rating is accurate) and when the opponent's RD is high (opponent's true rating is uncertain, so less information is gained). RD also increases over time during inactivity.

Glicko-2 is used by Chess.com, Lichess, Dota 2, Counter-Strike 2, Guild Wars 2, and other competitive games. It is in the public domain.

Key Concepts

  • Rating Deviation (RD): Measures accuracy of rating. RD = 50 means the player's true strength is expected to be within ±50 points (1 SD). After a game, RD decreases; during inactivity, it increases.
  • Rating volatility (σ): Only in Glicko-2. Measures consistency of player performance. Low σ = consistent; high σ = erratic. The algorithm uses iterative steps to find σ.
  • Rating period: A batch of games occurring in the same time period, treated simultaneously for updates
  • Scale: Glicko uses a different scale from Elo. The conversion is: r = (R - 1500) / 173.7178, RD' = RD / 173.7178
  • Uncertainty increase over time: RD increases based on time elapsed: RD_new = min(RD_max, sqrt(RD_old² + c² × t)) where t = rating periods since last game

Glicko Algorithm (Step by Step)

Step 1: Determine new RD after inactivity:
$$RD' = \min(RD_{max}, \sqrt{RD_{old}^2 + c^2 \times t})$$

Where c is a constant based on expected growth in uncertainty (typical c ≈ 30–50), and RD_max ≈ 350 for new/uncertain players.

Step 2: Update rating after games:
$$r' = r + Q \times \sum_{i=1}^{m} g(RD_i) \times (s_i - E)$$

Where Q = 1/ln(10)/400, g(RD_i) = 1/sqrt(1 + 3×Q²×RD_i²/π²), E = 1/(1 + 10^(-Q×(r-r_i)/400))

Step 3: Update RD after games:
$$RD' = \sqrt{\frac{1}{\frac{1}{RD^2} + \frac{1}{Q^2 \times \sum g(RD_i)^2 \times E \times (1-E)}}}$$

Glicko-2 Key Formulas (Rating Update)

The Glicko-2 system converts ratings to a glicko-2 scale (μ, φ, σ) where:
- μ = (r - 1500) / 173.7178
- φ = RD / 173.7178

After games, new μ is computed and converted back. The volatility σ is found via iterative algorithm (regula falsi / Illinois algorithm) to satisfy an equation involving the observed results and expected scores.

Python Implementation

import math

class Glicko2:
    def __init__(self, rating=1500, rd=350, volatility=0.06, tau=0.5):
        self.rating = rating
        self.rd = rd
        self.volatility = volatility
        self.tau = tau  # constraints on volatility

    def scale(self):
        mu = (self.rating - 1500) / 173.7178
        phi = self.rd / 173.7178
        return mu, phi

    def expected_score(self, mu, mu_j):
        return 1 / (1 + math.pow(10, -1 * (mu - mu_j) / math.sqrt(2 * math.pow(30, 2))))

    def update(self, opponents_ratings, opponents_rds, scores):
        """Update after a rating period with m games."""
        mu, phi = self.scale()
        # Compute g(phi_j) and E for each opponent
        v_sum = 0
        delta_sum = 0
        for r_j, rd_j, s in zip(opponents_ratings, opponents_rds, scores):
            mu_j, phi_j = (r_j - 1500) / 173.7178, rd_j / 173.7178
            g = 1 / math.sqrt(1 + 3 * math.pow(phi_j, 2) / (math.pow(30, 2) * math.pow(math.pi, 2)))
            E = 1 / (1 + math.pow(10, -1 * (mu - mu_j) / (math.sqrt(2) * 30)))
            v_sum += math.pow(g, 2) * E * (1 - E)
            delta_sum += g * (s - E)
        v = 1 / v_sum
        delta = v * delta_sum
        # Update phi and rating
        phi_star = math.sqrt(math.pow(phi, 2) + math.pow(self.volatility, 2))
        phi_new = 1 / math.sqrt(1 / math.pow(phi_star, 2) + 1 / v)
        mu_new = mu + math.pow(phi_new, 2) * delta_sum
        # Convert back
        self.rating = 173.7178 * mu_new + 1500
        self.rd = 173.7178 * phi_new
        return self.rating, self.rd

Notes

  • Glicko-2 is particularly well-suited for sports with infrequent matches (like international football), where player/rating uncertainty grows meaningfully between games
  • The volatility parameter σ is the key differentiator from Elo — it allows the model to capture players whose ratings change erratically vs. consistently
  • Implementation note: the iterative algorithm for finding σ is the most complex part; consider using established libraries (python-glicko2) for production use
  • For sports betting, Glicko-2's RD can be used to weight predictions — give less weight to recent results from players with high RD (uncertain)