Back-testing fantasy basketball drafts and weekly moves
Ten NBA seasons, 2016-17 to 2025-26, free data only
Update: with an age and games-played model, a draft strategy beat a naive consensus board in simulation, and in-season management added more. The edge depends on the market ignoring age and injury history, and we could not test against real ADP.
Abstract
Superseded in part: the first two paragraphs report the original studies, which used prior-season statistics only. The update below the abstract, and sections 8 to 12, report later work with an age and games-played model that did find a drafting edge in simulation.
We ran two simulation studies on free, public NBA data. Study 1 simulates 10-team snake drafts for the 2017 to 2026 seasons (end-year labels), 44,000 drafts per run and 4,000 per strategy, and scores the resulting rosters on full-season nine-category roto results. Eleven strategies were compared against drafting by an average-draft-position (ADP) proxy. No strategy beat the baseline on held-out seasons. A guardrail variant that gained +1.74 roto points on the training seasons (2017 to 2021) gained +0.11 on the test seasons (2022 to 2026), with a confidence interval that includes zero.
Study 2 simulates in-season management with weekly granularity from game-level box scores, using only information available at the start of each week. A policy that replaces players who have missed their team's last three games with the best available free agent improved category win rate by 7.2 points over a static roster on the test seasons (95% CI 6.4 to 8.0), and the gain was positive in every test season. More active streaming and waiver policies did not improve on it.
Update (15 October 2026): the combined strategy study
The sections below this update describe the first two studies, which used prior-season statistics only and found no drafting edge. We then added an age curve and a games-played model, tested out of sample (fit on target seasons 2016 to 2021, scored on 2022 to 2026), and re-ran the simulation under the live league rules. Where the older sections and this update disagree, this update governs.
Main results, all on test seasons 2022 to 2026 with 95 percent bootstrap intervals over season by draft-slot cells: the age-and-games-played draft model, with weekly expected-value lineups, replacing injured players, and low-margin streaming, wins 60.8 percent of category contests against all opponents, against 53.6 percent for a consensus-proxy draft that also replaces injured players. The difference is +7.2 points [+6.4, +8.0]. About two thirds of it comes from the draft model and a third from in-season management. The comparator is our own proxy for ADP, not real Yahoo ADP, so the draft part is probably overstated.
1. Introduction
Fantasy basketball advice tends to be asserted rather than tested. Preseason projections and ADP archives are mostly paywalled or overwritten each year, which makes honest back-testing hard: a test is only valid if the strategy sees nothing that was not known before the season began. We asked two questions. First, can a model that uses only prior-season statistics outdraft a consensus-style ADP ranking? Second, once the season starts, does disciplined weekly roster management produce measurable gains over leaving a roster alone?
We report results on held-out seasons and say where the design is generous or weak. We did not have real historical ADP or expert projections, so our market baseline is a proxy built from the same prior-season data. That choice favors our strategies relative to a real market, and the results should be read with that in mind.
2. Data
Study 1 uses season per-game tables from Basketball-Reference for 2015 to 2026 (12 pages, fetched 7 seconds apart to respect the site's rate limit). We kept one row per player, taking the combined row for players traded mid-season. Study 2 uses game-level player box scores from the open sportsdataverse/hoopR-nba-data repository on GitHub (ESPN-derived, regular season only, 2015 to 2026). The data are free and no paywalled or access-restricted source was used.
- Study 1 source
- basketball-reference.com/leagues/NBA_YYYY_per_game.html
- Study 2 source
- github.com/sportsdataverse/hoopR-nba-data (player_box parquet files)
- Seasons tested
- 2017 to 2026 (10), using Y-1 and Y-2 data as inputs
- Train / test split
- 2017 to 2021 / 2022 to 2026
We also checked Basketball Monster's free "Preseason" pages as a possible projection source. They contain statistics from a few actual NBA preseason games per player (1 to 5 games), not projections, and optional ADP columns could not be populated without an account. We did not use them. No free archive of true historical preseason ADP or analyst projections was found.
3. Study 1: drafting
3.1 League and scoring
Ten teams draft 13 players each in a snake draft. The strategy under test sits in each of the ten draft slots in turn. Scoring is nine-category roto: three-pointers, rebounds, assists, steals, blocks, points, turnovers (lower is better), and the field-goal and free-throw percentages. Each category is ranked across the ten teams and points are summed (maximum 90, league average 49.5). Season totals come from actual full-season per-game stats multiplied by games played. Each team counts its best 10 players by actual season value.
Player values are z-scores. Means and standard deviations come from the top 150 players by total minutes in the relevant season. Percentage categories use the impact method: made shots minus the league rate times attempts, then standardized. Turnovers enter with a negative sign.
3.2 Opponents and the ADP proxy
No historical ADP was available, so we built a proxy: 0.7 times last season's per-game z-score sum plus 0.3 times the season before's, ranked across all players with data in either season. Each of the nine opponents drafts from its own noisy copy of that ranking. Noise is Gaussian with standard deviation 4 + 0.12 times the proxy rank, drawn once per opponent and player. Players with no stats in the two prior seasons (rookies, for example) cannot be drafted by anyone.
3.3 Strategies
- ADP proxy: take the best remaining player by the proxy ranking. This is the baseline.
- Last-season value rank: take the best remaining by last season's total value (z-sum times games played share).
- Shrunk projection: per-minute rates from the last two seasons (weight 1 and 0.3 or 0.5), shrunk toward a replacement-level prior, times projected minutes and a games-played estimate, with an optional age adjustment.
- Category balance: the projection with category weights raised for categories where the current roster is weak (strength k).
- Guardrail: take the best projection among players whose proxy rank is within W picks of the current pick, W of 8 or 20.
- Rank blend: pick by a weighted average of projection rank and proxy rank.
3.4 Design
Each season, slot and repeat (40 of them) draws one set of opponent noise, and every strategy then drafts against that same set, so comparisons are paired. That gives 10 seasons x 10 slots x 40 repeats x 11 strategies = 44,000 simulated drafts per run, or 4,000 per strategy. We ran the full set twice: once with default projection parameters (run 1) and once after tuning them on the training seasons (run 2). Confidence intervals are bootstrap intervals over season-by-slot cells (4,000 resamples), which treats the cell as the unit of independence.
4. Study 1 results
Run 2 used projection parameters chosen on the training seasons only: a games-played discount strength of 0.5, an age penalty of 0.03 per year over 29, a prior worth 300 minutes, and a weight of 0.3 on the older season. The choice came from a 54-combination grid over the projection model's parameters, scored on training seasons with 8 repeats. Because that is a search, the training numbers below are optimistic by construction. The test numbers are the honest ones.
| Strategy | Roto pts | Avg rank | Top 3 % | Diff vs ADP | 95% CI |
|---|---|---|---|---|---|
| Rank blend, alpha=0.3 | 55.8 | 3.66 | 55 | +2.01 | [+1.05, +2.99] |
| Projection, guardrail ADP+8 | 55.5 | 3.70 | 54 | +1.74 | [+0.61, +2.84] |
| Rank blend, alpha=0.5 | 55.5 | 3.74 | 54 | +1.72 | [+0.89, +2.55] |
| Projection, guardrail ADP+20 | 55.2 | 3.80 | 52 | +1.40 | [+0.27, +2.53] |
| ADP proxy (baseline) | 53.8 | 4.24 | 46 | 0 | - |
| Balance k=1 + guardrail 20 | 53.4 | 4.42 | 42 | -0.41 | [-1.39, +0.52] |
| Shrunk projection | 52.7 | 4.52 | 42 | -1.08 | [-2.34, +0.22] |
| Projection + category balance, k=0.5 | 52.2 | 4.71 | 40 | -1.59 | [-2.77, -0.46] |
| Projection + category balance, k=1 | 50.5 | 5.27 | 29 | -3.33 | [-4.41, -2.35] |
| Last-season value rank | 49.9 | 5.39 | 33 | -3.89 | [-5.99, -1.74] |
| Projection + category balance, k=2 | 48.9 | 5.72 | 23 | -4.91 | [-5.77, -3.99] |
| Strategy | Roto pts | Avg rank | Top 3 % | Diff vs ADP | 95% CI |
|---|---|---|---|---|---|
| Projection, guardrail ADP+8 | 53.2 | 4.49 | 43 | +0.11 | [-1.10, +1.30] |
| ADP proxy (baseline) | 53.1 | 4.53 | 44 | 0 | - |
| Balance k=1 + guardrail 20 | 52.8 | 4.65 | 40 | -0.31 | [-1.48, +0.93] |
| Rank blend, alpha=0.5 | 52.2 | 4.78 | 39 | -0.91 | [-1.85, +0.09] |
| Rank blend, alpha=0.3 | 51.8 | 4.91 | 38 | -1.32 | [-2.56, -0.05] |
| Projection, guardrail ADP+20 | 51.7 | 4.95 | 38 | -1.41 | [-2.86, +0.04] |
| Projection + category balance, k=0.5 | 48.8 | 5.82 | 27 | -4.33 | [-6.23, -2.51] |
| Projection + category balance, k=1 | 48.3 | 5.91 | 25 | -4.74 | [-6.15, -3.31] |
| Projection + category balance, k=2 | 47.8 | 6.02 | 21 | -5.31 | [-6.50, -4.12] |
| Shrunk projection | 47.7 | 6.04 | 28 | -5.42 | [-7.70, -3.12] |
| Last-season value rank | 45.8 | 6.53 | 18 | -7.33 | [-9.37, -5.20] |
Roto points are out of 90, league average 49.5. Difference is the paired mean versus the ADP proxy, with a bootstrap 95% interval over season-by-slot cells.
4.1 The overfitting result
The guardrail strategy gained +1.74 points on 2017 to 2021 (interval +0.61 to +2.84) and +0.11 on 2022 to 2026 (interval -1.10 to +1.30). The rank blends moved from about +2.0 to about -1.3. Across all ten seasons the guardrail's pooled gain is +0.92, which looks marginally positive only because the training seasons are included.
4.2 Sensitivity: default parameters (run 1)
With the untuned default projection (run 1), the pattern was different and equally unstable: the guardrail variant scored -0.99 on the training seasons and +0.75 on the test seasons. A strategy whose sign flips between the two parameter settings has no demonstrated edge.
| Strategy | Roto pts | Avg rank | Top 3 % | Diff vs ADP | 95% CI |
|---|---|---|---|---|---|
| Projection, guardrail ADP+8 | 54.0 | 4.18 | 48 | +0.75 | [-0.62, +2.19] |
| ADP proxy (baseline) | 53.3 | 4.47 | 45 | 0 | - |
| Rank blend, alpha=0.5 | 53.0 | 4.49 | 43 | -0.24 | [-1.21, +0.84] |
| Shrunk projection | 47.7 | 6.00 | 26 | -5.52 | [-7.65, -3.37] |
| Last-season value rank | 45.9 | 6.51 | 19 | -7.32 | [-9.37, -5.26] |
4.3 Other findings
- Category-balance drafting was worse than the baseline at every strength tested, on both splits.
- The projection model on its own trailed the proxy on test seasons by about 5 points. Using only last year's statistics cannot beat a ranking made from last year's statistics.
- Draft slot matters more than strategy: the baseline averaged about 55.7 roto points from slots 1 to 3 and about 50.7 from slot 10 in run 1.
- Season-to-season variation is large, about 10 roto points for any one strategy.
5. Study 2: weekly management
5.1 Setup
Study 2 reuses the draft simulator (our team always drafts by the ADP proxy) and then plays the regular season week by week. Weeks run Monday to Sunday. Matchups are scored as head-to-head category results against each of the other nine teams, averaged. Our 10-player lineup is set at the start of each week from the best 10 rostered players by expected value that week, and counts only the games those players actually play. Bench players score nothing.
At the start of each week a policy sees only information from completed games: per-game statistics to date blended with the preseason prior (15 pseudo-games of prior weight), whether the player appeared in each of his team's last 10 games, and the public schedule for the coming weeks. A player absent from his team's last three games is treated as out and given a 0.15 chance of playing. Free agents are everyone not drafted by any team who has either prior data or an appearance so far. Each team may make up to three adds per week.
5.2 Policies and opponents
- Static: never changes the roster. The lineup is still set each week and benches players who are out.
- Replace injured: swaps a player marked out for the best free agent by four-week expected value, if that value is higher.
- Waiver (horizon H, margin m): swaps the lowest-value rostered player for the best free agent when the H-week value gap exceeds m.
- Stream (margin m): adds the free agent who most raises next week's lineup value, dropping the lowest four-week-value player.
Opponents are tested in two modes. In passive mode they play the static policy. In active mode they play replace-injured, which stands in for a competent human manager. The tables use active opponents unless noted. Collisions where a rival and our team add the same free agent are ignored.
5.3 Results
Training seasons used 6 repeats per slot (300 leagues, 22 policies, 6,600 policy runs) to scan a grid of policies. Test seasons used 12 repeats per slot (600 leagues) for five selected policies (3,000 policy runs).
| Policy | Cat win % | Diff vs static (pts) | 95% CI | Adds/season |
|---|---|---|---|---|
| Replace injured player | 53.1 | +7.20 | [+6.40, +8.00] | 8.4 |
| Waiver, 2-week value, margin 2 | 51.6 | +5.70 | [+4.70, +6.70] | 14.3 |
| Waiver, 4-week value, margin 10 | 51.6 | +5.70 | [+4.80, +6.70] | 7.8 |
| Stream, margin 2 | 51.1 | +5.10 | [+4.20, +6.10] | 5.3 |
| Static roster (lineup only) | 45.9 | 0 | - | 0.0 |
| Policy | Cat win % | Diff vs static (pts) | 95% CI | Adds/season |
|---|---|---|---|---|
| Replace injured player | 60.5 | +6.40 | [+5.80, +7.20] | 8.4 |
| Waiver, 2-week value, margin 2 | 59.2 | +5.10 | [+4.30, +6.00] | 14.3 |
| Waiver, 4-week value, margin 10 | 59.2 | +5.10 | [+4.30, +5.90] | 7.8 |
| Stream, margin 2 | 58.6 | +4.60 | [+3.80, +5.40] | 5.3 |
| Static roster (lineup only) | 54.1 | 0 | - | 0.0 |
| Policy | Cat win % | Diff vs static (pts) | 95% CI | Adds/season |
|---|---|---|---|---|
| Replace injured player | 53.5 | +5.80 | [+4.90, +6.60] | 8.3 |
| Waiver, 2-week value, margin 2 | 52.2 | +4.40 | [+3.10, +5.70] | 15.7 |
| Waiver, 4-week value, margin 10 | 52.2 | +4.50 | [+3.40, +5.60] | 9.4 |
| ros2_5 | 52.1 | +4.40 | [+3.20, +5.60] | 10.2 |
| ros4_5 | 52.1 | +4.40 | [+3.20, +5.60] | 12.8 |
| ros8_10 | 51.9 | +4.20 | [+2.90, +5.30] | 11.9 |
| ros1_2 | 51.6 | +3.90 | [+2.60, +5.20] | 12.6 |
| Stream, margin 2 | 51.6 | +3.90 | [+3.00, +4.80] | 6.0 |
| ros2_10 | 51.5 | +3.80 | [+2.90, +4.60] | 6.1 |
| ros8_5 | 51.5 | +3.80 | [+2.60, +4.90] | 15.4 |
| stream0 | 51.5 | +3.80 | [+2.70, +4.90] | 14.2 |
| ros4_2 | 51.4 | +3.70 | [+2.40, +4.90] | 17.8 |
| ros1_5 | 51.3 | +3.60 | [+2.70, +4.40] | 6.8 |
| ros8_0 | 51.2 | +3.50 | [+2.30, +4.60] | 23.4 |
| ros8_2 | 51.2 | +3.50 | [+2.40, +4.60] | 18.5 |
| ros4_0 | 50.9 | +3.20 | [+2.10, +4.30] | 26.0 |
| ros2_0 | 50.7 | +3.00 | [+1.80, +4.20] | 29.8 |
| ros1_0 | 50.1 | +2.40 | [+1.00, +3.70] | 38.2 |
| ros1_10 | 49.7 | +2.00 | [+1.50, +2.50] | 2.8 |
| stream5 | 49.3 | +1.60 | [+1.20, +2.00] | 2.5 |
| stream10 | 47.9 | +0.20 | [0.00, +0.30] | 0.6 |
| Static roster (lineup only) | 47.7 | 0 | - | 0.0 |
Category win % is the share of category comparisons won against the other nine teams. Difference is in percentage points versus the static roster.
Replace-injured won on both splits and in every test season (static versus replace-injured, category win %: 2022: 45.9 vs 53.3; 2023: 49.2 vs 52.8; 2024: 41.8 vs 53.8; 2025: 47.1 vs 52.5; 2026: 45.8 vs 53.3). Policies that add players more often, whether for streaming or longer-horizon waiver value, did not beat it. On the training grid the most active waiver settings, which made 23 to 38 adds per season, did worse than the plainer policy.
6. Limitations
- The ADP baseline is a proxy built from the same prior-season statistics the strategies use. A real market also prices in rookies, injuries, trades and expert news, so real ADP is harder to beat and the Study 1 results are, if anything, generous to our strategies.
- Players with no stats in the two prior seasons cannot be drafted in Study 1. Real drafts include rookies.
- Roto scoring counts each team's best 10 players by actual full-season value, a hindsight form of bench management that softens injury losses equally for all teams and probably favors star-heavy rosters.
- The 2019-20 season was interrupted and finished in a bubble, and 2020-21 was shortened to 72 games. We include both and treat the weekly calendar as is.
- Study 2 has no positions, no injured-list slots, no weekly category targeting, a fixed three-add weekly cap, and ignores contention between teams for the same free agent.
- The static baseline in Study 2 never replaces anyone. Part of the replace-injured gain is the cost of leaving a thin roster alone, so the headline difference overstates the gain over a manager who already handles injuries. The better comparison is the active-opponent column, where the policy still scored above 50 percent.
- Study 2 used a different data source than Study 1 and the two were not reconciled.
- The policy parameter grids were searched on training seasons. The test seasons were run once for the five policies shown. Other choices were not re-tuned.
- Intervals treat season-by-slot cells as independent. Seasons share many players, so they are likely narrower than they should be.
7. Conclusion
Using prior-season statistics alone, no drafting strategy reliably beat a consensus-style ranking (studies 1 and 2). Adding an age curve and a games-played model changed that in simulation: a draft model using them, with weekly expected-value lineups, replacing injured players and low-margin streaming, beat a consensus-proxy draft by about 7 points of category win rate on held-out seasons. About 5 points come from the draft model and the rest from management. The draft edge exists only to the extent real drafters under-price age decline and injury history, which we could not test because historical ADP is not available for free. Category-targeting lineups did not help. Conditional age effects were tested: minutes load matters, position does not. No claim here is a forecast for 2026-27.
8. Age, breakout and games-played models (out of sample)
League rules used: head-to-head nine categories (FG%, FT%, 3PM, PTS, REB, AST, STL, BLK, TO), 10 teams, 14 draft rounds, 11 position-eligible starters, 3 bench, 2 IL, at most 50 acquisitions, weekly lineups. Models are ridge regressions fit on earlier target seasons only. Test set: 1,587 player-seasons.
| Age | Effect | 95% CI |
|---|---|---|
| 22 and under | +0.84 | [+0.66, +1.04] |
| 23-24 | +0.46 | - |
| 25-26 | +0.28 | [+0.12, +0.43] |
| 27-28 | -0.09 | [-0.26, +0.06] |
| 29-30 | -0.34 | [-0.53, -0.15] |
| 31-32 | -0.76 | [-0.95, -0.58] |
| 33-34 | -0.93 | [-1.19, -0.69] |
| 35 and over | -1.16 | [-1.41, -0.95] |
Adding age cuts test squared error by 0.365 [0.237, 0.488]. Adding minutes, usage, late-season form and team change brings test R-squared from 0.682 to 0.720. A gradient-boosted model was worse than ridge.
Breakouts (a rise of 2 or more z-points, 15.5 percent of cases): the classifier reaches AUC 0.720 against 0.655 for a base model, a gain of +0.065 [0.033, 0.099]. Its top decile hits 35.4 percent against 30.4 percent, but that difference's interval includes zero [-0.025, +0.133]. A simple rule, age 24 or under with minutes up more than 2, hit only 18 percent. We treat the breakout signal as modest and did not use it in the draft model.
Games played: predicting next-season games played from two seasons of history, age, minutes, usage and prior low-minute games gives R-squared 0.169, against 0.133 for history alone and below zero for a league mean. Expected games next season by prior games: under 41 gives 42.6, 41 to 57 gives 51.1, 58 to 69 gives 58.0, 70 and over gives 64.1. Age costs about 1 game at 29 to 30, 3 at 33 to 34, 6 at 35 and over, and nothing at 25 to 28. Prediction is weak but real.
9. Does the age effect depend on who the player is?
We interacted the age curve with position, minutes load, prior-year games played, value tier and statistical style, fitting on 2016 to 2021 and scoring on 2022 to 2026. An interaction counts only if its test error gain has an interval above zero.
| Group | Gain | Holds out of sample? |
|---|---|---|
| Position (guard, wing, big) | +0.007 [-0.015, +0.028] | No |
| Minutes load (under 22, 22-30, 30+ mpg) | +0.026 [+0.002, +0.049] | Yes |
| Prior-year games played | +0.022 [0.000, +0.043] | Borderline |
| Value tier (role, mid, star) | +0.007 [-0.029, +0.043] | No (few stars) |
| Style (rebound/block, balanced, assist/3PT/steal) | +0.006 [+0.001, +0.011] | Yes, tiny |
| All together | +0.065 [+0.015, +0.118] | Yes, small |
- Minutes load matters. Players at 30 or more minutes decline far less after 29 (-0.27 at 29-32, -0.40 at 33 and over) than 22-30 minute players (-0.76, -1.38) or bench players (-0.52, -1.21).
- Stars (value above 3, n=263) show no decline through 33 and over in sample (+0.13, +0.06) while role players fall hard (-0.55, -1.27). This did not pass the formal test and overlaps with minutes load, so it is a direction, not a rule.
- Position does not matter. Guards, wings and bigs have similar curves.
- Players who missed many games (under 60 percent) get a weaker youth bump (+0.23 against +0.72) with similar decline.
- Rebound- and block-heavy players decline slightly more at 33 and over (-1.24 against -0.76). The gain is real but tiny.
The per-category version of the conditional model gave mixed, tiny gains across the nine categories, so the draft model keeps the flat age curve. The conditional effects are reported here as findings, not shipped as adjustments.
10. Combined draft and in-season strategy under the live rules
Ten seasons, 1,000 simulated leagues per season, every draft slot, 5,000 simulations per strategy per half. Score is the all-play category win rate (share of the nine categories won against each of the nine opponents each week; 0.50 is average). Strategies were chosen on 2017 to 2021 and scored once on 2022 to 2026.
| Draft | In-season management | All-play | Edge vs ADP-proxy + replace injured (95% CI) |
|---|---|---|---|
| ADP proxy | replace injured | 0.536 | 0 |
| ADP proxy | replace injured + streaming | 0.561 | +0.026 [+0.022, +0.030] |
| Model (guardrail ADP+8) | none | 0.547 | +0.010 [0.000, +0.021] |
| Model (guardrail ADP+8) | replace injured | 0.584 | +0.049 [+0.039, +0.058] |
| Model (guardrail ADP+8) | replace injured + streaming | 0.608 | +0.072 [+0.064, +0.080] |
| Model blend 50/50 with ADP | replace injured + streaming | 0.604 | +0.068 [+0.062, +0.074] |
| Prior-value only, guardrail | replace injured + streaming | 0.591 | +0.055 [+0.047, +0.063] |
- Category-targeting lineups (chasing the weekly opponent's weak categories) did not beat plain expected-value lineups at any setting tested: 0.606 against 0.608.
- Replacing injured players is the largest in-season move. Streaming at a low margin adds about 2.4 points and uses about 13 to 15 of the 50 acquisitions. A higher margin did worse on training seasons.
- The draft model with no in-season moves gains little over the proxy (+0.010, interval touches zero). Draft and management gains add up.
- The guardrail: take the highest model value among players whose consensus rank is no worse than the current pick number plus 8; if fewer than 3 qualify, take the best model value left.
11. What-if scenarios
We asked what happens when each assumption fails. 10 seasons, 800 simulations per cell. Edge is the full strategy against ADP proxy with replace-injured.
| Scenario | Strategy | Baseline | Edge | Draft-only edge |
|---|---|---|---|---|
| Base | 0.614 | 0.536 | +0.078 [+0.069, +0.087] | +0.050 |
| Opponents use a better board (no age or games-played) | 0.598 | 0.537 | +0.061 [+0.053, +0.070] | +0.043 |
| Opponents price age and games played exactly like us | 0.532 | 0.533 | -0.001 [-0.004, +0.003] | 0.000 |
| Opponents reach off-board twice as much | 0.652 | 0.605 | +0.047 [+0.041, +0.053] | +0.023 |
| Our first pick out 10 weeks | 0.593 | 0.519 | +0.074 [+0.066, +0.083] | +0.047 |
| Our first two picks out 10 weeks | 0.575 | 0.490 | +0.085 [+0.076, +0.094] | +0.056 |
| Waivers thin (25% of free agents available) | 0.608 | 0.542 | +0.066 [+0.058, +0.074] | +0.069 |
| Our projections noisy (sd 1.5 per player) | 0.571 | 0.536 | +0.035 [+0.026, +0.045] | +0.006 [0.000, +0.014] |
| Age and games-played terms removed from our model | 0.601 | 0.536 | +0.065 [+0.057, +0.072] | +0.036 |
The strategy never lost to the baseline. The draft edge disappears if the market already prices age and games played as we do, and shrinks to the management edge if projections are noisy. Injuries to our top picks lower both sides but leave the edge intact. In-season management is robust in every scenario.
A separate check on low-games-played former stars (Young, Tatum, Davis class): lighter shrinkage of thin-sample seasons did worse out of sample across 517 player-seasons (squared error 1.15 current against 1.21 to 1.38 lighter), and only 7 former stars were available to test an exception, which is too few to conclude anything.
12. Pick-1 deep dive and season-to-season variance
For the first draft slot we ran 250 leagues per season for 10 seasons (2,500 per strategy), paired against the baseline (ADP-proxy draft, replace injured). All-play category win rate with edge and 95 percent interval from simulation noise within seasons.
| Strategy | All-play | Edge vs baseline | Matchup win rate |
|---|---|---|---|
| Baseline: ADP proxy, replace injured | 0.539 | 0 | 0.586 |
| ADP proxy, replace injured + streaming | 0.565 | +0.026 [+0.025, +0.028] | 0.643 |
| Prior-value draft (no age), full management | 0.602 | +0.063 [+0.060, +0.066] | 0.720 |
| Model draft (age + games played), full management | 0.611 | +0.072 [+0.070, +0.076] | 0.751 |
| Blend draft, full management | 0.615 | +0.076 [+0.073, +0.079] | 0.757 |
On the test seasons alone (2022 to 2026) the model draft edge at slot 1 is +0.072 [+0.068, +0.077]. Across the ten draft slots the edge is about the same, so the first pick neither helps nor hurts it.
These intervals do not cover season-to-season variation. The model draft edge by season: 2017 +0.054, 2018 +0.054, 2019 +0.065, 2020 +0.091, 2021 +0.100, 2022 +0.133, 2023 -0.003, 2024 +0.063, 2025 +0.126, 2026 +0.043. It was positive in nine of ten seasons and about zero in 2023. A single season can land anywhere in that range.
Guardrail width (ADP plus 4, 8, 12, 30, or none) made no difference beyond noise, and the order reversed between train and test seasons, so width is not a source of edge.
Board spot-check vs category consensus
Separate from the simulations, we compared the top 15 of the draft board against external category-league rankings for 2026-27, checked on the morning of 15 October 2026. The league format is head-to-head nine-category with no punting. No board ranks were changed as a result. A player is flagged by one uniform rule: the absolute gap between board rank and the FantasyPros category expert consensus rank (ECR, six experts, dated 13 October) exceeds 3. The expert range is the spread of opinion, not a confidence interval, and the other sources are shown side by side rather than pooled into a consensus.
| Player | Board | FP ECR | Expert range | Hashtag | 9cat per-game | Board minus ECR |
|---|---|---|---|---|---|---|
| Nikola Jokic | 1 | 1 | 1-2 | 1 | 1 | 0 |
| Victor Wembanyama | 2 | 2 | 1-2 | 2 | 3 | 0 |
| Shai Gilgeous-Alexander | 3 | 4 | 3-5 | 3 | 2 | -1 |
| Luka Doncic | 4 | 3 | 3-4 | 4 | 4 | +1 |
| Cade Cunningham | 5 | 5 | 5-9 | 6 | 13 | 0 |
| Jayson Tatum | 6 | 8 | 7-13 | 7 | 12 | -2 |
| Jalen Johnson | 7 | 9 | 8-14 | 15 | 17 | -2 |
| Cooper Flagg | 8 | 10 | 5-18 | 16 | 30 | -2 |
| Anthony Edwards | 9 | 6 | 3-11 | 8 | 10 | +3 |
| Giannis Antetokounmpo (flag) | 10 | 15 | 6-34 | 5 | 20 | -5 |
| Scottie Barnes | 11 | 14 | 11-15 | 21 | 16 | -3 |
| Tyrese Maxey (flag) | 12 | 7 | 6-12 | 9 | 6 | +5 |
| Donovan Mitchell | 13 | 12 | 9-17 | 12 | 7 | +1 |
| Kevin Durant | 14 | 16 | 13-16 | 11 | 15 | -2 |
| Karl-Anthony Towns (flag) | 15 | 11 | 8-16 | 13 | 25 | +4 |
Negative means the board ranks the player earlier than the FantasyPros consensus. Hashtag is its displayed projection rank.
Flags
- Giannis Antetokounmpo: board 10, ECR 15 (expert range 6-34), so the board is 5 spots earlier. The disagreement among experts is large. The concern is free-throw volume and threes in a no-punt format, not a new injury adjustment.
- Tyrese Maxey: board 12, ECR 7 (range 6-12), so the board is 5 spots later. Free throws, threes and steals remain strong; the board's baseline discounts his Philadelphia usage.
- Karl-Anthony Towns: board 15, ECR 11 (range 8-16), so the board is 4 spots later. He fits the categories as a free-throw-friendly rebounding center and lacks blocks.
Anthony Edwards (board 9, ECR 6) is exactly 3 off and is not flagged under the rule. Jayson Tatum and Jalen Johnson are each one spot earlier than the best individual expert rank.
Tatum versus Giannis
The category consensus supports Tatum first for a no-punt league: Tatum ECR 8 (experts 7-13) against Giannis ECR 15 (experts 6-34). The sources are not unanimous. Hashtag shows Giannis 5 and Tatum 7, the 9cat per-game table has Tatum 12 and Giannis 20, NBA.com category rankings have Tatum 6 and Giannis 10, and RotoBaller has Tatum 5 and Giannis 7. There is no objective reason to flip the two individually, and no one-off adjustment was made.
Larger model disagreement
Jalen Johnson is board 7 against Hashtag 15 and 9cat per-game 17. Cooper Flagg is board 8 against Hashtag 16 and 9cat per-game 30. Both are upside-heavy first-round bets, not settled category consensus. Flagg's expected second-year jump and Johnson's free-throw and turnover profile explain the sensitivity.
Sources
- FantasyPros overall rankings: https://www.fantasypros.com/nba/rankings/overall.php
- Hashtag Basketball projections: https://hashtagbasketball.com/fantasy-basketball-projections
- 9cat rankings: https://9cat.app/rankings/
- NBA.com category top 150: https://www.nba.com/news/nba-fantasy-rankings-2-0-top-150-for-category-leagues
- RotoBaller category rankings: https://www.rotoballer.com/fantasy-basketball-rankings-sleepers-targets-and-values-categories/1951387
Appendix: code and run evidence
The code ran in the research workspace under /tmp/bt. These are scratch files with no external hosting. Pickled outputs hold every simulated draft and league result, and results.json holds the tables in this paper.
- Draft simulator
- data.py, sim.py, run.py (draft logic), an.py (analysis)
- Parameter search
- grid.py, 54 combinations on 2017-2021
- Weekly study
- inseason.py (season data), league.py (policies), exp1.py (train), exp2.py (test)
- Result files
- out.pkl, out2.pkl (draft runs, 44,000 drafts each); train.pkl, test.pkl (weekly)
- Table source
- dump.py writes results.json from the pickles
- Counts
- Study 1: 88,000 drafts across two runs. Study 2: 300 train leagues and 600 test leagues, 9,600 policy runs.