Every team starts the season with the same score: 1500 points. Win a match and your score goes up; lose and it goes down. Beat a team that's better than you and you gain a lot; beat a team you were expected to beat and you gain a little. Over a season, teams settle at a number that reflects how they've actually performed on the court — no committee, no reputation, no politics.
This is the same family of math used to rank chess players worldwide and inside college football analytics. Every number on this site can be traced: click any team and you'll see every match behind their rating.
Before every match, the system estimates who should win based on both teams' current ratings. The result is then compared to that expectation.
This is why running up wins against weak schedules doesn't inflate a rating. The only way to climb high is to beat good teams.
Two teams can both "win," but 25–10, 25–12 and 25–23, 24–26, 15–13 are very different statements. So the rating looks at three things in every match:
A brand-new team's rating jumps around a lot at first — that's on purpose, so a genuinely strong team doesn't spend half a season stuck at the starting number. After about 10 matches, the system has a read on them and the rating settles down, so one bad morning at one tournament can't wreck a season of results.
Three great matches don't prove a team is elite — maybe they caught weak opponents, or everything just clicked that day. Until a team has played about 15 rated matches, the ranking shows a "reined-in" version of their score, pulled part-way back toward their division's starting number. The fewer matches, the stronger the pull.
Teams with fewer than 20 matches also get a P (provisional) tag in the rankings, so you know their number is still settling.
And a top seed must be earned against the best: the top 48 spots in each age group — the size of the Open field — are reserved for teams that have played in the Open division of a qualifying event. Go 30–0 in Club and your rating will be strong — but the seed line waits until you've played Open at one of them. Teams held out of the band carry a No Open tag, and their rating itself is never touched.
We'd rather be upfront about the limits than pretend there aren't any:
On the Tournaments page, every event gets a 0–100 score answering one question: how strong was this field, really? It rewards events that draw top-rated teams, deep fields, and — most heavily — teams from many different states. A stacked local invitational is still a local event; the score is built so only true national draws rank as national.
That score is informational only — it doesn't change any team's rating.
Nothing here is a black box. Click any team on the rankings and you'll see every match, every score, and exactly how much each result moved their rating. Run the season again from the same match data and you get the same numbers every time. If a coach or director disagrees with a seed, the receipts are one click away — and if you find a mistake in the data, the Contribute page is the fastest way to flag it.
Want the exact formulas, weights, and multipliers? Flip the toggle at the top of this page to the Technical view.
Every team starts at 1500. After each match, both teams' ratings move up or down based on the result, adjusted by how they won (point margin, set margin, match outcome), who they played (stronger opponents = bigger reward for a win), where they played (bracket matches count more than pool play), and how much we trust the signal (more matches = more confidence).
The engine processes every match chronologically from the current season (Aug 2025 – Jul 2026). If you re-run the pipeline, the numbers are fully reproducible from the raw match CSV.
Before a match, the engine asks: given the two teams' current ratings, what's the expected win probability for each side? This is the classic ELO formula — a team rated 200 points higher has roughly a 76% expected win rate.
After the match, the engine compares the actual result to this expected result. Beating a team you were supposed to beat gains you a little. Beating a team you weren't supposed to beat gains you a lot. The gap between expected and actual is the engine of the rating change.
A volleyball match isn't just a win or loss — it's a sequence of points inside sets inside a match. The ranking uses all three:
The most granular signal. A 25–10 set transfers more rating than 25–23. Margin is scaled logarithmically so 40-point blowouts don't dominate — beyond a certain margin, extra points give diminishing returns. This protects ratings from being skewed by mercy-rule wins against vastly weaker opponents.
Winning 2–0 is a stronger signal than 2–1. The set layer rewards sweeps. A sweep multiplier of roughly 1.15–1.3× applies based on the set differential.
The binary outcome. You won or you didn't. This is the classic ELO component and guarantees that winning always moves you up, regardless of margin.
K controls the magnitude of the rating change. The bigger K, the faster ratings move. The engine uses an adaptive K:
| Stage | Description | K |
|---|---|---|
| New team | Fewer than 10 matches processed | 48 |
| Established | 10+ matches processed | 32 |
Newer teams calibrate faster — if a true top-tier team starts at 1500, higher K lets them climb to their real rating quickly. Once we have enough data on them, K drops so ratings stabilize and don't swing wildly after a single bad match.
Not every match carries equal weight. The engine applies several multipliers on top of the base rating change:
| Event level | Open (O) | USA (U) | Club (C) |
|---|---|---|---|
| Basemost events (leagues always rate as USA) | 1.0× | 0.5× | 0.5× |
| Super-Regional eventbase + 0.1, by Competitive Index | 1.1× | 0.6× | 0.6× |
| National eventbase + 0.2, by Competitive Index | 1.2× | 0.7× | 0.7× |
Open-division matches carry full weight — that's the strongest competition and the most reliable signal of team quality. USA and Club matches count for half. A win in Club doesn't tell us as much about a team's ceiling as a win in Open, so it moves the rating less.
Divisions with no tier in their name are rated as USA. Local events — leagues, play dates, warmups and mini-club days — usually name divisions by age only ("16 Boys"), and the rule of thumb is that local play is USA tier. Qualifiers and national events name their tiers; if one doesn't, it is flagged for review rather than assumed.
Leagues are rated as USA. A regional power league — the same teams coming back round after round on separate weekends — is rated as USA throughout, whatever its divisions are called: 0.5×, the USA entry rating, and no Open matches. Higher tiers are earned at tournament-format events (a 1–4 day block of play: nationals, qualifiers, invitationals). An event counts as a league when at least half its teams play a second round more than four days after their first; known leagues are also listed by name so they are treated as leagues from their first weekend. By committee decision a few regional tournaments are also weighted 0.5× in every division, Open included, to match regional play elsewhere in the country; unlike a league, their divisions keep their own entry ratings. In 2026-27 those are the UBVA Seeding Tournament and UBVA Kings Peak Seeding. In the other direction, SoCal Cup: The Open Championship and the Nike Steel City Boys Championship never weigh less than 1.0×; a high Competitive Index tier can still raise them above that.
Stronger events weigh more. At a tournament-format event the multiplier rises with the event's Competitive Index (§8): +0.1 for a Super-Regional event, +0.2 for a National one, as in the table. The index is on fixed scales (states represented and team count), so an event's bonus is settled by its own field and never shifts as other events are added.
| Tier | Entry rating |
|---|---|
| Open | 1500 |
| USA / Premier | 1500 |
| Club | 1500 |
Every team enters the pool at 1500, whatever division its first match is in. Every team opens its season in regional and league play, so no division earns a head start; strength between divisions comes only from the division multipliers above (Open 1.0×, USA and Club 0.5×) and from teams crossing over. The anchor is also the confidence-blend target in §5. In force from the 2026-27 season, replacing tiered anchors (Open 1550 / USA 1500 / Club 1450) used earlier in the season; the anchors remain committee-tunable settings per tier.
Crossover matches carry the division they're played in. When a Club-rostered team enters an Open division at an event, those matches count at the full Open 1.0× — crossover play is the calibration bridge between tiers, and the system rewards it.
Pool-play and bracket matches currently carry the same weight (1.0×). The engine supports a bracket bonus, but it's disabled in the current settings because the sample of scouted matches already skews toward competitive pools.
If two teams meet multiple times in the same tournament (e.g., pool then gold bracket), the engine progressively dampens the rating change so a single event doesn't dominate:
| Meeting | Multiplier |
|---|---|
| 1st | 1.0× |
| 2nd | 0.75× |
| 3rd+ | 0.5× |
Some tournaments draw stronger fields than others. Individual events can be weighted up or down by an admin-configurable multiplier (typically between 0.5× and 1.0×) to reflect field strength. The biggest events currently carry the full 1.0× weight; regional and mid-tier events are weighted lower.
A team that's played 40 matches has a rating we can trust. A team that's played 3 matches does not — those three results might be wildly atypical. Instead of showing raw ELO to everyone, the dashboard shows a confidence-blended display rating:
Confidence ramps linearly from 0 to 1 over the first 15 matches. A team with only 5 matches gets confidence = 5/15 ≈ 0.33, so its display rating is pulled about two-thirds of the way back toward its entry rating (1500 for every team — see §4). This prevents fresh teams with a lucky weekend from leapfrogging established programs on the leaderboard, and keeps a low-sample Club team anchored to where its division enters the pool rather than a flat 1500.
Teams with fewer than 20 matches are flagged as provisional in the rankings — their rating is shown but tagged as preliminary.
Matches are sorted by date before processing. This matters: an October win against a team that later develops into a powerhouse should use that team's October rating, not their April rating. Out-of-order processing would inflate or deflate the signal.
Sets with a 25–0 or 0–25 scoreline are treated as forfeits and excluded from ELO processing entirely. Forfeit scores would corrupt the point-differential layer.
Ratings can't drop below 800. This prevents teams from being driven into statistical oblivion after a rough stretch — they stay in the system so future results can still pull them back toward their true level.
The top 48 places of each age group's rankings — the size of the Open field — are reserved for teams that played in the Open division of one of this season's Open qualifiers: JVA The Reckoning, Boys Winter Invitational, SoCal Cup: The Open Championship, JVA Brew City Battle, Nike Steel City Boys Championship or SoCal Cup: The Winter Formal. This matches the rule for Open: a team must play in one of those Open divisions to be considered. Any match counts, rated or not; it is entering the division that qualifies. Each event is confirmed by the committee once its results are in, so a similarly named event can't qualify teams by accident. A team without one is capped just below the band — flagged No Open in the rankings — no matter how high its rating climbs. The rating itself is never altered; only the seeding order. In force from the 2026-27 season; the band size and the qualifier list are committee-tunable settings.
The gate only applies once some team in the age group has played a qualifier's Open division. Until then (early in the season, before the first qualifier) there is nobody to reserve the band for, so the rankings stay in rating order and no team is tagged. The gate also stands aside on any age group with fewer than 48 ranked teams, since early in the season not every team has played yet. A division whose name carries no tier (a plain "16 Boys") is treated as USA.
A team that plays in both 16 Open and 16 Club (or similar) is merged into a single entry in the universal rankings. Their ELO history across the tiers of one age group is stitched together chronologically and their match totals are summed. The merge happens only within an age group — the rule below is its complement.
Every match is rated, including matches between teams of different ages — a 15s team playing in an 18U league, a 12s team in a 14s division. To keep a team that plays up from being penalised for taking on older opponents, the rating formula gives the older team a head start of 20 rating points per year of age difference (capped at 4 years) when it works out the expected result. The ratings themselves are not changed; the offset only sets the expectation, the way home-court advantage works in other rating systems. So a 15s team losing to an 18s team at equal ratings was expected to lose (about a 59% chance for the 18s team), and gives up fewer points than it would to a 15s team; beating that 18s team earns more.
The offset is deliberately small. Fitted on the 2026-27 matches to date, about 20–40 points per year predicts results between adjacent ages best (a 15s team against a 16s team, a 17s team against an 18s). Teams that play two or more years up do better than their age alone predicts — the strong teams are the ones that choose to play up — so a larger offset would over-reward them. Before October 2026, matches across rating pools were recorded but not rated.
The rankings show each team's strength of schedule (SOS): the average rating of every opponent it has played, counting an older opponent higher by the same age offset — a 1500-rated 18s team counts as 1560 for a 15s team. It is there so a team that seeks out the strongest competition gets credit for it in plain view. SOS is context only and never changes the rating or the rank.
A team's age group — the rankings it is listed in — is the age in its USA Volleyball team code: the two digits after the gender letter, so b15sclege1sc is a 15s team. Division labels can't be trusted for this: mixed-age divisions are common (SoCal Cup "16/15" and "18/17", league "16 Boys" divisions that hold 15s) and are labelled by either the older or the younger age, so the division says where a team played, not what age it is. A team without a USAV code (AAU-only events, some tournaments) is listed by the age in its name ("Salt City Boys 17 Salty Reed" is a 17s team; "17/18" lists under the older age), which agrees with the code for 99% of teams that carry one; a name with no age falls back to the age group the team played most of its matches in. Only a real USAV code counts as one: some result systems let organisers type free text into the code field (partial codes, AAU ids, a club name shared by several teams), and that text is ignored so it can never merge two different teams.
A team's home pool — the scale its rating lives on — follows from its age group. Boys are pooled in two-year bands: 11s with 12s, 13s with 14s, 15s with 16s, 17s with 18s; girls by single age. Since every match is rated, the pools are linked through the matches between them, and the age offset applies to any match between different ages, whether or not the pools differ.
The Tournaments page ranks every event in the database with a 0–100 Competitive Index that answers the question: how strong was the field at this tournament?
The score is built on fixed scales: an event's score depends only on who came to it. Adding another event to the database never moves it, and it means the same in October as in June. (Before the 2026-27 season the index ranked events against each other, so the biggest of a handful of early-season events scored "National" by default.)
| Factor | What it measures | Full marks at | Weight |
|---|---|---|---|
| Geographic Reach | Number of distinct states represented | 20 states | 60% |
| Field Size | Number of teams, log-scaled | 500 teams | 40% |
Geography carries the heaviest weight because it's what most separates a genuine national event from a loaded regional. A stacked invitational can have elite top-end teams, but if every team is from the same area it's a regional battle, not a national one. The log scale on size means a 100-team event already earns about 74% of the size marks, so a big field helps but can't stand in for reach.
Why no ratings in the index. The index sets a rating bonus, so it can't be built from the ratings it helps set. And ratings early in a season sit close to the 1500 start, so an Elo-based score would mark every autumn event down whatever its field. Top-10 strength and field depth are still shown on the Tournaments page for reference, but they are not scored.
club_states.js, by team or club name; researched entries cite their source in club_states_sources.json), the state a result system reports for the team, and the team's USA Volleyball region when that region lies inside one state (Northern and Southern California, Arizona, Florida, Great Lakes, Palmetto). Club names repeat across states, so a name match is thrown out when the team's region code rules that state out — a Rocky Mountain team is never counted as the Indiana club that shares its name. The reach factor counts the distinct states represented at the event. A team with no state adds nothing to reach, so keeping the table complete matters; it currently places 92% of this season's teams.
| Tier | Score threshold |
|---|---|
| National | 75+ |
| Super-Regional | 55 – 74 |
| Regional | 30 – 54 |
| Local | < 30 |
tournament_weights.json still apply on top.
The pipeline is deterministic. Given the same input CSV and the same settings file, running the rebuild script produces the same ratings every time. The inputs are:
combined_all_tournaments.csv — raw match results (teams, sets, division, date, source)elo_settings.json — layer weights, K-factors, tier multipliers, thresholdstournament_weights.json — per-event quality multipliersclub_aliases.json — name normalization for clubs with duplicate spellingsFound a bug or have a suggestion? Use the Contribute page to submit an event, flag a duplicate, or share feedback.
Last updated: July 2026