Methodology

Every number here is an estimate of something, made from public box scores. This page says what each one estimates, how it is estimated, what that rests on, and where it breaks — including the places we know it is weakest.

New to this?

Read the grey summary under each heading and skip the blue formula boxes — turn them off below if they are in the way. Sections 6 to 9 are the ones that explain why a number looks odd in October, during the NBA Cup, or in the playoffs.

Reviewing the methodology?

Estimators and their assumptions are in the formula boxes; the adjustment model is section 10 and the shrinkage in section 11. Every known defect we have not fixed is in section 12, and section 14 says how to reproduce any published figure.

Window: 10 gamesSeasons: 2025-26, 2024-25, 2023-24, 2022-23, 2021-22, 2020-21, 2019-20, 2018-19, 2017-18, 2016-17Phases: Regular Season + PlayIn + PlayoffsUpdate: nightly

01Data and scope

Official box scores for every team in every game, across the regular season, play-in, and playoffs, across three seasons. Nothing else goes in — no proprietary inputs, no hand adjustments, no projections.

The unit of observation is one team's performance in one game. Two kinds of measurement go into it: counting statistics taken straight from the box score, and the per-possession ratings published by the league, which express scoring relative to how many possessions a team used. The second matters because teams differ in pace — a fast team scores more points without being more efficient, and per-possession figures remove that difference.

Points allowed are not a separate measurement: they are the opposing team's points in the same game. So the two rows describing a game are consistent with each other by construction, and no game can contribute a scoring margin to one team that it does not contribute, with the opposite sign, to the other.

Preseason is out of scope. Exhibition minutes are not drawn from the same distribution as competitive ones — rotations are experimental and effort is not comparable — so including them would contaminate an early-season average precisely when it is least stable.

Courtrend's derived estimates, charts, and rankings may be cited and shared freely with attribution to Courtrend. Courtrend is an independent analytics project and is not affiliated with, endorsed by, or sponsored by the National Basketball Association (NBA) or its teams. All NBA team names, logos, and statistics are the property of their respective owners.

Sample and scope
unit of observation  one team's performance in one game
coverage             all 30 franchises, seasons 2016-17 to 2025-26
phases               Regular Season, PlayIn, Playoffs (preseason excluded)
measurements         box-score counts; per-possession ratings as published
                     by the NBA's public stats API
refresh              nightly, so a corrected box score propagates within a day
missing data         a game with no recorded score is treated as missing,
                     never as a zero
invalid data         a box score with no shot attempts of any kind is
                     impossible; such rows are rejected rather than smoothed
                     over, and nothing is published until they are resolved

Every quantity on this site is a function of that table alone. There is no prior, no external rating, and no information from outside the games themselves.

This is description, not prediction. Courtrend summarises what has happened, smoothed. It does not forecast, does not regress teams toward an expected level, and knows nothing about why a team's form changed. A team missing its best player for eight of the last 10 games has a rolling line that reflects those absences and no way of telling you they were absences.

02The 10-game rolling average

Each point on a chart is the plain average of a team's most recent 10 games. It answers "how has this team been playing lately", and it is the estimate everything else on the site is built from.

A single game is a poor measurement of a team. Shooting variance alone moves a game's result several points in either direction, and garbage-time minutes distort the rest. Averaging the most recent 10 games keeps the part that persists and discards much of the part that does not.

Choosing the window length is the one real decision, and it is a trade. A short window tracks a genuine change quickly but is dominated by noise; a long window is precise about a level the team may no longer be playing at. Ten games is about three weeks of schedule: long enough that sampling noise is roughly a third of what a single game carries, short enough that a real change in a team surfaces within a month.

Three properties are worth being explicit about. The average is trailing, so no point uses information from a game played after it — the chart at any date is what could have been known at that date. It is unweighted: within the window every game counts equally, and outside it none counts at all. And it is indexed by games, not days, so a window may span two weeks or six.

Estimator and its behaviour
x̄(t) = (1/k) · Σ x_i        over the k most recent games up to and including t
                                k = min(10, games played)

Simple rectangular moving average of the raw game values, per team.
Before a team has 10 games the window is the games that exist — a shorter
average, never a padded or missing one.

What it assumes. That the team's underlying level is approximately constant across the window — local stationarity. Where that fails, mid-window, the estimate is a blend of two levels rather than a measurement of either.

Sampling error. If game-to-game values have standard deviation σ, the window mean has standard error σ/√10. Single-game net rating has a spread of roughly 12 points per 100 possessions, putting the standard error near ±3.8: two teams within about 7 points of each other are not separated by this statistic alone. (σ here is an order-of-magnitude figure for interpretation, not a fitted quantity.)

Dependence between points. Consecutive windows share 9 of 10 games, so under an i.i.d. null their correlation is about 0.9. A smooth three-point climb is one or two good games seen three times, not three independent pieces of evidence — which is the single most common way a moving-average chart is over-read.

We use a flat window rather than an exponentially weighted one deliberately. An EWMA responds faster and never fully forgets, but its effective sample size is not something a reader can hold in their head, and its value cannot be checked by hand. "The last 10 games" can be recomputed by anyone with the box scores, and being checkable is worth more here than being optimally responsive.

A window has an edge, and things fall off it. A blowout counts fully for 10 games and then, in one step, not at all. When a line jumps on a night a team played unremarkably, it is usually an old extreme result leaving the back of the window. That is inherent to equal weighting: the alternative is to let old games fade, which trades the jump for a number nobody can verify.

03Volatility

How much a team scatters around its own recent level. Two teams with the same average can be completely different to watch, and this is the number that separates them.

One team scores 108 to 116 every night; another alternates 95 and 130. Their averages are identical and their seasons are not. We measure the difference as the dispersion of the individual game results over a window — the sample variance of the raw game values, not of the smoothed line.

That distinction is the whole point. The variance of an already-smoothed series would be tiny and would mostly reflect how much the average drifted, which is a different question. On a team alternating 80 and 140 points, the dispersion of games is 1000 and the dispersion of the moving average is near zero. It is the first number that tells you what watching them is like.

Estimator and its precision
s²(t) = (1/(k−1)) · Σ (x_i − x̄(t))²        sample variance, ddof = 1

Reported for points, assists, rebounds, turnovers, True Shooting % and net rating.
k = 10 for a team's own series, k = games played for the League Overview.
A single game has no dispersion to measure: the published series reports zero,
and the League Overview leaves that team unranked rather than ranking a zero.

Precision. A variance is itself imprecise, with relative standard error √(2/(k−1)) — about 47% over 10 games, about 16% over a full season. Read the window figure as "this team has been erratic lately" rather than as a fine ordering; over a season the ordering is worth more, though the gap between the first and second most volatile team is still rarely significant.

What inflates it. Dispersion conflates genuine instability with variation in opposition: a team that faced three contenders and three rebuilding teams will look erratic even if it played to exactly its level each night. Nothing here separates the two.

Two windows, for two questions. A team’s own charts carry the 10-game figure, which is what “erratic lately” means. The League Overview ranks teams on the dispersion of every regular-season game they have played to the date you are viewing, because “the most erratic team this season” should not be decided by ten games and thrown away by the eleventh. Same estimator, same games, wider window — and roughly three times the precision, since the standard error falls with the square root of the sample.

The featured teams are compared at the same date and within the regular season only, so the six panels answer the same question about the same population. Where two teams tie, the order is fixed rather than arbitrary, so the selection does not change between reloads.

04True Shooting %

One shooting number that counts twos, threes and free throws together, weighted by what each is actually worth.

Field-goal percentage treats a three-pointer like a layup and ignores free throws entirely, so it ranks teams by shot selection as much as by shooting. True Shooting divides points by the scoring chances used to produce them, counting a free throw as 0.44 of a chance. As a rough scale: 0.50 is poor, 0.57 is around typical, 0.60 is very good.

Over a window we take the ratio of the totals — all the points divided by all the chances — rather than averaging the ten single-game percentages. The two are not the same estimator. Averaging percentages gives a 70-shot night the same influence as a 95-shot night, which understates exactly the games where a team did most of its scoring.

Estimator
per game     TS% = PTS / (2 · (FGA + 0.44 · FTA))

over the window
             TS% = ΣPTS / (2 · (ΣFGA + 0.44 · ΣFTA))

i.e. a ratio of sums (volume-weighted), not a mean of per-game ratios.
The two coincide only when volume is constant across the window.

Reported as a fraction (0.570), not a percentage (57.0). Where a team took no shots of any kind the quantity is undefined and is reported as missing rather than as a number — an impossible box score, and a case we would rather show as absent than as plausible.

The free-throw weight is an approximation. The 0.44 coefficient is a league-wide estimate of the fraction of free throws that end a possession. Teams that draw an unusual share of and-one or technical free throws deviate from it slightly. It is the standard convention, and we use it unmodified so our figures are comparable with everyone else's.

05The league baseline

The dashed LGA line is the average team as of that date — carrying every team's most recent line forward within each season and phase (widening to all teams in that phase as they play).

Most numbers here only mean something relative to a baseline: 112 points is good or bad depending on the year and the pace of play. For every date we form that baseline by carrying each team's most recent rolling line forward and averaging across them (widening to all teams in that phase as they play — all 30 during the regular season), scoped to each season and phase. It is the comparison line on charts, and during the regular season (once all teams have played) it coincides with the reference the schedule adjustment in section 10 measures opponents against.

Estimator
L(d, p) = (1/|T|) · Σ  x̄_j(d)        j ∈ T, all teams with a line as of date d in phase p

A cross-sectional mean of team-level rolling averages carried forward: an average of averages,
holding composition within each season and phase.

Carrying forward is scoped to each season and phase, so opening night does not inherit the previous June, and a playoff date does not inherit regular-season composition.

Early in a season or phase the baseline covers fewer teams. Carrying forward is scoped to each season and phase. On opening night, or at the start of the play-in or playoffs, the average necessarily covers only the teams that have played in that phase so far, widening to all teams in that phase as more teams play their first game.

06The first 10 games

Before a team has played 10 games there is no full window to average. We use the games that exist, and each view then decides how much to trust the result.

At game 4 an average of 10 games cannot exist; there are four. We use those four rather than withholding the number, which keeps early figures available but makes them noticeably less precise — the standard error of a mean falls with the square root of the sample, so a 4-game average carries about 1.6 times the sampling noise of a full window.

Because that precision differs by view, the treatment does too:

  • Charts stay closed until a team reaches 10 games. A line whose early points average four games and whose later points average ten is not one comparable series, and drawing it invites exactly the wrong reading.
  • Power Rankings publish from the first game, shrinking a team's own noisy figure toward the league average in proportion to how little is known about it (section 11).
  • The data table shows the partial averages unshrunk, because its purpose is to expose the underlying values rather than to interpret them.
Precision during calibration
standard error of a k-game mean  ∝  1/√k

    k = 3   →  1.8 × the sampling noise of a full window
    k = 5   →  1.4 ×
    k = 8   →  1.1 ×
    k = 10  →  1.0 ×   (calibrated)

Suppressing the value until game 10 was the alternative. It was rejected because it makes "not enough games" indistinguishable from "no games" and from "missing data" — three states a reader needs told apart, and only the first is normal.

Two views can disagree in October. A team can be ranked in Power Rankings and then show a calibration notice on its charts. Both are defensible answers to "is four games enough" — one shrinks the estimate, the other declines to draw it — and they are computed from the same underlying games, never from different numbers.

07Emirates NBA Cup (In-Season Tournament)

Viewing the Cup re-estimates rolling averages strictly over tournament dates, evaluating performance across the Cup games played to that date.

Group-play games count in the standings like any other, but turning on the Cup view recalculates every rolling metric strictly across tournament games. Because an NBA Cup run spans roughly four to seven games, these averages cover fewer games than the standard window used elsewhere, and early points reflect only the tournament games played up to that date.

True Shooting % is recalculated over the tournament subset using the same possession-weighted formula applied elsewhere. Schedule adjustments are omitted under this view because opponent strength cannot be estimated reliably from the tournament games alone.

SeasonWindowDates (inclusive)
2025-26Group play2025-11-11 → 2025-12-02
Knockout rounds2025-12-10 → 2025-12-15
2024-25Group play2024-11-12 → 2024-12-03
Knockout rounds2024-12-10 → 2024-12-17
2023-24Group play2023-11-03 → 2023-11-28
Knockout rounds2023-12-04 → 2023-12-09
The Cup is identified by date, not by fixture. Our source data carries no tournament marker, so the Cup is approximated by the date windows above. Any regular-season game played inside a window is included, whether or not it was a Cup fixture, and the final — which the league excludes from regular-season records — is not in the data at all. Read the view as "games played during the Cup", which is what it is.

With the view off, the tournament is still marked on charts: group play is shaded and the knockout dates are drawn as vertical lines, alongside the trade deadline and the All-Star break. Those are context for reading the line, and change nothing about it.

08Playoffs and phase changes

The play-in and playoffs are a different competitive environment, so comparisons between teams use regular-season games only. A single team's own line, though, carries its window across the boundary.

Playoff basketball is not a continuation of the regular season: rotations shorten, the opponent repeats, and effort is not comparable. Treating the two as one population would quietly break every cross-team comparison — a team still playing in June would be measured on postseason form against an eliminated team's months-old regular-season figure. So rankings and volatility use regular-season games only, all season.

A team's own charts answer a narrower question — how is this team playing now — and there the window runs continuously. The first playoff game is averaged with the nine regular-season games before it, and only from the tenth playoff game is the line purely postseason. Against those points we draw the team's own regular-season level, so the change in environment is visible rather than implied.

The same division decides what each page draws once a season is over. The League Overview is a comparison — six teams picked out of thirty and set side by side — so its panels stop at the last regular-season game, even for a team that went on a deep run. Plotting the postseason there would put games on the line that the ranking behind it never saw, and would give a finalist twenty-odd more of them than a lottery team in a grid meant to be read across. A team's own page has no such constraint, so it shows the whole year through the last game actually played, with the boundary marked and the regular-season level drawn against what followed. Same rule, read twice.

Behaviour at the boundary
within a team's own series   window is continuous across the phase change
across teams                 regular-season games only, always
reference level              the team's own average regular-season rolling
                             level for the metric on screen
end of season                after 14 days without a game, no line is drawn
                             rather than a stale one extended

Restarting the window at the boundary was the alternative. It would give a clean postseason-only series whose first nine points are 1- to 9-game averages — at their noisiest exactly when the games matter most. Carrying the window keeps every point a 10-game estimate, at the cost of mixing two environments early in a series. Neither is free; this one is at least uniform in precision.

Playoff opponents are not a cross-section of the league. A postseason average describes performance against one strong opponent faced repeatedly, not against the league. The schedule adjustment in section 10 accounts for the opponent's strength; nothing here accounts for the series itself — the familiarity, the matchup-driven rotation, the injuries that accumulate through a round.

09Breaks, gaps and the calendar

The window counts games, not days. Nothing is adjusted for rest, travel, or a roster that changed mid-window — those are assumptions we make explicitly rather than model.

Ten games may span fifteen days or thirty-five. We do not discount older games by elapsed time and we do not restart anything at the All-Star break or the trade deadline. That keeps every published figure the same kind of estimate, and it means a rolling line in late February can be describing a roster that no longer exists.

So the dates where that is most likely are marked: the All-Star break is shaded and the trade deadline is drawn as a line. A window that straddles the deadline is best read as two different teams' games averaged together — the estimate is not wrong, but the thing it estimates changed underneath it.

Assumptions made explicit
games are exchangeable within a window, regardless of

    · rest — no back-to-back or schedule-density term
    · travel and altitude
    · venue — no home/away split
    · availability — an absent rotation player is invisible
    · roster continuity — a trade does not restart a window
    · time — a game 5 weeks ago counts like a game last night

Each is an omission we have chosen, not one we have missed. Every one of them could be modelled, and each would make a published number depend on a coefficient a reader cannot check against a box score — which is the property the whole approach exists to keep. Where these effects matter, they are for the reader to apply, not for us to bury in the estimate.

10Strength of Schedule

An optional adjustment that credits results against strong opponents and discounts results against weak ones, in the same units as the underlying statistic.

Beating a contender and beating a rebuilding team are not the same achievement, and the raw numbers cannot tell them apart. The adjustment restates a team's figure as if every opponent behind it had been exactly league-average, by adding the average gap between the opponents faced and the league baseline on the dates they were faced.

The gap is averaged over the same 10 games as the figure it adjusts. That matters more than it sounds: a single opponent can sit twelve points either side of average, which is further than a 10-game figure typically moves in a month. Applying one opponent's gap to a 10-game average would make the adjusted line swing with whoever happened to be on the schedule that night rather than with how the team is playing.

  • Net rating rises when the opponent is stronger than average.
  • Offensive rating rises when the opponent's defence is better than average — scoring against a stingy defence is worth more.
  • Defensive rating falls when the opponent's offence is better than average — conceding to a great offence is forgiven.
  • Points scored rise when the opponent's defence is better than average — 110 against a stingy defence is worth more than 110 against a sieve.
  • Points allowed fall when the opponent's offence is better than average — conceding to a great offence is forgiven.
  • True Shooting % rises when the opponent's defence is better than average — shooting efficiently against a stingy defence is worth more.
  • Defensive rebounds rise when the opponent is strong on the offensive glass — securing defensive boards against a great offensive rebounding team is worth more.
  • Offensive rebounds rise when the opponent is strong on the defensive glass — winning offensive boards against a great defensive rebounding team is worth more.
  • Total rebounds are the sum of adjusted offensive and defensive rebounds.
  • Assists rise when the opponent's defence allows fewer assists per made basket — creating assists against a stingy defence that suppresses ball movement is worth more.
  • Turnovers fall when the opponent forces turnovers at an above-average rate. Because turnovers are a metric where fewer is better, facing a ball-hawking defence that inflates your turnovers reduces your adjusted figure to credit you for schedule difficulty.
The adjustment
For each game g the team played, with opponent o(g) and league baseline L(g)
on that date, writing N for rolling net rating, O for offensive rating, D for
defensive rating, P for points scored, A for points allowed, S for True Shooting %,
S_opp for opponent True Shooting % allowed, r_D for defensive rebound rate (DREB / pool_D),
r_O for offensive rebound rate (OREB / pool_O), pool_D for defensive rebound pool per game,
pool_O for offensive rebound pool per game, AST_opp for opponent assist rate allowed (AST / FGM),
TOV_opp for opponent turnover rate forced (TOV / POSS), FGM_t for trailing field goals made per game,
and POSS_t for trailing possessions per game, the game's gaps are:

    gN(g)   =  N_o(g)  −  N_L(g)
    gO(g)   =  D_L(g)  −  D_o(g)
    gD(g)   =  O_L(g)  −  O_o(g)
    gP(g)   =  A_L(g)  −  A_o(g)
    gA(g)   =  P_L(g)  −  P_o(g)
    gS(g)   =  S_opp,L(g)  −  S_opp,o(g)
    gr_D(g) =  r_O,o(g)  −  r_O,L(g)
    gr_O(g) =  r_D,o(g)  −  r_D,L(g)
    gAST(g) =  AST_opp,L(g)  −  AST_opp,o(g)
    gTOV(g) =  TOV_opp,L(g)  −  TOV_opp,o(g)

Each gap is signed so that adding it credits the team for schedule difficulty.
Possessions are estimated using the two-team averaged estimate:
    POSS = 0.5 × ( (FGA − OREB + TOV + 0.44 × FTA) + (opp_FGA − opp_OREB + opp_TOV + 0.44 × opp_FTA) )

At game t the published figures add the mean gap over the same 10-game
window the statistic itself covers:

    Ñ_t    =  N_t  +  mean( gN(g) : g in the last 10 games )
    Õ_t    =  O_t  +  mean( gO(g) : g in the last 10 games )
    D̃_t    =  D_t  +  mean( gD(g) : g in the last 10 games )
    P̃_t    =  P_t  +  mean( gP(g) : g in the last 10 games )
    Ã_t    =  A_t  +  mean( gA(g) : g in the last 10 games )
    S̃_t    =  S_t  +  mean( gS(g) : g in the last 10 games )
    R̃_D,t  =  R_D,t  +  mean( gr_D(g) : g in the last 10 games ) × pool_D,t
    R̃_O,t  =  R_O,t  +  mean( gr_O(g) : g in the last 10 games ) × pool_O,t
    R̃_t    =  R̃_D,t  +  R̃_O,t
    ÃST_t  =  AST_t  +  mean( gAST(g) : g in the last 10 games ) × FGM_t
    T̃OV_t  =  TOV_t  +  mean( gTOV(g) : g in the last 10 games ) × POSS_t

Ratings, points and shooting are additive in their own units. Rebounding, assist
and turnover rates are adjusted for opponent strength and re-expressed at count
scale using the team's own trailing denominator (missed-shot pool, made field goals,
and possessions respectively).

A stretch of exactly average opponents leaves every quantity unchanged, so the
adjustment is a no-op by construction. Games whose opponent cannot be identified
are left out of the mean rather than counted as average; only where no game in
the window has an identifiable opponent does the unadjusted value stand.

Direction is verified against a constructed league in which every team is exactly average: a single game against a +9 opponent moves net rating by +9, against a −9 opponent by −9, and an opponent conceding 100 where the league concedes 112 lifts points scored by 12. Over a full window those gaps average, so the same +9 opponent among ten average ones moves net rating by +0.9.

The adjustment exists across all nine categories (eleven quantities). Every metric is adjusted for strength of schedule when the SOS toggle is active.

Adjusted
  • Net Rating
  • Offensive Rating
  • Defensive Rating
  • Points Scored
  • Points Allowed
  • True Shooting %
  • Rebounding
  • Assists
  • Turnovers
Never adjusted
  • None (all metrics adjusted)
The baseline can diverge from the dashed LGA line in the postseason. Both lines carry each team's most recent figure forward, but they differ in phase scoping. During the regular season, both lines widen to all 30 franchises once each team has played. In the postseason, the plotted LGA line restarts composition at the start of the playoffs and averages only teams with postseason lines (at most 16), while the baseline the schedule adjustment measures against carries regular-season lines forward through the postseason across all 30 franchises. The two coincide in the regular season once each team has played, but diverge on playoff dates.
Early in a season the adjustment is noisier. The gap averages over however many of the 10 games exist, so a team's second game of the season is adjusted by two opponents rather than 10. The damping that keeps the line steady is proportional to the number of games behind it, and it is not fully in effect until the window fills. The baseline is thinner then too, covering only the teams that have played at all.
The adjustment sees the results it is adjusting. Each opponent's strength is measured as of the date it was faced, so it already contains that game's outcome. Beating a team makes it look very slightly worse, which slightly reduces the credit for beating it. With a 10-game window the effect is about a tenth of one input, further diluted by averaging across the window's opponents, but it is a genuine circularity: measuring each opponent as of their previous game would remove it.
An unavailable input is treated as zero. Where a term inside one game's gap cannot be computed, it enters the arithmetic as zero rather than making the result missing. A game with no identifiable opponent at all is handled differently — it is dropped from the average rather than counted as an average opponent — but a partially missing figure still contributes a zero that looks identical to a genuine one.

11Power Rankings

Teams are ordered by their current 10-game net rating, shrunk toward the league average while they have played fewer than 10 games. There is nothing else in the score.

The ranking estimates one thing: how much better than its opponents a team has been, per 100 possessions, over its most recent 10 regular-season games. No win-loss record, no conference weighting, no bonus for a marquee win, no human vote. With the schedule adjustment on, the opponent-adjusted net rating from section 10 is used instead.

Using record instead would be a worse estimator of the same thing — a one-point win and a thirty-point win are the same in the standings, and margin is the better predictor of how a team has actually played. Using margin alone, however, is unstable early, so a team's own figure is shrunk toward the league average with a weight that grows with games played: at 3 games the score is 30% the team and 70% the league, at 9 games it is 90% the team, and from 10 games it is entirely the team.

Score, shrinkage and trend
S_i   team i's rolling net rating in its most recent regular-season game
      on or before the viewed date (opponent-adjusted when that view is on)
G_i   team i's regular-season games played by that date
B     mean of S_j over the teams that have played at least one game

    score_i  =  w·S_i  +  (1 − w)·B ,        w = min(G_i, 10) / 10

w is continuous at G = 10, so no team moves in the table merely by reaching
its 10th game. Equal scores keep a fixed order, so the table is stable
across reloads rather than reshuffling.

    trend_i  =  rank_i(t − 7 days)  −  rank_i(t)          positive = climbed

On the shrinkage weight. A linear ramp in games played is not the variance-optimal weight — that would depend on the ratio of between-team variance to within-team variance, and would not reach 1 at any finite sample. We use the ramp because it is transparent and reaches the team's own figure at the point where the window is full. It is deliberately crude, and it shrinks toward the mean of the teams that have played, which early in a season is itself a small sample.

On the trend. 7 days is long enough that most teams have played two to four games, so the arrow reflects a change in form rather than one result. A team with no games at the comparison date has no prior rank, and is shown as incomparable rather than as unchanged — those are different statements and used to look identical.

Net rating is not always exactly the same quantity. Where the league's per-possession figures are available, net rating is per 100 possessions. Where they are missing for a game, we fall back to raw scoring margin. The two are close in magnitude but are not the same statistic, so a team with incomplete coverage has a rating computed on mixed units.
A ranking is not a prediction. This orders teams by how they have played, not by how they will play. It contains no strength-of-remaining-schedule, no injury information, and no regression to a preseason expectation, all of which a forecasting model would need.

12Known limitations

Everything above, collected in one place. These are the objections we would raise ourselves if we were reviewing this work.

LimitationImpactStatus
Early in a season or phase, the league baseline covers only teams that have played so farLowInherent — widens as each team plays its first game in that phase
The schedule adjustment measures each opponent as of the game being adjusted, so it contains that resultLowOpen — roughly a tenth of one input, diluted further across the window
The schedule adjustment averages over however many of the 10 games exist, so it damps less early in a seasonLowInherent to a trailing window — the same property the rolling averages have
Net rating falls back to raw scoring margin where per-possession figures are missingLowOpen — mixed units for teams with incomplete coverage
Variance conflates genuine instability with variation in the quality of opposition facedLowOpen — not separated
The NBA Cup is identified by date window rather than by fixtureLowBy design — stated in the view and in section 7
Charts stay closed below 10 games while rankings publish a shrunk score from game 1LowBy design — two defensible answers, reconciled on the same games
An unavailable input to the schedule adjustment is treated as zero rather than as missingLowOpen
No adjustment for rest, travel, venue, availability or a mid-window roster changeVariesBy design — section 9

Every one of these came out of a review of our own work, and each is tracked with a decision attached — fix it, or accept it and say so here. Nothing on this list is a surprise to us, which is the point of publishing it.

13Glossary

Plain definitions for every term used above.

Rolling average
The average of the most recent 10 games, recomputed after every game.
Possession
One team's turn with the ball. Teams differ in pace, so per-possession rates compare them fairly.
Offensive rating
Points scored per 100 possessions.
Defensive rating
Points allowed per 100 possessions. Lower is better.
Net rating
Offensive minus defensive rating — roughly, points better than the opponent per 100 possessions.
True Shooting %
Points per scoring chance, counting free throws at 0.44 of a chance. Section 4.
Variance
How widely a team's individual games scatter around its own level. Section 3.
Standard error
How much an estimate would move if you could rerun the same stretch of games. Smaller means more trustworthy.
Shrinkage
Pulling a small-sample estimate toward a baseline, because extreme values from few games are usually partly luck. Section 11.
League baseline (LGA)
The average team as of a given date, carrying every team's most recent line forward within each season and phase. Section 5.
Strength of Schedule
Restating a figure as if every opponent behind it had been league-average. Section 10.
Calibration
The stretch before a team has played 10 games and a full window exists. Section 6.
Phase
Regular season, play-in, or playoffs. Preseason is out of scope.
Time Machine
The date control. Every view is rebuilt as of that date, using only games played on or before it.

14Checking our work

Every figure here can be recomputed from public box scores. Nothing depends on a model you cannot inspect.

To verify any point on a chart: take the team's last 10 games up to that date, average the statistic, and compare. That is the whole of it — no weighting to reconstruct, no coefficients to guess. For a schedule-adjusted figure you also need each of those 10 opponents' averages, and the baseline on the date each was faced.

Every method described on this page is computed in exactly one place and checked against hand-worked examples, so the arithmetic is verified independently of whatever the live data happens to be. The figures quoted here — window length, calibration threshold, free-throw weight, comparison window, Cup dates — are not typed into this page. Where the value lives in code this page can read, it is rendered straight from it; where it lives in the pipeline that builds the data, an automated check compares the two and fails on any disagreement. That check runs alongside the rest of our tests, so the page and the numbers are verified together rather than assumed to agree.

Two further checks examine the published data rather than the arithmetic. One re-derives every stored figure from the original games and reports any disagreement, which is how we catch a change in method quietly rewriting history. The other audits what is published — date formats, duplicate team-dates, values outside plausible ranges, and whether the league baseline exists for every date that needs one.

Both are run deliberately, around changes to how the numbers are built, rather than continuously. So they are how a mistake gets found, not a guarantee that none exists — which is the honest version, and the reason section 12 is written the way it is.

Courtrend is an independent analytics project and is not affiliated with, endorsed by, or sponsored by the National Basketball Association (NBA) or its teams. All NBA team names, logos, and statistics are the property of their respective owners. Source data is the NBA's public stats API.

Courtrend is an independent analytics project and is not affiliated with, endorsed by, or sponsored by the National Basketball Association (NBA) or its teams. All NBA team names, logos, and statistics are the property of their respective owners.