Edge-3 dossier — Adverse-selection / toxic-flow (follow the one-sided flow)
Verdict: FALSIFIED (out-of-sample, net of costs, vs the favorite null), and the OP's "wide-spread = retail/safe" is FALSE in our data. Ticket: T-0015 (powered re-run of T-0011). Script: research/03_toxic_flow.py. Date: 2026-07-24.
> History: provisional FALSIFIED on the Apr 18–29 window (~485k fills, > TEST ≈ 1 day; T-0011) → powered FALSIFIED on the FULL 53-day window > (29.6M fills, real 15-day June OOS; T-0015). The powered run removes the > provisional's deceptive "+1.45 edge vs the uniform null" by scoring against the > sharp favorite null instead: the honest edge is now cleanly negative > (−0.55), and the follow-flow return still *worsens* as flow gets *more* > one-sided.
> Honest > profitable. Two clean negatives: (1) following short-window one-sided > flow does not beat buying the favorite net of costs — it gets *worse* as > flow gets *more* one-sided; and (2) wide-spread and near-close fills do not > have worse taker outcomes. The adverse-selection *mechanism* is real for a > market-maker, but it is not a taker-side edge you can harvest by chasing flow.
---
Hypotheses
From the r/algotrading thread: the bot's residual bled to adverse selection — informed, one-sided flow lifts stale quotes right before the move.
- STRATEGY ("follow the toxic flow"): when the short-window signed flow into a market is strongly one-sided, take the flow direction and hold to resolution. If informed flow predicts the winner, following it should be +EV out of sample, net of costs.
- DIAGNOSTIC (the OP's unverified claim): "wide spread = retail/safe." If true, wide-spread (and/or near-close) takers should have worse realized outcomes than the rest.
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). - Causal per-market signed-flow features (a fill's signal uses only flow strictly before it): orient on outcome-0 (
token1); signed USD flow =size · (+buy/−sell) · (+token1/−token2). Rolling 30-min window sums (inclusive, then the fill's own contribution subtracted) givew_signed,w_abs,w_cnt; then one_sidedness = w_signed / w_abs ∈ [−1, 1],flow_speed, a spread proxy, and time-to-close. The rolling is computed in market-batches (identical groupby-rolling semantics) because one groupby.rolling over ~30M rows OOM-segfaults on this box. - Follow-flow universe: each fill with prior flow → a BUY of the flow-favored outcome at the current price, held to resolution. Equal-notional ($1/bet), returns capped to [−1, +1] (a chased cheap outcome cannot fabricate a mean). N = 29,178,815 fills; 3,085,977 are strongly one-sided (|osid| ≥ 0.60).
- Threshold from TRAIN ONLY: scan
|osid|buckets on TRAIN; band = contiguous span where the capped follow-return is > 0. - Costs:
polymarket_costs, sports defaultsfee_rate=0.05,spread=0.02. - Null (standard control): the FAVORITE null — back the priced-favorite side of the same opportunities; the sharp control that strips the uniform null's extreme-price cost artifact. The uniform null (follow-everywhere) is reported for context only. Verdict = TEST
edge_vs_naive> 0 and TESTmean_net > 0. - vbt.pro
2026.4.7simulates the TEST book (hourly equal-weight, gate L1.2).
TRAIN scan — follow-flow return by one-sidedness (capped, net of costs)
The hypothesis predicts the follow-flow return should rise with one-sidedness. It does the opposite — every bucket is gross-negative once capped, and it *worsens* as flow gets more one-sided (chasing a stronger move buys a more extreme, more expensive-to-cross price):
| |one-sidedness| | N | win_rate | mean_gross (capped) | mean_net (capped) |
|---|---|---|---|---|
| 0.0–0.1 | 5,173,490 | 0.446 | −0.1926 | −0.7768 |
| 0.1–0.2 | 4,031,132 | 0.375 | −0.2959 | −0.7004 |
| 0.2–0.3 | 3,207,602 | 0.331 | −0.3614 | −0.8047 |
| 0.3–0.4 | 2,263,085 | 0.265 | −0.4820 | −0.9853 |
| 0.4–0.5 | 1,559,235 | 0.211 | −0.5870 | −1.1323 |
| 0.5–0.6 | 1,097,293 | 0.173 | −0.6619 | −1.2309 |
| 0.6–0.7 | 674,702 | 0.153 | −0.7038 | −1.4474 |
| 0.7–0.8 | 417,752 | 0.125 | −0.7626 | −1.9171 |
| 0.8–0.9 | 244,581 | 0.131 | −0.7598 | −2.5090 |
| 0.9–1.0 | 681,792 | 0.342 | −0.5006 | −3.5558 |
No bucket is gross-positive, so the band derivation falls back to the least-one-sided bucket, |osid| ∈ [0.00, 0.10) — the exact opposite of the thesis (same fallback as the provisional run, now on 30M fills).
TRAIN vs TEST — the verdict table
A — follow-flow in the TRAIN-derived band [0.00, 0.10) vs buy-the-favorite (favorite null)
| split | N | gross | net | sharpe | naive_net | edge_vs_naive |
|---|---|---|---|---|---|---|
| TRAIN | 5,173,490 | −0.1926 | −0.7768 | −0.3008 | −0.0272 | −0.7497 |
| TEST | 2,717,883 | −0.1516 | −0.5881 | −0.2595 | −0.0365 | −0.5516 |
B — follow-flow when |osid| ≥ 0.60 (the ticket's literal "strongly one-sided") vs favorite null
| split | N | gross | net | sharpe | naive_net | edge_vs_naive |
|---|---|---|---|---|---|---|
| TRAIN | 2,044,007 | −0.6474 | −2.3589 | −0.6295 | −0.0272 | −2.3317 |
| TEST | 1,041,970 | −0.6525 | −1.9138 | −0.5894 | −0.0365 | −1.8772 |
Against the sharp favorite null, both runs are cleanly negative on both splits: the band strategy loses ~59% per bet (TEST net −0.588, edge −0.552) and the literal "strong flow" cut loses ~191% (TEST net −1.914, edge −1.877; chasing an extreme move at a thin, cheap price is dominated by the cost of crossing the spread). FALSIFIED. For context, vs the *uniform* null the band run shows a misleading edge_vs_naive = +0.25 — positive only because follow-everywhere (−0.84) is even more catastrophic; that is the trap the favorite null removes.
Robustness — one strongest-|osid| position per market, |osid| ≥ 0.60 (de-clustered): TEST net −0.3966, edge_vs_naive −0.3722, N=26,655 — negative on both splits. De-clustering does not rescue the edge.
Tail-variance honesty
| run | TEST N | median_net | mean_net (capped) | mean_gross UNCAPPED | min | max | top-1% |PnL| share |
|---|---|---|---|---|---|---|---|
| band (A) | 2,717,883 | −1.0374 | −0.5881 | +0.0102 | −11.05 | +0.955 | 0.084 |
| strong (B) | 1,041,970 | −1.1204 | −1.9138 | −0.0069 | −11.05 | +0.955 | 0.051 |
- The band run's uncapped gross mean is a *whisker positive* (+0.0102) while its capped, cost-paid net is −0.588. That gap is the finding: any apparent "edge" from following flow lives entirely in the fat right tail of a handful of chased-cheap-outcome hits (min −11.05 shows how deep the short/cheap tail runs). Cap the tail and pay the spread, and it is gone.
- Sizing is equal-notional ($1/bet); returns are capped to [−1, +1] for the verdict — no handful of longshots can fabricate (or hide) the result.
vbt.pro simulation (gate L1.2)
vbt.pro 2026.4.7, hourly equal-weight book over the TEST window (375 bars across the ~15-day June era, band run, cost-adjusted): total_return −100.0%, Sharpe(1h-ann) −67.4, max_drawdown −100.0%. Unambiguously negative and consistent with the harness verdict. Pandas equity curve emitted as fallback.
OP DIAGNOSTIC — is "wide-spread = retail/safe" true? NO.
1 — taker outcome by spread-width proxy quintile (price range over the local 30-min window; mean_taker = the taker's own realized return; ~5.93M fills/quintile):
| spread quintile | N | mean_taker (uncapped) | mean_taker (capped) | median | win_rate | mean_price |
|---|---|---|---|---|---|---|
| Q1_tight | 5,930,355 | −0.0090 | +0.1239 | −0.001 | 0.544 | 0.550 |
| Q2 | 5,930,355 | −0.0000 | +0.1371 | −0.001 | 0.515 | 0.514 |
| Q3 | 5,930,355 | +0.0027 | +0.1414 | +0.001 | 0.514 | 0.514 |
| Q4 | 5,930,355 | −0.0040 | +0.1317 | +0.001 | 0.517 | 0.515 |
| Q5_wide | 5,930,355 | −0.0375 | +0.1434 | +0.001 | 0.537 | 0.535 |
The retail story predicts Q5_wide should be the worst. On the tail-robust capped measure the quintiles are essentially flat (+0.124 … +0.143), and Q5_wide is NOT the worst — it is indistinguishable from the middle. Wide-spread fills do NOT have worse outcomes → "wide spread = retail/safe" is FALSE in our data.
2 — taker outcome by time-to-close:
| time to close | N | mean_taker (uncapped) | mean_taker (capped) | win_rate | mean_price |
|---|---|---|---|---|---|
| <1h | 1,161,325 | +0.0282 | +0.1455 | 0.628 | 0.628 |
| 1–6h | 21,923,118 | −0.0036 | +0.1404 | 0.528 | 0.527 |
| 6–24h | 5,456,393 | −0.0287 | +0.1175 | 0.501 | 0.502 |
| 1–3d | 899,429 | −0.0690 | +0.1122 | 0.485 | 0.505 |
| >3d | 211,510 | −0.0865 | +0.1332 | 0.519 | 0.524 |
Near-close (<1h) takers are not the worst — they have the highest capped outcome (+0.146) and win 63%, the direction adverse selection predicts (informed flow picking off stale quotes), the opposite of "near-close = safe." Here the price-level confound is mild (near-close mean price 0.628 ≈ its win-rate), so the read is clean: there is no evidence that wide-spread or near-close identifies *worse-informed* (retail, "safe") flow.
Verdict
FALSIFIED (powered). "Follow the toxic flow" does not beat buying the favorite out of sample and is not profitable net of costs — following flow gets *worse* as flow gets *more* one-sided (TRAIN scan monotone-negative), and any apparent edge is a fat-tail / longshot-chasing mirage that dies under equal-notional capped sizing and the cost of crossing a thin spread. Separately, the OP's "wide-spread = retail/safe" is FALSE: wide and narrow spread carry the same taker outcome, and near-close flow is if anything *more* (not less) informed. The adverse-selection mechanism is real for a market-maker (a reason to widen/skew quotes), but it is not a taker-side statistical edge in this dataset.
Caveats
- The favorite null is the honest control. vs the uniform null the band run shows a deceptive +0.25 "edge" (the null is even worse); vs the favorite null it is a clean −0.55. Read the favorite-null row, not the uniform one.
- Entry-time split + fill-clustering: the all-fills runs weight high-volume markets by fill count; the de-clustered per-market run agrees in sign (negative, TEST edge −0.372). Event-level weighting is the stricter unit (deferred).
- Spread proxy is a realized-dispersion surrogate, not the book. We have no historical order-book snapshots, so "spread width" is proxied by local traded-price range. A true top-of-book spread could sharpen the diagnostic — but the *flat capped outcome across quintiles* (now on 5.9M fills/quintile) is a strong prior that the retail-vs-spread link is weak at best.
- Cost sensitivity: chasing one-sided flow buys extreme/cheap prices where the fractional cost of crossing the spread is largest; the result is *more* cost-sensitive than Edge-1, not less. Even a materially cheaper execution path would have to overcome a *gross* signal that does not point the right way.
Reproduce
uv run --extra backtest python research/03_toxic_flow.py
# tunable: ODDS_FLOW_WINDOW=30min
uv run pytest -q → green.