Basketball Data Science All studies Scouting one-pagers Code on GitHub
27 seasons of NBA free agency

Do NBA players play better in contract years?

The idea is older than most box scores: a player about to hit free agency plays harder, because the next contract depends on it. This study tests it across 1999-00 to 2025-26, one season at a time, by comparing each player's contract years with seasons where the next deal was already guaranteed. The answer depends on who you ask about. Role players show a small, real bump. Stars show none, and if anything slip.

Role players
+0.21
Box Plus/Minus per 100 possessions above expectation in a contract year (95% interval +0.06 to +0.36).
Stars
โˆ’0.25
No boost. The interval runs from โˆ’0.54 to +0.05.
Contract years
2,921
Seasons labeled as the last of a deal with the next one not yet secured.
Comparison seasons
3,185
Seasons where the player's deal was certain to continue.
The question

Why this is harder than it looks

Two things make a naive comparison wrong. The first is regression to the mean: players often reach a contract year right after a good season, so their next season looks worse whatever the contract says. The second is aging: contract years cluster at particular ages, and players at those ages are already rising or falling.

So the outcome here is not raw Box Plus/Minus. It is Box Plus/Minus against an expectation built only from the player's earlier seasons, pulled toward the league average and adjusted for age. A contract-year effect has to show up on top of both.

How one season is scored: Julius Randle, 2021-22Scoring one season: Julius Randle, 2021-22league average (0)01234BPM1.52018-19weight 0.6โˆ’0.32019-20weight 0.83.82020-21weight 1.02021-22the seasonexpected+1.81actual 0.5โˆ’1.31โˆ’3.31Weight the three priorseasons by recency (0.6,0.8, 1.0, oldest tonewest) and by minutes.2Pull that toward theleague average by adding500 minutes of an averageseason: +1.89.3Add the league's typicalchange at age 27: โˆ’0.08.Expected: +1.81.4Actual minus expected:0.5 โˆ’ 1.81 = โˆ’1.31. Thisresidual is what thestudy compares.The naive change from the season before (โˆ’3.3) mixes in regression to the mean: aplayer's best season is usually followed by a lower one. Against the expectation the dropis โˆ’1.31.
How one season is scored: Julius Randle, 2021-22Scoring one season: Julius Randle,2021-22league average (0)01234BPM1.52018-19weight 0.6โˆ’0.32019-20weight 0.83.82020-21weight 1.02021-22the seasonexpected+1.81actual 0.5โˆ’1.31โˆ’3.31Weight the three prior seasons byrecency (0.6, 0.8, 1.0, oldest tonewest) and by minutes.2Pull that toward the league average byadding 500 minutes of an average season:+1.89.3Add the league's typical change at age27: โˆ’0.08. Expected: +1.81.4Actual minus expected: 0.5 โˆ’ 1.81 =โˆ’1.31. This residual is what the studycompares.The naive change from the season before (โˆ’3.3)mixes in regression to the mean: a player'sbest season is usually followed by a lowerone. Against the expectation the drop isโˆ’1.31.
One real season scored the way the study scores every season. Julius Randle's season here came right after his best one. The dashed gray drop is the naive change from the year before; the red segment is the residual against an expectation built from his three prior seasons, which already allows for a fall back toward average. The example shows the scoring only and says nothing about his contract.

Players are grouped into four tiers (stars, starters, role players, and fringe players) by their value in the two seasons before, never by the contract year itself. The map below lines these pieces up season by season and shows where each result in this story comes from.

Which seasons feed which part of the studytโˆ’3tโˆ’2tโˆ’1tt+1Seasons beforetโˆ’3 to tโˆ’1Expected BPMPrior seasons weighted0.6, 0.8, 1.0 (oldest tonewest) and by minutes,pulled toward average by500 minutes, plus the ageadjustment.TierValue over the two seasonsbefore, ranked within theseason:Stars: top 10%Starters: next 20%Role players: middle 40%Fringe players: bottom 30%The seasont: the testContract year: 2,921last season of a deal, thenext not yet securedGuaranteed year: 3,185the deal was certain tocontinueThe comparisonResidual (actual minusexpected BPM), contractyear minus guaranteedyear:Role players +0.21Stars โˆ’0.25The season aftert+1Did they play again?20% of contract-year roleplayers never playedanother NBA season (stars:10%).Returners, next seasonRole players againstexpectation:โˆ’0.02after a contract year+0.11after a guaranteed year
Which seasons feed which part of the studySeasons beforetโˆ’3 to tโˆ’1Expected BPMPrior seasons weighted 0.6, 0.8, 1.0 (oldest tonewest) and by minutes, pulled toward averageby 500 minutes, plus the age adjustment.TierValue over the two seasons before, rankedwithin the season:Stars: top 10%Starters: next 20%Role players: middle 40%Fringe players: bottom 30%The seasont: the testContract year: 2,921last season of a deal, the next not yet securedGuaranteed year: 3,185the deal was certain to continueThe comparisonResidual (actual minus expected BPM), contractyear minus guaranteed year:Role players +0.21Stars โˆ’0.25The season aftert+1Did they play again?20% of contract-year role players never playedanother NBA season (stars: 10%).Returners, next seasonRole players against expectation:โˆ’0.02after a contract year+0.11after a guaranteed year
The study in one picture. The seasons before set each player's expectation and tier, the season itself is where contract years and guaranteed years are compared, and the season after shows who returned and how they played. Each result on the map has its own section below.
The answer

Role players rise a little; stars do not

Contract-year difference by tier, with two independent estimates. The first compares contract years with guaranteed years across players, with an interval from resampling players. The second compares each player only with their own other seasons. Role players land above zero in both (+0.21 and +0.37). Stars land at or below zero in both (โˆ’0.25 and โˆ’0.34). Pooling every tier hides the split: the all-tier difference is โˆ’0.06 (โˆ’0.15 to +0.03).

The size matters as much as the sign. About a fifth of a point of Box Plus/Minus per 100 possessions is small: a real effect, not a transformation. For stars the estimate points the other way. One possible reading is that stars have little to prove and a lot to protect, but the data here can only say that no boost shows up.

Stress test

The pattern survives every specification

The analysis plan was written before any contract-year comparison was run, and its two amendments were dated before any outcome was looked at. The checks below were also fixed in advance.

Role players and stars under each pre-specified alternative: a broad contract-year definition (any final contract season), only the labels held with high confidence, the original 1,000 regression minutes for the expectation, and stars defined by All-NBA or All-Star honors instead of value. Role players stay above zero in every version, from +0.19 to +0.29. Stars defined by honors fall further, to โˆ’0.39 (โˆ’0.71 to โˆ’0.08).

The contract-year boost does not spread into other measures much. Of six secondary outcomes checked across five tier groups, only two survive a correction for multiple comparisons, both across all tiers: players appear in +1.73 more games than their age alone predicts, and their true shooting rises by +0.32 percentage points.

Afterward

The season after

A contract year is also a sorting event. Of the contract-year role players, 20% never played another NBA season (stars: 10%).

Next-season Box Plus/Minus against expectation, for players who played at least 250 minutes the following season. Stars, starters, and role players coming off a contract year do worse the next season than players coming off a guaranteed year (role players: โˆ’0.02 against +0.11). Fringe players are level. This is descriptive: the players who returned after a contract year are a selected group, since the market has just decided who got a new deal.
Limits

What the labels can and cannot support

No public source lists contract status as a clean field, so every season is labeled from signing and extension events, team moves, the rookie-scale schedule, departures from the league, and Wikipedia's free-agent tables. Checked against a hand-verified gold set and Patricia Bender's 2017-18 contracts snapshot, the labels are strong for recent seasons and weaker before about 2012. Mislabeled seasons mostly blur a real effect toward zero, so the role-player estimate is more likely understated than overstated. Earlier eras are reported in the README for context only, because their labels could not be checked against independent records.

How a season gets its contract label, and how often the label is right1. Read the signals for each seasonSigning and extension eventsTeam movesThe rookie-scale scheduleDepartures from the leagueWikipedia's free-agent tablesContract yearlast season of a deal, next not yet securedGuaranteed yeardeal certain to continueUnknownleft out of both groups2. Check the labels against referencesEach dot is 1% of the seasons a reference knows were contract years, colored by what thelabeler called them:called contract yearunknown (left out)called guaranteedGold set, 2012-13 to 2025-2694% called contract year, 6%unknown, 0% called guaranteed98% of its contract-yearlabels were rightGold set, 1999-00 to 2011-1283% called contract year, 8%unknown, 9% called guaranteed83% of its contract-yearlabels were rightBender's snapshot, 2017-1874% called contract year, 23%unknown, 3% called guaranteed81% of its contract-yearlabels were rightA wrong label puts a season in the other group, which pulls the two groups' averagestogether. Label errors shrink a real gap toward zero; they rarely create one.
How a season gets its contract label, and how often the label is right1. Read the signals for each seasonSigning and extension eventsTeam movesThe rookie-scale scheduleDepartures from the leagueWikipedia's free-agent tablesContract yearlast season of a deal, next not yet securedGuaranteed yeardeal certain to continueUnknownleft out of both groups2. Check the labels againstreferencesEach dot is 1% of the seasons a referenceknows were contract years, colored by what thelabeler called them:called contract yearunknown (left out)called guaranteedGold set, 2012-13 to 2025-2694% called contract year, 6% unknown, 0% calledguaranteed98% of its contract-year labels were rightGold set, 1999-00 to 2011-1283% called contract year, 8% unknown, 9% calledguaranteed83% of its contract-year labels were rightBender's snapshot, 2017-1874% called contract year, 23% unknown, 3% calledguaranteed81% of its contract-year labels were rightA wrong label puts a season in the othergroup, which pulls the two groups' averagestogether. Label errors shrink a real gaptoward zero; they rarely create one.
How a season gets its label and how the labels were checked. Each dot is one percent of the seasons a reference knows were contract years, colored by what the labeler called them. Most misses are "unknown", which leaves the season out of both groups, and few true contract years land in the guaranteed group. Labels for the older seasons are right less often.

Performance comes from Basketball-Reference, cross-checked season by season against stats.nba.com before any modeling. The raw pages stay local; only derived estimates are published.

Method, code, and verification

The full technical write-up (data provenance, model specification, validation gates, and stated limitations) is the README. The analysis is implemented independently in R and Python and reconciled to numeric tolerance; run_checks.sh replays every gate.