Edge-2 dossier — Drift-to-resolution / closing-line value (does price predict outcome?)
Verdict: FALSIFIED as a tradeable edge (out-of-sample, net of costs, vs the favorite null) — while the underlying *information* claim (closing-line value) is CONFIRMED, and more strongly than before. Ticket: T-0015 (powered re-run of T-0012). Script: research/04_drift.py. Date: 2026-07-24.
> History: provisional FALSIFIED on the Apr 18–29 window (847 markets, > TEST ≈ 1 day / ~2 hourly bars; T-0012) → powered FALSIFIED on the FULL > 53-day window (35,797 qualifying markets, real 15-day June OOS; T-0015). CLV > went from "confirmed but noisy" to strongly confirmed (TEST Brier 0.022 for > the late price vs 0.201 for the early price).
> Honest > profitable. The late price does carry real outcome information > (drift toward the winner is strong and measurable), but by the time the drift > reveals the winner the price has already moved there — the signal is > un-tradeable net of costs and adds nothing over buying the late > favorite (which Edge-1 already falsified).
---
Hypothesis
Within a resolved sports market's life the price trajectory carries outcome information — either momentum (price drifts *toward* the eventual winner = closing-line value) or mean-reversion (the drift over-shoots and the fallen side is under-priced). Test which, honestly; and separately: does the late price predict the outcome better than the early price?
Method (powered — T-0015)
- Universe:
build_resolved_bets_full(use_cache=True)→ 29.6M resolved sports fills across 83,019 markets, span 2026-04-18 → 06-11 (53 days). - Split:
calendar_split— TRAIN = Apr18→May26, TEST = May27→Jun11 (a genuine 15-day June OOS era). - Canonical price axis: each fill's price re-expressed as the outcome-0 price
p0(priceif token1 else1−price) so a market's fills are one series. (Trajectory build is fully vectorised — masked groupby VWAP sums — as a per-market Python loop is untenable on ~30M fills / ~83k markets.) - Per-market trajectory (causal): size-weighted VWAP in two disjoint windows —
early_p0= first hour of fills,late_p0= last hour of fills;drift0 = late_p0 − early_p0. Keep markets with fill span ≥ 2h and ≥ 10 fills → 35,797 qualifying markets, mean |drift0| = 0.356. - Entry point (no look-ahead): enter at the late VWAP, at the market's last pre-close fill time — the drift is fully observed by then, and the entry never touches resolution.
- Two-sided harness universe: both sides of every market as buys at the late price. The strategy selects one side per market — drifted-up (momentum) or drifted-down (reversion) — with the direction chosen on TRAIN only.
- Null (standard control): the FAVORITE null = buy the late-favorite of every market — the sharp control that strips the uniform null's extreme-price cost artifact. The uniform null (coin-flip on side) is reported for context only.
- Costs:
polymarket_costs, sports defaultsfee_rate=0.05,spread=0.02. - vbt.pro
2026.4.7simulates the TEST book (hourly equal-weight, gate L1.2).
TRAIN direction pick
Against the favorite null, momentum (edge_vs_naive TRAIN −0.0219) beats mean-reversion (−7.08 — the huge magnitude is the extreme-price cost artifact of buying crushed longshots, not skill). Chosen direction: MOMENTUM. The sign selects the direction; it is decided on TRAIN.
TRAIN vs TEST — the verdict table
EDGE-2: MOMENTUM, entered at LATE price, vs buy-the-late-FAVORITE (favorite null)
| split | N | gross | net | sharpe | naive_net | edge_vs_naive |
|---|---|---|---|---|---|---|
| TRAIN | 23,002 | +0.0217 | −0.0006 | −0.0016 | +0.0213 | −0.0219 |
| TEST | 12,717 | +0.0370 | +0.0153 | +0.0219 | +0.0464 | −0.0311 |
The momentum edge vs the favorite null is negative on both splits (−0.0219 → −0.0311). Even though momentum's TEST net is marginally positive (+0.0153 — it makes a little money in absolute terms on the powered window), buying the late favorite makes more (+0.0464), so the drift signal adds nothing over favorite-buying. This is the decisive falsification. (For context, vs the *uniform* null momentum shows a big +2.78 "edge," but that magnitude is purely the cost model's extreme-price slippage — the favorite null is the honest read.)
Supporting / adversarial runs (context, not the verdict)
- EDGE-2c — contested markets only (
late_p0 ∈ [0.15, 0.85], N_test=2,097): vs the favorite null,edge_vs_naive−0.066 TRAIN / −0.137 TEST — negative on both splits. Even where the market is genuinely undecided, betting the drift does not beat buying the (contested) favorite. The provisional run's TRAIN→TEST sign flip here does not reproduce; the powered answer is a clean negative. - MIRROR (mean-reversion): the rejected direction is deeply negative everywhere (TEST net −5.54); the huge magnitude is the extreme-price cost artifact, not a real loss rate.
Closing-line value — early vs late price predicting the outcome
CONFIRMED (strongly). The late price is a *far* better outcome predictor than the early price, on both splits:
| split | N | brier_early | brier_late | ll_early | ll_late | late better? |
|---|---|---|---|---|---|---|
| TRAIN | 23,064 | 0.2049 | 0.0144 | 0.5925 | 0.0556 | YES |
| TEST | 12,733 | 0.2010 | 0.0216 | 0.5892 | 0.0849 | YES |
Late-price calibration is tight where confident (the near-decided buckets 0.0–0.1 and 0.9–1.0 hold 30k of 35.8k markets at ~0 gap); the mid buckets are directionally correct (e.g. 0.5–0.6: emp win-rate 0.69 vs price 0.55). The information is real — that is exactly why it is not tradeable: it is already in the price by the time you can act on it.
Tail-variance honesty & sizing
| unit | TEST N | median_net | mean_net | min | max | top-1% |PnL| share |
|---|---|---|---|---|---|---|
| MOMENTUM (chosen) | 12,717 | −0.0070 | +0.0153 | −11.05 | +65.38 | 0.196 |
- Median (−0.0070) vs mean (+0.0153): the positive mean is entirely a fat-tail artifact — the median bet *loses* money, and a single +65× outlier (a drifted-up longshot that resolved in) plus a handful of others make the top-1% carry 19.6% of absolute PnL. This is the tail-honesty red flag: the "profit" is not broad-based. Cap it or de-cluster and it evaporates.
- Sizing: equal-notional per bet. A price-floor ≥ 0.05 variant is nearly identical (
mean_net +0.0190, N=12,680) — momentum mostly buys the drifted-up (higher-priced) side, so few ultra-longshot entries exist to cap.
vbt.pro simulation (gate L1.2)
vbt.pro 2026.4.7, hourly equal-weight book over the TEST window (364 bars, cost-adjusted): total_return −99.7%, max_drawdown −102%, Sharpe(1h-ann) +3.58 (directional only — the equity is driven to ~0 by the occasional −100% bar despite a positive-mean tail). The pandas fallback matches (final_equity 0.0030).
Verdict
FALSIFIED (powered) as a tradeable edge; CLV CONFIRMED (strongly) as a diagnostic. Betting the drift-toward side, entered at the late price, does not beat buying the late favorite (edge_vs_naive −0.031 TEST) and its marginal absolute "profit" (+0.0153) is a fat-tail artifact (median −0.0070; top-1% carries 19.6% of |PnL|). The late price is a much better outcome predictor than the early price (TEST Brier 0.022 vs 0.201) — the drift-carries-information hypothesis is true — but the information is already priced by entry time, and what remains is indistinguishable from favorite-buying, which Edge-1 already falsified.
Caveats
- The positive absolute TEST net is a tail, not an edge. Median is negative; the mean rides one +65× outcome. Do not read +0.0153 as a tradeable return.
- "Late price" causality / execution. Late = last-hour VWAP of *observed* fills, strictly before the last pre-close fill; it never reads resolution. But *entering at the late VWAP* assumes you can transact near it — a real entry pays the then-current spread (modeled) and could face worse fills in a thin book.
- Uniform-null magnitude is a cost artifact. The
(spread/2)/priceslippage term explodes below ~0.05, so any run whose null buys crushed longshots shows huge |edge|. The favorite null is the artifact-free read; trust it. - Overlap with Edge-1. Because the drifted-up side ≈ the late favorite, this edge lives in the same space as favorite-longshot; the favorite null is the correct control and it kills the marginal signal.
Reproduce
uv run --extra backtest python research/04_drift.py
# windows: ODDS_EARLY_WIN_H=1.0 ODDS_LATE_WIN_H=1.0 ODDS_MIN_FILLS=10
uv run pytest -q → green.