NSBA Draft Analyticsembargoed · 2026-06-06

21_adp_market.md

21 — ADP & Market (In)Efficiency

21 — ADP & Market (In)Efficiency

Date: 2026-05-30 Script: scripts/adp_market.py (reproducible; tidy tables only) Outputs: outputs/adp_table.csv (189 analyzed picks, one row per matched pick) Env: /home/david/code/nsba/.venv/bin/python

LEAD CAVEAT — tiny, leaky, three-season sample. This rests on 3 draft seasons and only 113 picks that have any realized game value (and just 11 in nsba1, where game sheets use nicknames so the pick→game link barely resolves). Realized "value" is same-season game PPTF for nearly every row, so a player's draft happened before the very games we score them on — but nsba3's value model (proj_pptf) is built from those same games (41/42 nsba3 drafted players are single-season), so the nsba3 "edge" number is circular and is reported only as a ceiling. The honest, ex-ante recoverable-edge figure comes from nsba2 alone. Read every number as directional.

The question: did the field draft on the combine everyone saw, and if rivals do, how much value is left on the table by drafting on projected game value instead?


(a) Did the field draft on combine rank? — YES, strongly, every season

Spearman ρ between draft pick order and the combine score each manager could see (raw_overall = the raw combine total; theta_overall = the IRT-debiased ability from finding 10). Sign convention: a perfectly combine-driven draft gives ρ(pick, score) = −1 (pick #1 = highest score).

season n ρ(pick, raw combine) ρ(pick, theta)
nsba1_2022 54 −0.71 −0.53
nsba2_2023 51 −0.75 −0.75
nsba3_2025 67 −0.67 −0.62

All p < 0.0001. Draft position tracks the raw combine tightly (|ρ| ≈ 0.67–0.75) in all three drafts. The field overwhelmingly drafts the board the combine produced — the "Anurag drafts on vibe/combine" prior generalizes to the whole field. The raw combine explains pick order at least as well as the debiked theta (equal in nsba2, better in nsba1/nsba3), i.e. managers anchor on the visible raw score, not on any bias-correction — exactly the exploitable behavior.


(b) ADP vs realized value — steals & busts

Realized value = the player's game PPTF that season (toss-up points per faced toss-up; finding 13's key rate). Within each season we regress realized PPTF ~ overall_pick and take the residual = value above/below what the pick slot predicts. Positive residual at a late pick = steal; negative at an early pick = bust.

Pick order does forecast value, but weakly and unevenly:

season n r(pick, realized PPTF) slope (PPTF lost per pick)
nsba1_2022 11 −0.78 −0.0106
nsba2_2023 60 −0.64 −0.0100
nsba3_2025 42 −0.18 −0.0018

In nsba3 the draft order barely predicted who actually produced (r = −0.18). nsba2 is the only season where pick order is a respectable value signal.

Top steals (most value above slot):

season pick rd player realized PPTF combine rank residual
nsba3 45 5 Kaden W 0.80 27 +0.62
nsba2 3 1 Yufei Chen 1.17 4 +0.58
nsba2 16 2 Geo 1.02 (no combine) +0.55
nsba2 1 1 Sanjay Suresh 1.03 1 +0.42
nsba2 51 5 Ray 0.51 48 +0.40
nsba1 33 3 Vish 0.74 56 +0.39

Top busts (most value below slot):

season pick rd player realized PPTF combine rank residual
nsba2 36 3 Rohan Dhillon −0.14 11 −0.40
nsba2 9 1 Sean 0.15 (no combine) −0.39
nsba2 39 4 Sidhant Chaliha −0.11 23 −0.35
nsba3 48 5 Kshitij Tomar −0.16 38 −0.34
nsba2 4 1 Connor Zhao 0.29 6 −0.29
nsba2 12 1 owen fei 0.24 6 −0.26

Pattern: the busts cluster among early picks with strong combine ranks who under-delivered in games (Rohan Dhillon combine#11→PPTF −0.14; Connor Zhao #6, owen fei #6) — i.e. the combine over-rated them and the field followed the combine. The steals are late picks the combine under-rated (Kaden W #27→0.80; Ray #48, Vish #56). This is the signature of a market anchored on a noisy, gameable signal. Full per-pick residuals are in outputs/adp_table.csv.


(c) The recoverable market edge

If rivals draft combine-greedy (best available raw_overall), how much realized value does a manager capture by drafting best-available by value instead? We simulate one seat in a T-team serpentine where every other seat picks combine-greedy, and average the per-pick realized-PPTF lift over the combine player we'd otherwise have taken, across all seat positions.

First, the forecasting gap the edge comes from — how well each ordering predicts realized PPTF among drafted players with both signals:

season ρ(combine raw, realized) — rivals' model ρ(actual pick order, realized) ρ(theta, realized)
nsba2_2023 +0.43 +0.72 +0.43
nsba3_2025 +0.36 +0.22 +0.34

Key asymmetry. In nsba2 the actual draft order (+0.72) forecast value far better than the combine alone (+0.43) — the field added real scouting information on top of the combine. In nsba3 the draft order (+0.22) was worse than the combine (+0.36): the field over-thought it, moving players away from their combine rank in the wrong direction. So the edge is not "ignore the combine" — it is "use the combine, de-biked, and don't add noise."

Per-pick edge (realized PPTF) of a value-ordered draft vs combine-greedy rivals:

season ORACLE (hindsight value order) PROJ-MODEL (ex-ante proj_pptf)
nsba2_2023 +0.240 PPTF (~+36 pts/szn/slot) +0.090 PPTF (~+13 pts/szn/slot)
nsba3_2025 +0.217 PPTF (~+33 pts/szn/slot) +0.217 (circular — see below)

Bottom line on edge: a defensible ~13 toss-points/season/slot is recoverable by drafting on projected game value rather than the visible combine, with a ceiling near +35. The edge exists because the field anchors on the raw combine (ρ(combine, value) only ≈ 0.36–0.43) instead of the better debiked/game-based projection, and in nsba3 actively degraded it.


What this means for the nsba4 draft

  1. The field will draft the visible combine board (|ρ| ≈ 0.7). Expect early picks to follow raw_overall closely.
  2. Fade combine-inflated early names; target combine-underrated late values. The de-biked combine theta (finding 10) and projected game value (finding 13) diverge most from raw combine exactly where the steals/busts live.
  3. Realistic edge ≈ 13 toss-points per draft slot per season vs a combine-anchored field, ceiling ~35. Concentrate it in the middle rounds — round 1 is efficient (ρ(pick,value) strong, top combine = top players: Yufei, Sanjay both #1-area combine AND top realized), but rounds 3–5 is where the field's combine-anchoring leaks value (Kaden W, Ray, Vish were rounds 3–5 steals).

Limitations / threats (read before trusting)

  1. n is tiny and nsba1 is nearly blind — 11 pick→value links in nsba1 (nickname/real-name mismatch, per data dictionary), so nsba1 is excluded from the edge sim and contributes little. The whole edge claim leans on nsba2 (60 picks).
  2. Same-season realized value = drafted-for-season leakage. We score picks on the season they were drafted for; the proj-model partly reuses those games. The nsba2 number is the only one with enough prior-season history to be a fair ex-ante test, and even it is optimistic. nsba3's edge is circular and reported as a ceiling only.
  3. Survivorship (ties to redteam T5, finding 91). Players appear here only if they drafted and played ≥2 clean games. Combine-high no-shows/tankers — the exact players who break the combine→value link — are missing, which makes the combine look more predictive than it is for the population you actually draft from. The true recoverable edge is therefore likely larger than the +13 estimate, not smaller.
  4. PPTF, not wins. Value = individual toss-up rate. We carry win_shares columns in adp_table.csv but the headline uses PPTF; win-share-based ADP (schedule-confounded, redteam T6) is left for a team-construction finding.
  5. nsba3 PPTF uses estimated TUH (paired layout, no heard-counts) — the whole nsba3 column is a games×rate proxy, which is part of why its draft-order signal is so weak (r = −0.18). Trust nsba2's PPTF-based ADP most.
  6. Captains/keepers excluded (overall_pick = 0, 14 nsba3 rows) — they have no linear ADP slot, so they can't be steals/busts here.
  7. No multiplicity control on the named steals/busts; with ~113 picks, individual residuals near ±0.3 PPTF are flags for the board, not significance claims.

Reproduce

/home/david/code/nsba/.venv/bin/python scripts/adp_market.py

Prints the (a)/(b)/(c) tables above and writes outputs/adp_table.csv.

outputs/adp_table.csv schema (one row per matched, non-captain pick)

season, overall_pick, round, team, player_raw, discord_tag, canonical_id, match_confidence, raw_overall, theta_overall, raw_rank, theta_rank, pptf_same, ws_same, pptf_next, ws_next, realized_pptf, realized_ws, realized_src, val_resid, steal_score. realized_src ∈ {same, next, none}; steal_score = val_resid = realized PPTF minus the within-season pick-slot regression prediction (high = steal, low = bust).


NSBA Draft Analytics · embargoed until after the SSB draft · ← hub