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) |
- ORACLE orders by realized value itself → a hard upper bound (~33–36 toss-points per slot per season). Not achievable; it knows the answer.
- PROJ-MODEL orders by finding 13's reliability-shrunk
proj_pptf, an ex-ante model. For nsba2 this is a fair test (24/60 drafted players carry prior-season history) and yields a realistic recoverable edge of ≈ +0.09 PPTF per pick, ~13 toss-points per slot per season. Over a 5-round draft that is ~65 toss-points of roster value a value-drafting manager gains on a combine-anchored field — material in a league where the median player produces under 0.4 PPTF. - The nsba3 proj-model number equals the oracle (+0.217) and must be discarded:
41/42 nsba3 drafted players are single-season, so their
proj_pptfis just their shrunk same-season realized PPTF — the model is scoring itself. It confirms the mechanism (value-greedy beats combine-greedy) but not the magnitude.
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
- The field will draft the visible combine board (|ρ| ≈ 0.7). Expect early
picks to follow
raw_overallclosely. - 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. - 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)
- 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).
- 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.
- 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.
- PPTF, not wins. Value = individual toss-up rate. We carry
win_sharescolumns inadp_table.csvbut the headline uses PPTF; win-share-based ADP (schedule-confounded, redteam T6) is left for a team-construction finding. - 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.
- Captains/keepers excluded (overall_pick = 0, 14 nsba3 rows) — they have no linear ADP slot, so they can't be steals/busts here.
- 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).