← AcesAPICONTENT HISTORY
WHAT CHANGED
Update to AcesAPI
Snapshot Oct 31, 2026 · 15:03 UTC · version 1.0.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save AcesAPI and check future changes in Following.
Up to 10 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Tennis Elo by surface: how AcesAPI ratings are built, reading overall versus hard, clay and grass ratings, converting a rating gap to a win probability, tracking rating change over time, and building your own surface-weighted blend. Use when the user asks who is best on clay, how likely A is to beat B on grass, how a rating moved, or how to build a surface rating. Do not use for direct meetings: see head-to-head-analysis. Do not use for set or game-count markets: see set-score-modeling.",
"included_files": [],
"name": "surface-elo",
"skill_md_contents": "---\nname: surface-elo\ndescription: \"Tennis Elo by surface: how AcesAPI ratings are built, reading overall versus hard, clay and grass ratings, converting a rating gap to a win probability, tracking rating change over time, and building your own surface-weighted blend. Use when the user asks who is best on clay, how likely A is to beat B on grass, how a rating moved, or how to build a surface rating. Do not use for direct meetings: see head-to-head-analysis. Do not use for set or game-count markets: see set-score-modeling.\"\nmetadata:\n version: 1.0.0\n author: AcesAPI\n---\n\n# Surface Elo\n\n> **Default data tool:** AcesAPI (`acesapi-tool`).\n> `get_ratings` (2 credits) is the leaderboard overall or by surface. `get_player` (5) gives one player's overall and surface ratings with peak. `get_match` (2) gives both players' ratings before and after a match. `get_player_matches` (5 per page) gives `rating_before` and `rating_after` over time.\n\nYou turn ratings into probabilities. The ratings are AcesAPI's own, computed from every completed tour-level singles match since 2007, so they are inspectable and you can check any claim against `get_match`.\n\n## How the Rating Is Built\n\nFrom the `method` field returned by `get_ratings`:\n\n- Elo from every completed singles match on the tour since 2007 (main draw and qualifying), start 1500\n- `K = 250 / (matches + 5)^0.4`, so new players move fast and veterans move slowly\n- The surface rating counts only matches on that surface, so it has its own match count and its own K\n- Walkovers, defaults and retirements are not rated\n- A player is listed once they have at least 10 rated matches and a rated match since `active_since` (default one year ago)\n\n**Consistency check on the 2025 Wimbledon final** (real `get_match` output): Sinner overall 2325 to 2335 (+10), Alcaraz 2278 to 2267 (-11). With the standard 400-point logistic, Sinner's expected score was 0.567, so a +10 change implies K of about 23. The formula gives about 23 for a player with roughly 400 rated matches. The overall rating is therefore consistent with a 400-point logistic scale.\n\n## When to Use\n\n- \"Who is the best clay-court player right now?\"\n- \"What is Sinner's chance against Alcaraz on grass?\"\n- \"How much did that loss cost him?\"\n- \"Is a player's grass rating real or based on 12 matches?\"\n- \"Build a better rating than the overall one\"\n\n## When NOT to Use\n\n- The direct record between the two: see `head-to-head-analysis`\n- Total games, set scores, tiebreak odds: see `set-score-modeling`\n\n## Commands Available\n\n| Command | What It Does | Credits |\n|---------|-------------|---------|\n| `get_ratings` | Leaderboard: `rank`, `rating`, `surface_matches`, `last_match`; filter `surface`, `active_since`, `limit` | 2 |\n| `get_player` | One player: overall rating, per-surface ratings, peak | 5 |\n| `get_match` | `players[].rating`: `before`, `after`, `surface_before`, `surface_after` | 2 |\n| `get_player_matches` | `rating_before`, `rating_after` per match, filter by `surface` | 5 |\n| `get_surface_splits` | `ratings` block with a rating and match count per surface | 3 |\n\n## Commands That Do NOT Exist\n\n| Not Available | Use Instead |\n|--------------|-------------|\n| `get_elo` | `get_ratings` |\n| `get_win_probability` | Compute from the formula below |\n| `get_rating_history` | `get_player_matches` and read `rating_after` in date order |\n| `get_serve_rating` | Not available: no serve data |\n\n## Win Probability\n\nFor ratings `Ra` and `Rb` on the same scale:\n\n```\nP(A beats B) = 1 / (1 + 10^(-(Ra - Rb) / 400))\n```\n\nThis is a single-match probability for the whole match, regardless of format. A 100-point gap is 64 percent, 200 is 76 percent, 300 is 85 percent.\n\n**Worked example (real ratings before the 2025 Wimbledon final, grass):** Sinner surface 1995, Alcaraz surface 2176. Gap -181, so P(Sinner) = 1 / (1 + 10^(0.4525)) = 0.26. The overall ratings (2325 against 2278) said 0.57. The two numbers disagree by 31 points of probability because the grass ratings had few matches behind them. The market's no-vig close was 0.524 for Sinner (Pinnacle, see `odds-explorer`), closer to the overall number. Neither rating alone is the answer: blend them.\n\n## The Scale Pitfall\n\nOverall and surface ratings are on different scales. A surface rating sees fewer matches, so it is less settled, and it starts at the same 1500 baseline with a different number of updates. In the example, Sinner's overall was 2325 and his grass was 1995. Never subtract a surface rating from an overall rating, and never compare two players' ratings from different surfaces. Compare overall to overall, or the same surface to the same surface.\n\n## Blending Overall and Surface\n\nA practical blend that respects small samples:\n\n```\nw = n_surface / (n_surface + c) # c = shrinkage, try 30\nR_blend = w * (R_surface - R_surface_baseline) + (1 - w) * 0 + R_overall\n```\n\nwhere `R_surface_baseline` is the tour average of that surface rating among active players (take it from `get_ratings` with a large `limit`). The idea: start from the overall rating, add the player's surface edge, and trust the edge in proportion to its match count. Fit `c` by walk-forward log loss (see `walk-forward-validation`), not by feel. Report it as a model, because it is.\n\n## Rating Change and Form\n\n`rating_after - rating_before` per match is the update. Over a window it measures results against expectation:\n\n- Sum over the last 10 completed matches = rating form\n- A player who wins a lot at low-rated opponents barely moves; a single loss to a much lower-rated opponent costs a lot\n- Retirements and walkovers have `null` rating fields (real example: a walkover row had `rating_before: null`), so drop them\n\n## Pitfalls\n\n- **Leakage.** Using `rating.after` or `surface_after` to predict that same match. Always use `before`.\n- **Stale ratings.** A player who has not played for months keeps the old rating. `last_match` in `get_ratings` tells you. Returns from injury are the largest errors.\n- **Small surface samples.** `surface_matches` below about 30 means the surface rating is mostly noise. Grass is usually the thinnest because the grass season is short (Alcaraz had 39 grass matches against 121 on clay).\n- **Best-of-five.** Men's Grand Slam matches are best of five, which favors the better player more than best of three. The rating does not know the format, so a gap understates favorites at Slams. Check this with a calibration table (see `probability-calibration`) before relying on it.\n- **Inactive players are filtered.** Players with no rated match since `active_since` and fewer than 10 rated matches are missing from `get_ratings`.\n- **Tour boundaries.** ATP and WTA ratings are separate pools. Never compare a WTA rating to an ATP rating.\n\n## Credit Usage\n\n| Task | Calls | Credits |\n|------|-------|---------|\n| Top 20 on a surface | `get_ratings` | 2 |\n| One match probability, both players | 2 x `get_player` or `get_ratings` for both | 4 to 10 |\n| Rating history for one player, last 50 | `get_player_matches` | 5 |\n| Pre-match ratings for one player's season | `get_player_matches` with `season` | 5 per 50 matches |\n\nFor a model, take pre-match ratings from `get_player_matches` (5 credits per 50 matches) rather than calling `get_match` per row.\n\n## Anti-patterns\n\n| What the Model Might Do | Why It's Wrong | Do This Instead |\n|---------------------|---------------|-----------------|\n| Predict with `rating.after` | Contains the outcome | Use `before` |\n| Compare a clay rating to a hard rating | Different scales | Same surface only |\n| Trust a 12-match surface rating | Noise | Blend with overall, or say it is thin |\n| Quote 76 percent as \"the model says\" without a source | Needs the method and the ratings | Show both ratings and the formula |\n| Mix ATP and WTA ratings | Separate pools | Keep tours apart |\n\n## Output Format\n\n```\n[A] vs [B], [surface]\nOverall: A 2325 B 2278 -> A 57%\nSurface: A 1995 B 2176 -> A 26% (grass sample thin)\nBlend: A xx%\nMarket (no-vig close): xx%\n```\n\n## What to Do Next\n\n| What You Found | Next Action | Skill |\n|----------------|-------------|-------|\n| A rating gap with a thin sample | Look at the direct record | `head-to-head-analysis` |\n| A model-ready rating column | Build features | `feature-engineering` |\n| Probabilities to be trusted | Check calibration | `probability-calibration` |\n"
}SHA-256 of public snapshot: 1bc30aefc5cdb766e3ff31703a036f2afdf7cdbc7d097afd35ffeb9702b5ead0