ELO Tier System — Cross-Rating Mapping Specification
Status: Active
Last updated: 2026-06-21
Related: elo-rating.md · progression-system.md · international-data-model.md
Design Principles
- Honest mapping — Korean 동호인 levels are mapped conservatively to NTRP/UTR. No inflation.
- Dense where it matters — 80% of Korean recreational players sit in NTRP 2.5–4.5. The ELO range is dense there.
- Headroom above — The scale extends to NTRP 7.0 / UTR 16 for international expansion and 선수출신, even though current users will never reach it.
- Sub-tiers for granularity — Each tier has III (bottom), II (mid), I (top) sub-tiers. Players see incremental progress within a tier.
- ELO is relative — It measures you against the people you play. The absolute number only matters for tier placement.
- Rating integrity — A self-assessed seed is a provisional suggestion, never a verified rating. Competitive gating applies only to players with a trusted, play-verified rating.
The 5-Tier System
Single source of truth
The canonical definitions live in packages/app/src/domain/rules/tier.config.ts (TIER_DEFINITIONS array and EloTier type). The server-side cut points mirror this in private.tier_index_from_elo / private.tier_from_elo (migration 00235_tier_at_match_time.sql). Both use the same four boundaries: 900 / 1400 / 2000 / 2500. There are exactly five tiers: bronze | silver | gold | master | grandmaster.
There is no platinum tier and no diamond tier. Those names appeared in an earlier draft; they were removed before launch (migrations 00151 + 00229 pin the 5-tier CHECK constraint on
profiles.display_tierandclubs.min/max_tier). Any documentation or code that references platinum or diamond describes a dead state.
Step 1: The Full ELO Range
Scale Design
| Zone | ELO Range | Who Is Here | Notes |
|---|---|---|---|
| Floor | 400–900 | Bronze — theoretical minimum to Silver entry | True beginners. Almost nobody stays near 400 long. |
| Korean 동호인 bulk | 900–2000 | 95%+ of Korean recreational players | Silver through Gold — the active zone for the Korean market. |
| Korean ceiling | 2000–2500 | Best 동호인 + strong 선수출신 | 오픈부 champions, former junior players |
| International competitive | 2500–2900 | College players, Futures/Challengers, strong club players | NTRP 6.0–6.5, UTR 11–15 |
| Professional | 2900+ | ATP/WTA tour level | NTRP 7.0, UTR 15+. Theoretical ceiling. |
Why This Range
- Floor at 400: With K=32 (K=40 provisional), a player who loses every match drops roughly 16–20 points per loss against equal opponents. After 30 straight losses from 1000 they land near 400–500. Realistic floor.
- Korean bulk at 900–2000: Silver through Gold covers the dense NTRP 3.0–4.5 block where nearly all active Korean players converge.
- Center of mass at ~1100–1400: Where most active Korean players settle after 20+ matches (신인부 to 오픈부 하위 range).
Step 2: The Tier Table
Korean Reality Check (Before Mapping)
| Korean Division | What It Actually Means | Honest NTRP | Why |
|---|---|---|---|
| 테린이 / 입문 | Can barely rally. Serves are push-serves. | 1.5–2.0 | Still learning basic contact |
| 신인부 하위 | Can rally at slow pace. Double-faults often. | 2.0–2.5 | Inconsistent groundstrokes |
| 신인부 | Steady rally. Basic serve goes in. Can play a set. | 2.5–3.0 | The "finally playing tennis" moment |
| 신인부 상위 ~ 오픈부 하위 | Directional control. Starting to use spin. Net approaches. | 3.0–3.5 | Where most active 동호인 players sit |
| 오픈부 | Good topspin. Reliable serve with some pace. Tactical play. | 3.5–4.5 | Club tournament winners |
| 오픈부 상위 | Weapon shots. Controls points. Tournament level. | 4.5–5.0 | Top 5–10% of 동호인 |
| 선수출신 (banned from most 동호인) | Former registered competitive players | 5.0–6.0+ | Literally banned because the gap is too large |
Note on terminology: Korean 동호인 tennis uses 신인부/오픈부/챌린저부/마스터즈부 (KATA/KATO system). D조/C조/B조/A조 is the badminton division system — not tennis. We use 신인부/오픈부 as the universally understood labels.
The Complete Tier Table
| Tier | Sub | ELO Range | NTRP | UTR | Korean Division | % KR Players | Description |
|---|---|---|---|---|---|---|---|
| Bronze | III | 400–567 | 2.0 | 1–2 | 테린이 | <1% | First time holding a racquet. Learning grip and contact. |
| Bronze | II | 567–734 | 2.0–2.5 | 2–3.5 | 테린이 | 2% | Can make contact but rallies end in 2–3 hits. Push serve. |
| Bronze | I | 734–900 | 2.5–3.0 | 2–3.5 | 테린이 / 입문 | 5% | Slow rallies work. Basic forehand exists. Double-faults most serves. |
| Silver | III | 900–1067 | 3.0 | 3.5–4.5 | 신인부 하위 | 10% | Medium-pace rallies. Serve goes in sometimes. Backhand is weak. |
| Silver | II | 1067–1234 | 3.0–3.5 | 3.5–4.5 | 신인부 | 15% | Consistent medium rallies. Flat serve. Starting to play sets. |
| Silver | I | 1234–1400 | 3.5 | 3.5–4.5 | 신인부 상위 | 15% | Rally with direction. Serve rarely double-faults. Basic volleys. |
| Gold | III | 1400–1600 | 3.5–4.0 | 4.5–6.0 | 신인부 ~ 오픈부 하위 | 18% | Directional control both sides. Starting topspin. Net approaches. |
| Gold | II | 1600–1800 | 4.0–4.5 | 5.0–7.0 | 오픈부 하위 | 15% | Reliable topspin. Second serve has spin. Tactical awareness. |
| Gold | I | 1800–2000 | 4.5–5.0 | 6.0–7.5 | 오픈부 | 10% | Good all-court player. Club tournament competitive. |
| Master | III | 2000–2167 | 5.0–5.5 | 7.5–10 | 오픈부 상위 / 선수출신 | 5% | Weapon shots. Controls points. Top 동호인 territory. |
| Master | II | 2167–2334 | 5.5–6.0 | 10–13 | 선수급 | <1% | Near ceiling of Korean recreational tennis. |
| Master | I | 2334–2500 | 6.0–6.5 | 11–14 | 선수급 상위 | ~0% | Former competitive players. Strong college level (US). |
| Grand Master | III | 2500–2700 | 6.5 | 13–15 | — | ~0% | Futures/Challenger level. International expansion tier. |
| Grand Master | II | 2700–2900 | 6.5–7.0 | 14–16 | — | ~0% | Low professional. ATP Challenger regulars. |
| Grand Master | I | 2900+ | 7.0+ | 15+ | — | ~0% | ATP/WTA Tour. Theoretical ceiling. |
Distribution Summary (Korean 동호인 Only)
Bronze III–I (400–900): ~7% — 테린이/입문
Silver III–I (900–1400): ~40% — 초급~중급 하위 ← LARGEST GROUP
Gold III–I (1400–2000): ~43% — 중급~오픈부 ← where the bulk actually are
Master III–I (2000–2500): ~9% — 오픈부 상위 / 선수출신
Grand Master (2500+): ~1% — international expansion / pro-levelThe median Korean 동호인 player is Silver II–III (~1000–1200 ELO, ~NTRP 3.0). Gold is intentionally wide (600 ELO, three equal 200-ELO sub-bands) to represent the dense NTRP 3.5–5.0 cluster that makes up the majority of active club players.
Step 3: Honesty Checks
Check 1: Is Korean Silver really NTRP 3.0–3.5?
YES. Korean 신인부 players:
- Can rally at moderate pace on forehand
- Backhand is often a slice or inconsistent two-hander
- Serve is flat, moderate pace, ~50% in
- No meaningful spin, no net game
This is textbook NTRP 3.0 (can sustain rally) to 3.5 (beginning to direct shots). An American NTRP 3.0 self-rating often means "I took lessons for a year." A Korean 신인부 regular is comparable.
Check 2: Is Korean Gold really NTRP 3.5–5.0?
YES. Korean 오픈부 players at the low end:
- Consistent forehand with direction and developing topspin
- Second serve exists but is weak
- Starting to approach net
- Maps to NTRP 3.5–4.0
At the high end of Gold (오픈부 / 오픈부 상위):
- Has weapon shots (big forehand, reliable serve with pace)
- Controls points and works the court
- Plays with spin and variety
- Maps to NTRP 4.0–4.5
Gold I (오픈부, 1800–2000) reaches NTRP 4.5–5.0. The temptation is to call 중급 players NTRP 4.0+ early, but that is inflation. NTRP 4.0 means "uses pace AND spin as weapons, has dependable overhead, approach shots." Most 오픈부 하위 players are not quite there.
Check 3: Is Korean Master really beyond 선수출신?
YES. Master III–I (2000–2500) covers the level where former registered competitive players (선수출신) converge. They seed at ~2100 and climb quickly. Master I and Grand Master are effectively international-expansion territory — very few Korean recreational players will ever reach them.
Check 4: Does the ELO range give enough resolution?
With sub-tiers of ~167–200 ELO each:
- K=32 means a win against an equal opponent gives +16 points
- Sub-tier promotion takes ~10–12 wins against equal opponents
- Full tier promotion from Silver to Gold takes ~25 wins against equal opponents
- This feels right for recreational play (1–2 matches per week = tier progress every 2–4 months)
Check 5: Can 선수출신 coexist without breaking the scale?
YES. 선수출신 seed at Master III (~2100). They quickly climb to Master II–I through ELO convergence. They are separated from the 동호인 bulk by 700–1000 ELO points, which means a Master I player has roughly 3–5% expected win rate against a Silver II player. That accurately reflects reality.
Step 4: Onboarding Seed ELOs
Seed ELOs represent where a player starts before any play-verified matches. They are a provisional placement, not a verified rating. See the rating integrity section below for how rating_state and rating_source gate what these seeds can unlock.
Korean Onboarding
| Self-Assessment | Korean Label | Seed ELO | Starting Tier | Reasoning |
|---|---|---|---|---|
| 완전 입문 (Complete Beginner) | "테니스를 막 시작했어요" | 500 | Bronze III | First time on court. Converges quickly. |
| 입문 (Beginner) | "기본 그립과 공 접촉이 돼요" | 750 | Bronze II/I | Learning basic contact. Slow rallies. |
| 초급 (Lower-Intermediate) | "기본 스트로크와 서브가 돼요" | 1000 | Silver III | Can rally and serve. Most common 동호인 entry point. |
| 중급 (Intermediate) | "랠리하며 경기를 즐겨요" | 1300 | Silver I | Directional play. Playing sets regularly. |
| 상급 (Advanced) | "대회 출전 경험이 있어요" | 1700 | Gold II | Club tournament players. Strong all-court game. |
| 선수출신 (Former Competitive) | "선수 등록 경험이 있어요" | 2100 | Master III | Former registered players. Converges quickly upward. |
Default ELO for new profiles: 1000 (Silver III floor) — the single most common Korean 동호인 entry level, and the median after convergence for self-described 초급 players.
International Onboarding
| NTRP Self-Rating | Seed ELO | Starting Tier | Notes |
|---|---|---|---|
| NTRP 2.0 | 600 | Bronze III/II | True beginner, basic contact |
| NTRP 2.5 | 800 | Bronze I | Can rally slowly |
| NTRP 3.0 | 950 | Silver III | Consistent rally, basic serves |
| NTRP 3.5 | 1300 | Silver I | Developing direction and spin |
| NTRP 4.0 | 1600 | Gold III | Good all-court game |
| NTRP 4.5 | 1900 | Gold II/I | Strong club-tournament level |
| NTRP 5.0 | 2100 | Master III | College-level background |
| NTRP 5.5 | 2250 | Master II | Strong college / post-college |
| NTRP 6.0 | 2400 | Master I | Futures/Challenger level |
| NTRP 6.5–7.0 | 2600 | Grand Master II | Tour-level |
| UTR Self-Rating | Seed ELO | Starting Tier | Notes |
|---|---|---|---|
| UTR 1–2 | 600 | Bronze III | Beginner |
| UTR 2–3.5 | 800 | Bronze I | Basic recreational |
| UTR 3.5–4.5 | 1000 | Silver III | Club player, 신인부 |
| UTR 4.5–6.0 | 1400 | Gold III | 오픈부 하위 |
| UTR 6.0–7.5 | 1700 | Gold II | Strong club |
| UTR 7.5–10 | 2050 | Master III | 오픈부 상위 / 선수출신 |
| UTR 10–13 | 2300 | Master I | Former competitive |
| UTR 13–15 | 2600 | Grand Master II | College/low professional |
| UTR 15+ | 2900 | Grand Master I | Tour level |
Step 5: Rating Integrity — rating_state, rating_source, and display_tier_sub
These three fields, added to profiles as part of the WS-3 foundation (see docs/architecture/international-data-model.md §5), govern what a rating means and what it can unlock. They are the anti-sandbagging layer.
rating_state
| Value | Meaning | Match-delta behaviour |
|---|---|---|
unrated | No play-verified matches yet (newly seeded). | Results stored; opponent ELO delta = 0. |
provisional | 1–9 play-verified matches. Rating is converging. | Normal K-factor, lower confidence display. |
established | ≥10 play-verified matches. High-confidence rating. | Normal K-factor. Full competitive gating. |
An unrated player's score is stored so their own rating can converge, but it does not count against calibrated opponents until the new player has played enough to be trusted (UTR's "no impact until 3 results" principle extended here). This prevents a brand-new player with a high self-assessed seed from deflating an experienced opponent's rating on their first match.
rating_source
| Value | What It Means | Tier badge displayed? |
|---|---|---|
self_assessed | Player chose their level during onboarding; no matches played. | No — shows "미측정" with tap-explain. |
intra_club | Rating derived from matches within a single club. | Yes — with a confidence indicator. |
inter_club | Rating derived from cross-club or pickup matches. | Yes — stronger trust signal. |
verified_tournament | Rating anchored by an external verified tournament result. | Yes — highest trust. |
Key rule: self_assessed ratings never gate competitive RSVP enforcement (rsvps_enforce_tier). A player who says "나는 상급이에요" and seeds at 1700 Gold II is shown a provisional badge, but a Gold-and-above club CAN still accept them — the club has no verified evidence they are NOT Gold. Only a player with intra_club+ source and provisional+ state can be confidently barred from a Bronze-only club on the basis of their ELO alone. This is the USTA self-rated NTRP lesson: self-ratings inflate ~0.5 NTRP on average, so treating them as verified leads to sandbagging and mismatched matches.
display_tier_sub
Stored as 'I' | 'II' | 'III' (or NULL). Derived from the player's ELO position within their tier band using the sub-tier boundaries in TIER_DEFINITIONS. The sub-tier is displayed alongside the tier badge (e.g. "Silver II") and is used by the roster display so viewers can quickly scan player-pool compatibility. It is recalculated on every elo_history write; the stored column is a denormalized display cache.
The sub-tier is a granularity tool inside a wide tier. Gold spans 600 ELO (NTRP 3.5–5.0) — an enormous practical skill range. Gold III (1400–1600) and Gold I (1800–2000) are very different players; the sub-tier makes that visible without splitting the tier into separate competitive brackets.
Step 6: Korean Points System (1.0–9.0)
Korean 동호인 communities use a 9-point informal scale. Here is the mapping to the 5-tier ELO system:
| Points | ELO Range | NTRP | Level | Division |
|---|---|---|---|---|
| 1.0 | 400–567 | 2.0 | 완전 초보 | — |
| 2.0 | 567–734 | 2.0–2.5 | 테린이 | — |
| 3.0 | 734–900 | 2.5–3.0 | 입문 | 입문부 |
| 3.5 | 900–1067 | 3.0 | 초급 하위 | 신인부 하위 |
| 4.0 | 1067–1234 | 3.0–3.5 | 초급 | 신인부 |
| 4.5 | 1234–1400 | 3.5 | 중급 하위 | 신인부 상위 |
| 5.0 | 1400–1600 | 3.5–4.0 | 중급 | 오픈부 하위 |
| 5.5 | 1600–1800 | 4.0–4.5 | 중상급 | 오픈부 |
| 6.0 | 1800–2000 | 4.5–5.0 | 상급 | 오픈부 상위 |
| 6.5 | 2000–2167 | 5.0–5.5 | 상급 상위 | 선수출신 초입 |
| 7.0 | 2167–2334 | 5.5–6.0 | 선수출신 | 선수급 |
| 7.5 | 2334–2500 | 6.0–6.5 | 선수출신 상위 | — |
| 8.0+ | 2500+ | 6.5+ | 프로급 | — |
Formula: koreanPoints = Math.min(9.0, Math.max(1.0, Math.round((elo - 400) / 200 * 2) / 2 + 1.0))
Each 100 ELO ≈ 0.5 Korean points, starting from 1.0 at ELO 400.
International Benchmarks (Real Data Points)
Korean Professional Anchors
| Player / Context | Peak ATP/WTA | Est. UTR | Est. NTRP | Approx. ELO |
|---|---|---|---|---|
| Hong Seong-chan (Korea #1 men) | ATP ~#141 | 13–14 | 6.5–7.0 | ~2700 |
| Ku Yeon-woo (Korea #1 women) | WTA ~#198 | 10–11 | 5.5–6.0 | ~2400 |
| Korea ATP #10 | ATP ~1000 | 9–10 | 5.5–6.0 | ~2300 |
| Korean Futures circuit player | ATP 1000+ | 10–12 | 5.5–6.5 | ~2300–2600 |
Korean Amateur Anchors
| Profile | NTRP (US-assessed) | UTR | WTN | Approx. ELO |
|---|---|---|---|---|
| 테린이 (6 months in) | 2.0–2.5 | 1–3 | 35–40 | ~700 |
| Median club player (3–5yr, 테춘기) | 3.0–3.5 | 4–5 | 22–28 | ~1100–1300 |
| 13-yr veteran (US coach assessed) | 4.0–4.5 | 5–7 | 17–23 | ~1600–1700 |
| 신인부 우승자 | 4.0–4.5 | 5–7 | 17–22 | ~1650–1750 |
| 오픈부 우승자 | 4.5–5.0 | 7–9 | 10–15 | ~1900–2100 |
| 슈퍼급 | 4.5–5.0 | 8–10 | 8–12 | ~2000–2100 |
| 선수출신 (former registered) | 5.0–5.5 | 9–11 | 5–10 | ~2100–2250 |
Key Findings
- Korean amateur self-assessment inflates ~0.5 NTRP vs US USTA standards
- The entire 동호인 spectrum (테린이 → 슈퍼) = NTRP 1.5–5.0
- ~75% of Korean recreational players are NTRP 3.0–4.5 (our Silver–Gold range)
- Korea is an ITF WTN partner but no bulk amateur WTN data is publicly available
- Korea has zero players inside ATP/WTA Top 100 as of 2026
NTRP Distribution (US reference for calibration)
- NTRP 3.5: 28% of USTA league players (largest group)
- NTRP 4.0: 21%
- NTRP 4.5: 6%
- NTRP 5.0: <1%
Sources: DC Inside tennis gallery, Brunch (Korean NTRP comparison), Tennis Explorer, KATA/KATO, ITF WTN
Appendix A: Visual Distribution
ELO 400 567 734 900 1067 1234 1400 1600 1800 2000 2167 2334 2500 2700 2900
| | | | | | | | | | | | | | |
Tier ├──── Bronze ────────┤───────────── Silver ─────────┤─────────────────── Gold ──────────────────────┤─── Master ────┤─── Grand Master ──→
│ III │ II │ I │ III │ II │ I │ III │ II │ I │ III │ II │ I │ III │ II │ I
NTRP 2.0 2.0-2.5 2.5-3.0 3.0 3.0-3.5 3.5 3.5-4.0 4.0-4.5 4.5-5.0 5.0-5.5 5.5-6.0 6.0-6.5 6.5 6.5-7.0 7.0+
│ │ │
│ 테린이/입문 │ 신인부~오픈부 하위 │ 오픈부 상위 / 선수출신
│ │ │
├────── ~7% ─────────────────┼──────────────── ~83% ───────────────────────┼────── ~9% ──────┤~1%
│ │ │
│ Bronze │ Silver + Gold (the Korean market core) │ Master + GMAppendix B: Cross-System Conversion Functions
These functions live in packages/app/src/domain/rules/tier.config.ts and are re-exported from @twomore/app. Use the exported versions; never inline the math in feature code.
/** Convert NTRP to approximate ELO seed (use for onboarding suggestion only). */
function ntrpToElo(ntrp: number): number {
// NTRP 1.5 → 500, NTRP 7.0 → 2800
// slope = (2800 - 500) / (7.0 - 1.5) ≈ 418 per NTRP point
return Math.round(500 + (ntrp - 1.5) * 418);
}
/** Convert UTR to approximate ELO seed (use for onboarding suggestion only). */
function utrToElo(utr: number): number {
// UTR 1 → 500, UTR 16 → 3100
// slope = (3100 - 500) / (16 - 1) ≈ 173 per UTR point
return Math.round(500 + (utr - 1) * 173);
}
/** Convert ELO to approximate Korean points (1.0–9.0 scale). */
function eloToKoreanPoints(elo: number): number {
const raw = (elo - 400) / 200 + 1.0;
return Math.min(9.0, Math.max(1.0, Math.round(raw * 2) / 2));
}The helpers eloToNtrp, eloToUtr, eloToKoreanDiv, and eloToKoreanWomenDiv also live in tier.config.ts and map ELO to human-readable range strings aligned with the 5-tier boundaries above.
Appendix C: Validation Against Known References
| Real-World Player | Expected NTRP | Our ELO | Our Tier | Passes? |
|---|---|---|---|---|
| Korean 테린이 (6 months in) | 2.0 | ~700 | Bronze II | Yes |
| Korean 신인부 regular | 2.5–3.0 | ~1000 | Silver III | Yes |
| Korean 신인부 상위 | 3.5 | ~1300 | Silver I | Yes |
| Korean 오픈부 하위 | 3.5–4.0 | ~1500 | Gold III | Yes |
| Korean 오픈부 winner | 4.5 | ~1900 | Gold I/II | Yes |
| Korean 슈퍼 champion | 4.5–5.0 | ~2050 | Master III | Yes |
| Korean 선수출신 (strong) | 5.5 | ~2250 | Master II | Yes |
| American club 4.0 | 4.0 | ~1600 | Gold III | Yes |
| American college D3 | 5.0–5.5 | ~2200 | Master II | Yes |
| American college D1 | 6.0–6.5 | ~2450 | Master I | Yes |
| ATP top 500 | 7.0 | ~2800 | Grand Master I | Yes |
Appendix D: Gender-Adjusted Display Tier
Deprecated (2026-07): the tier is now pure-skill / gender-neutral (owner decision); gender-awareness lives in the leaderboards, not the tier.
getGenderAdjustedTier()/eloToKoreanWomenDiv()are retained for reference only. Canonical model: rating-system-v2 §0.
The raw ELO is always gender-neutral (used for matchmaking). For display purposes the app can adjust the shown tier so that a woman at the 60th percentile of women and a man at the 60th percentile of men both display the same tier — even if their raw ELO differs (men's pool tends to be denser at higher ELO early in the app's lifecycle).
This is implemented via getGenderAdjustedTier(elo, gender, genderStats, combinedStats) in tier.config.ts, which applies a z-score re-mapping. Key constraints:
- The adjustment activates only once the gender pool reaches ≥50 players (
MIN_POOL_SIZE_FOR_ADJUSTMENT). Until then, all genders use raw ELO tiers. - Matchmaking always uses raw ELO regardless of display adjustment.
- The
display_tier_subcolumn onprofilesreflects the adjusted sub-tier, not the raw one.
Women's Korean division labels (개나리부 / 국화부 per KATA/KATO) are rendered via eloToKoreanWomenDiv(elo) alongside the standard labels where gender-specific display is shown.