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.

Run 2, training seasons 2017-2021 (2,000 drafts per strategy)
StrategyRoto ptsAvg rankTop 3 %Diff vs ADP95% CI
Rank blend, alpha=0.355.83.6655+2.01[+1.05, +2.99]
Projection, guardrail ADP+855.53.7054+1.74[+0.61, +2.84]
Rank blend, alpha=0.555.53.7454+1.72[+0.89, +2.55]
Projection, guardrail ADP+2055.23.8052+1.40[+0.27, +2.53]
ADP proxy (baseline)53.84.24460-
Balance k=1 + guardrail 2053.44.4242-0.41[-1.39, +0.52]
Shrunk projection52.74.5242-1.08[-2.34, +0.22]
Projection + category balance, k=0.552.24.7140-1.59[-2.77, -0.46]
Projection + category balance, k=150.55.2729-3.33[-4.41, -2.35]
Last-season value rank49.95.3933-3.89[-5.99, -1.74]
Projection + category balance, k=248.95.7223-4.91[-5.77, -3.99]
Run 2, test seasons 2022-2026 (2,000 drafts per strategy)
StrategyRoto ptsAvg rankTop 3 %Diff vs ADP95% CI
Projection, guardrail ADP+853.24.4943+0.11[-1.10, +1.30]
ADP proxy (baseline)53.14.53440-
Balance k=1 + guardrail 2052.84.6540-0.31[-1.48, +0.93]
Rank blend, alpha=0.552.24.7839-0.91[-1.85, +0.09]
Rank blend, alpha=0.351.84.9138-1.32[-2.56, -0.05]
Projection, guardrail ADP+2051.74.9538-1.41[-2.86, +0.04]
Projection + category balance, k=0.548.85.8227-4.33[-6.23, -2.51]
Projection + category balance, k=148.35.9125-4.74[-6.15, -3.31]
Projection + category balance, k=247.86.0221-5.31[-6.50, -4.12]
Shrunk projection47.76.0428-5.42[-7.70, -3.12]
Last-season value rank45.86.5318-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 best-looking variant on the training seasons was indistinguishable from the baseline on the test seasons.

Guardrail ADP+8: roto points versus the ADP proxy
+1.74+0.11
TrainTest

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.

Run 1 (default parameters), test seasons 2022-2026
StrategyRoto ptsAvg rankTop 3 %Diff vs ADP95% CI
Projection, guardrail ADP+854.04.1848+0.75[-0.62, +2.19]
ADP proxy (baseline)53.34.47450-
Rank blend, alpha=0.553.04.4943-0.24[-1.21, +0.84]
Shrunk projection47.76.0026-5.52[-7.65, -3.37]
Last-season value rank45.96.5119-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).

Test seasons 2022-2026, active opponents
PolicyCat win %Diff vs static (pts)95% CIAdds/season
Replace injured player53.1+7.20[+6.40, +8.00]8.4
Waiver, 2-week value, margin 251.6+5.70[+4.70, +6.70]14.3
Waiver, 4-week value, margin 1051.6+5.70[+4.80, +6.70]7.8
Stream, margin 251.1+5.10[+4.20, +6.10]5.3
Static roster (lineup only)45.90-0.0
Test seasons 2022-2026, passive opponents
PolicyCat win %Diff vs static (pts)95% CIAdds/season
Replace injured player60.5+6.40[+5.80, +7.20]8.4
Waiver, 2-week value, margin 259.2+5.10[+4.30, +6.00]14.3
Waiver, 4-week value, margin 1059.2+5.10[+4.30, +5.90]7.8
Stream, margin 258.6+4.60[+3.80, +5.40]5.3
Static roster (lineup only)54.10-0.0
Training grid 2017-2021, active opponents, all 22 policies
PolicyCat win %Diff vs static (pts)95% CIAdds/season
Replace injured player53.5+5.80[+4.90, +6.60]8.3
Waiver, 2-week value, margin 252.2+4.40[+3.10, +5.70]15.7
Waiver, 4-week value, margin 1052.2+4.50[+3.40, +5.60]9.4
ros2_552.1+4.40[+3.20, +5.60]10.2
ros4_552.1+4.40[+3.20, +5.60]12.8
ros8_1051.9+4.20[+2.90, +5.30]11.9
ros1_251.6+3.90[+2.60, +5.20]12.6
Stream, margin 251.6+3.90[+3.00, +4.80]6.0
ros2_1051.5+3.80[+2.90, +4.60]6.1
ros8_551.5+3.80[+2.60, +4.90]15.4
stream051.5+3.80[+2.70, +4.90]14.2
ros4_251.4+3.70[+2.40, +4.90]17.8
ros1_551.3+3.60[+2.70, +4.40]6.8
ros8_051.2+3.50[+2.30, +4.60]23.4
ros8_251.2+3.50[+2.40, +4.60]18.5
ros4_050.9+3.20[+2.10, +4.30]26.0
ros2_050.7+3.00[+1.80, +4.20]29.8
ros1_050.1+2.40[+1.00, +3.70]38.2
ros1_1049.7+2.00[+1.50, +2.50]2.8
stream549.3+1.60[+1.20, +2.00]2.5
stream1047.9+0.20[0.00, +0.30]0.6
Static roster (lineup only)47.70-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

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.

Effect of age on next-season value, relative to a model with last two seasons only (z-score points, 95% CI)
AgeEffect95% 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.

Gain in test squared error from adding age interactions (95% CI)
GroupGainHolds 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

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.

Test seasons 2022-2026
DraftIn-season managementAll-playEdge vs ADP-proxy + replace injured (95% CI)
ADP proxyreplace injured0.5360
ADP proxyreplace injured + streaming0.561+0.026 [+0.022, +0.030]
Model (guardrail ADP+8)none0.547+0.010 [0.000, +0.021]
Model (guardrail ADP+8)replace injured0.584+0.049 [+0.039, +0.058]
Model (guardrail ADP+8)replace injured + streaming0.608+0.072 [+0.064, +0.080]
Model blend 50/50 with ADPreplace injured + streaming0.604+0.068 [+0.062, +0.074]
Prior-value only, guardrailreplace injured + streaming0.591+0.055 [+0.047, +0.063]

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 results (all-play, edge with 95% CI)
ScenarioStrategyBaselineEdgeDraft-only edge
Base0.6140.536+0.078 [+0.069, +0.087]+0.050
Opponents use a better board (no age or games-played)0.5980.537+0.061 [+0.053, +0.070]+0.043
Opponents price age and games played exactly like us0.5320.533-0.001 [-0.004, +0.003]0.000
Opponents reach off-board twice as much0.6520.605+0.047 [+0.041, +0.053]+0.023
Our first pick out 10 weeks0.5930.519+0.074 [+0.066, +0.083]+0.047
Our first two picks out 10 weeks0.5750.490+0.085 [+0.076, +0.094]+0.056
Waivers thin (25% of free agents available)0.6080.542+0.066 [+0.058, +0.074]+0.069
Our projections noisy (sd 1.5 per player)0.5710.536+0.035 [+0.026, +0.045]+0.006 [0.000, +0.014]
Age and games-played terms removed from our model0.6010.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.

Draft slot 1, all 10 seasons
StrategyAll-playEdge vs baselineMatchup win rate
Baseline: ADP proxy, replace injured0.53900.586
ADP proxy, replace injured + streaming0.565+0.026 [+0.025, +0.028]0.643
Prior-value draft (no age), full management0.602+0.063 [+0.060, +0.066]0.720
Model draft (age + games played), full management0.611+0.072 [+0.070, +0.076]0.751
Blend draft, full management0.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.

Top 15: board rank vs category rankings
PlayerBoardFP ECRExpert rangeHashtag9cat per-gameBoard minus ECR
Nikola Jokic111-2110
Victor Wembanyama221-2230
Shai Gilgeous-Alexander343-532-1
Luka Doncic433-444+1
Cade Cunningham555-96130
Jayson Tatum687-13712-2
Jalen Johnson798-141517-2
Cooper Flagg8105-181630-2
Anthony Edwards963-11810+3
Giannis Antetokounmpo (flag)10156-34520-5
Scottie Barnes111411-152116-3
Tyrese Maxey (flag)1276-1296+5
Donovan Mitchell13129-17127+1
Kevin Durant141613-161115-2
Karl-Anthony Towns (flag)15118-161325+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.