RotoProphet

Reference

Methods explained

The story of a stat: how a box score turns into a daily label like “Sleeper” and a next-10-games projection — and exactly which numbers do the work at each step.

Every night, ~450 stories land in the database

Every player who steps on the floor produces a box score line the moment the final buzzer sounds — points, rebounds, assists, the usual. On its own, that line doesn't say much. Twenty-four points off a hot shooting night and 24 points off a heavy, ground-out workload look identical in the box score, but they mean very different things for what happens next. This page follows one number through the whole pipeline that tries to tell those two nights apart: from the raw box score, through two extra layers most stat sites never show you (Opportunity and Pressure), into a daily classification, and finally into a projection with an honest range instead of a single confident-looking number.

Chapter 1 — What actually happened

The Performance family of metrics is the box score cleaned up: raw counts normalized so you can compare a starter's 30 minutes to a bench player's 15, or a slow-paced defensive team to a track meet. It answers “what happened,” not “why” — that distinction matters later, because it's exactly the piece this family can't provide on its own.

Per-36
X × (36 / minutes)
Removes playing time as a variable.
Per-100 possessions
X × (100 / possessions)
Removes pace as a variable.
eFG% / TS%
Shooting efficiency, adjusted for three-point value (eFG%) or all scoring including free throws (TS%).
PTS/POSS, FTr, 3Rate
pts/poss · FTA/FGA · 3PA/FGA
Scoring rate and shot-diet shape.
REB/CH
rebounds / rebound_chances_total
Conversion, not volume — see Chapter 2.

Every one of these lives in public.player_performance_game, and every one of them is outcome-only. That becomes important below: this is the one family of metrics that plays no part in the 9-Cat system.

Chapter 2 — Why it happened: opportunity

Two players can score the same 24 points from completely different places: one got there on volume — touches, shots, trips to the line — the other on efficiency alone. Opportunity metrics measure the raw input — how many touches, shots, and rebound chances a player actually got — independent of whether they converted any of it. This is the layer that starts to answer “why,” and it's the first of the two families that feed directly into the 9-Cat system.

Scoring Attempts
FGA + 0.44 × FTA
How many times a player tried to score.
OOR — Offensive Opportunity
touches + 0.8·scoring_attempts + 0.6·passes (per min)
SOR — Scoring Opportunity
scoring_attempts per minute
AOR — Assist Opportunity
passes + 0.5·touches + secondary_assists (per min)
A flags-layer index. The PROJECTION no longer uses it — see the assist chain below.
Potential Assists
passes that led to a shot attempt, per 36
What assists are actually made of. Replaced plain passes in 2026-08: everyone passes, but the shot-creating pass is the one players compete for.
Possession Time
minutes per 36 with the ball in his hands
A team budget — its total varies less across the league than shot attempts do.
Creation Skill
potential assists ÷ possession minutes
How well he turns ball-time into a shot for someone else. Draymond Green 2.90, Jalen Brunson 1.76.
Assist Conversion
assists ÷ potential assists
How often a created shot actually falls.
3POR — 3PT Opportunity
3PA per minute
ROR — Rebound Opportunity
rebound_chances_total per minute
Chance-based, not makes.
DPOR — Defensive Opportunity
deflections + contested_shots (+ matchup possessions)
Involvement without using steals/blocks, which are outcomes.

Each of these also has an _abs (volume) and _norm (share of the team, adjusted for minutes played) version:

_norm (any opportunity metric)
(player share of team total) / minutes_share
Around 1.0 = exactly as expected for the minutes played.

The normalized versions are what make it possible to fairly compare a 15-minute bench player's opportunity against a starter's — and they're the reason the system can say “this bench player is getting more than their share” before it ever shows up as a big per-game number.

Chapter 3 — Who else is eating: pressure

Opportunity isn't just about one player — it's a shared resource on a 5-man floor. Pressure quantifies how much teammates in a similar role are “crowding” that resource, which is what lets the system reason ahead of time about who actually benefits when a high-usage teammate goes down, instead of guessing.

Role vector
[touches_share, passes_share, possessions_share, usage_share]
A player's shape of involvement.
Crowdedness (HHI / entropy)
Whether teammates' share of a resource is concentrated in one or two players (lumpy) or spread thin (diffuse).
Peer-overlap pressure
w_ij = cosine(role_i, role_j); weighted share of teammates most similar to you
High = 'players like me already have this.'
Teammate-take
1 − player_share
How much of a resource all teammates combined are taking, e.g. minutes_teammate_take.

This is the second family that feeds directly into the 9-Cat system — specifically, it's what powers the daily Injury beneficiary and Role decline labels in Chapter 5.

Chapter 4 — Not overreacting to a hot week

A player who goes 3-for-4 from three on a Tuesday isn't suddenly a 75% shooter, and a system that reacted as if they were would be worse than useless. Every conversion rate is shrunk back toward that player's baseline, by an amount that depends on how much recent volume actually backs the new number up:

Shrinkage formula
adjusted = (volume·recent_rate + k·baseline_rate) / (volume + k)
More recent volume ⇒ trust the new number more. More k ⇒ trust the baseline more.

k isn't a guess — it's measured directly from 9 seasons of historical data as “how much volume until this category stops being mostly noise.” Points stabilize fast, needing only about 10 games' worth of volume; turnovers and steals are noisier and need roughly 40. The baseline itself comes from a fallback chain built for exactly this: a career-weighted average of the last 3 seasons first, a player's own first 20 games this season if they have no NBA history yet, and a historical rookie-cohort average (same draft slot, same position) as the last resort. A trade resets the clock — a player's “current typical” becomes their new-team stint, not a blend with their old team.

Rolling windows
Last 5 / 10 / 15 / 20 games (pooled, not averaged), plus a 10-game half-life recency-weighted average.
Primary window
15 games — the main 'recent form' read every signal below compares against.

Chapter 5 — Turning signals into a label

Every day, each of the 9 categories gets a handful of these stabilized signals compared against a threshold — and that threshold stretches wider for noisier categories, so a flag means roughly the same level of confidence whether it's attached to rebounds or free-throw percentage.

z_volume / z_volume_career
Recent opportunity vs. season baseline, or vs. career baseline (catches a slower breakout).
z_conversion
The stabilized rate above vs. baseline — real skill change, not a streak.
z_minutes / z_minutes_career
Same idea, applied to blowout-adjusted minutes.
teammate_take_z
Elevated volume specifically cross-referenced against a currently-out teammate.
vacated_opportunity_z
Team-wide unclaimed opportunity from injuries — can flag someone before it shows up in the box score.
pressure_delta
Change in how crowded a role is, recent vs. baseline.
trend_direction
Whether the last 5 games run above, below, or in line with the last 15.

If enough of these clear the bar in the right combination, a category earns one of 7 flags:

Sustainable riser

Real volume growth backed by more minutes, conversion in line.

Sleeper

Low competition for touches, or unclaimed opportunity — hasn't broken out yet.

Injury beneficiary

Elevated volume tied to a currently-out teammate — may regress on their return.

Buy low

Conversion running cold with opportunity intact — due for positive regression.

Sell high

Conversion running hot without the volume to back it up — expect it to cool.

Role decline

Crowding up or opportunity down despite stable minutes — a real usage cut.

Stable

No notable deviation from baseline.

Every flag travels with a strength (Strong / Moderate / Weak — how decisive and reliable the signal is) and a confidence score (how much sample backs it). Injury beneficiary is never the headline — it always shows as a caveat, since it depends on someone else staying hurt. In the Scanner, a player's flagged categories are rolled into one Overall label by weighted vote, heaviest group wins.

Chapter 6 — What comes next: the projection

Seven of the nine categories get a precise next-10-games number with a range around it. Steals and made threes get a range only, tagged Range only in the player view: a held-out validation test (never-seen-the-future data) showed neither stat supports a reliable single number — steals is just a noisy category by nature, and made threes multiplies three independently-uncertain factors (minutes, attempt rate, shooting%), which compounds noise faster than it can be averaged away. Rather than hide them, the range itself is separately checked against real outcomes and widened until it actually captures the true value about as often as it claims to.

Volume categories (PTS, REB, BLK, TOV)
value = projected minutes × per-minute rate × stabilized conversion
The 10th–90th percentile range comes almost entirely from how uncertain the minutes projection is.
Assists
minutes × possession time × creation skill × assist conversion
Rebuilt 2026-08-05. Four published numbers instead of one: how much ball he gets, how well he creates with it, and how often that becomes an assist. Each is projected separately and each is on the site.
Percentage categories (FG%, FT%)
value = stabilized conversion rate, range = ±4 percentage points
A fixed band, not tuned per player.
Range-only categories (STL, 3PM)
same formula as the volume categories, range widened ~2.2×
Checked against real history: the true value should land inside the range about 80% of the time. There is no separate point estimate worth trusting for these two.

Projected minutes come from their own small model: recent form, a discount for back-to-backs, a bump if a same-position teammate is currently out (capped, and fading the further into the 10 games it projects, since absences rarely last the whole window), and a ceiling based on rotation size.

Scoring, rebuilt — August 2026

Points used to be one blended number: chances to score, times points per chance. That hid two different skills inside one figure. A player who shoots well and a player who lives at the free-throw line are doing different things, and a single conversion rate cannot tell them apart — nor can it be smoothed correctly, because the two need very different amounts of evidence before they are worth believing. Shooting percentage takes roughly six times as many attempts to settle as free-throw percentage does.

Points, from August 2026
2 × eFG% × projected shots + FT% × projected trips
Two skills, each smoothed on its own evidence. A free throw is worth a point, not the 0.44 a possession-counting constant had it at.
Projected shots (new, published)
last season's rate × a roster adjustment
Crowding from teammates who shoot, an allocation that conserves the team's shots, and a projected team total. Now its own number on the site.
Projected trips to the line (new)
foul-drawing history + rim share + projected shots + its own crowding
Crowded by teammates who draw fouls, which is a different set of players from the ones who shoot.

The shot-attempt model is the part that matters most: it is worth around three quarters of everything the points projection has gained. It is also the most heavily tested thing here. Several stronger-looking versions were discarded — one where five random ranking keys scored exactly as well as the real one, and another whose arrival and departure terms came out with textbook opposite signs before turning out to be measuring minutes rather than shots. What survived was checked by rebuilding it from rebounding, a skill nobody competes with a shooter for; that version cannot beat the baseline at all, which is what tells you the real one is measuring competition for shots and not simply roster upheaval.

One thing the model does well and one it does not. When a high-volume shooter arrives, it moves every teammate down correctly — on one such roster it moved all eleven the right way and cut the error by 28%. When a shooter leaves, someone takes those shots and nothing tested so far identifies who: ten different approaches have failed at it. So a projection for a player whose team just lost a scorer deserves less confidence than one for a player whose team just gained one, and that asymmetry is real rather than a hedge.

A player with no games logged yet this season — an unplayed season, a not-yet-debuted rookie, a fresh trade — is tagged Preseason and projected from last season's own numbers instead (falling back to a career average only when there is no prior season at all), with a visibly wider range to reflect the extra uncertainty.

How it all connects

Nothing above is recomputed from scratch at each step — the 9-Cat system reuses Opportunity and Pressure as its raw inputs. Each category is wired to exactly one opportunity column (its volume signal) and one pressure column (its crowding signal); its “conversion” is the genuine box-score outcome divided by that same opportunity count — a stricter, chance-adjusted version of “shooting/passing/rebounding well” than a raw percentage.

CatOpportunity inputPressure inputConversion (skill rate)
PTSsor_p36 — Scoring Attemptspressure_v2_scorescon = points / scoring_attempts
REBreb_ch_p36 — Rebound Chancespressure_v2_rebchrcon = rebounds / rebound_chances_total
ASTpass_p36 — Passespressure_v2_aoracon = assists / passes
STLdeflections_p36pressure_v2_stl_oppstl_conv = steals / deflections
BLKcontested_p36pressure_v2_blk_oppblk_conv = blocks / contested_shots
TOVtouches_p36pressure_v2_touchtov_per_touch = turnovers / touches
3PMthree_opp_p36 — 3PT Opportunitypressure_v2_threefg3_pct = 3PM / 3PA
FG%uncontested_fga_share — shot-mix qualitypressure_v2_scorefg_pct = FGM / FGA
FT%fta_pg — FT attempts/gamepressure_v2_scoreft_pct = FTM / FTA

What does NOT feed the 9-Cat system: Chapter 1's Performance metrics. They live in a separate table (public.player_performance_game) the flags/ projection pipeline never reads — a parallel, outcome-only lens on the same games, good for browsing box scores, not part of how a flag or projection is calculated.

Backing tables: public.player_opportunity_game, public.player_opportunity_pressure_game, public.boxscore_traditional_v3 roll up into public.player_window_agg, which feeds public.player_cat_flags and public.player_cat_projection. All recompute daily.