How the “longshot reversion” lane is actually implemented in the pm-arb Rust crate, the cohort-validated empirical edge, and the parameter tuning history. This is the implementation/tuning companion to the analysis in top-trader-edge — read that first for the why, this for the what we built and how we tuned it.
Major corrections (2026-05-30) — read before the rest of this note
Yesterday’s framing of this note had two foundational errors that propagated everywhere. Both are fixed inline below; this callout calls them out so prior wikilinks don’t mislead anyone:
0x75cc3bis NOT the top trader of the cohort. They are wallet #2 by 30-day ROI (+40.1%). The actual #1 is0xb27bc932bf8110d8f78e55da7d5f0497a18b5b82at +48.1% / 30d (same band [5–10¢], same tiw window [0.66, 0.93] used in yesterday’s RE). The original reverse-engineering pass — and yesterday’s “we captured the top-trader edge” claim — was actually run against the second-best wallet. The RE did still find real edge (the cohort universally trades the basic longshot template), but specific parameters were biased to0x75cc3b’s personal pattern (e.g. their preference for 8–9¢, their willingness to sell, their HFT size).- The “8–9¢ sweet spot” finding is wrong — it fell out of an analysis that capped
price ≤ 10¢from the start. Data above 10¢ was never queried. Re-running across the full cheap tail on the 5-wallet cohort (3,206 trades, 30d, same tiw window) shows the universal sweet spot is 12–16¢ — every wallet in the cohort has 12–16¢ as its highest-win-rate band. See Cohort study (2026-05-30) — actual sweet spot is 12–16¢.Net effect today: signal cap raised
0.10 → 0.13(commita2de509), daily notional bumped$500 → $700(commit86dbbdc).
What the longshot lane does
Longshot reversion: buy the cheap (≤13¢ signal, ≤~16.9¢ max fill after the 1.3× slippage guard) outcome late in a 5-minute crypto up/down window (time_in_window ∈ [0.66, 0.93]), betting on late reversion. The book over-prices the deadness of nearly-dead outcomes; residual underlying volatility flips them more often than a low-cents ask suggests.
This is Lane 1 of the two-lane model in Lane 1 — late-window longshot reversion. Lane 2 (momentum-chase) is currently disabled, but the two share a code path — see the gotcha below.
Current live parameters (2026-05-30)
Live config — source of truth
All defaults in
crates/pm-arb/src/strategy_v2.rs; runtime overrides inmain.rs.
param value shipped DEFAULT_LONGSHOT_MIN_PRICE0.05earlier session (5¢ floor) DEFAULT_LONGSHOT_CAP_PRICE0.132026-05-30 a2de509(was 0.10)DEFAULT_LONGSHOT_MAX_TIME_IN_WINDOW0.93earlier session DEFAULT_MIN_TIME_IN_WINDOW0.66earlier session DEFAULT_MAX_SLIPPAGE_MULT1.3earlier session max_daily_notional_usd$7002026-05-30 86dbbdc(was $500)longshot_size_usd$10flat — no Kelly The cap is on the signal price. With the 1.3× slippage guard, the maximum acceptable fill is ~16.9¢.
Why 500)
The rig was hitting the 700 buys ~40% more trade-hours per day without changing per-trade risk (sizes stay flat $10). Sized to not collide with the cohort’s actual hot hours (8–14 UTC — see Hour-of-day clustering (medium confidence)).
New observability — mid-history columns (no behavior change)
Shipped in commit b677ce2: pm_paper_trades now records four extra book columns at fill time so we can backtest a trend filter later without re-deriving from pm_clob_book_snapshots:
| column | meaning |
|---|---|
up_mid_30s_ago | mid of UP outcome at fill_time − 30s |
down_mid_30s_ago | mid of DOWN outcome at fill_time − 30s |
up_mid_60s_ago | mid of UP outcome at fill_time − 60s |
down_mid_60s_ago | mid of DOWN outcome at fill_time − 60s |
Recording only. Populates as new trades land. See task #13 (trend-filter backtest) in Tasks active.
Cohort study (2026-05-30) — actual sweet spot is 12–16¢
5-wallet cohort (top 5 by 30-day ROI in [5–10¢] band, tiw [0.66, 0.93]), 3,206 trades total, 30-day window, same filter used by the original RE:
| price band | avg win% across 5 wallets |
|---|---|
| 5–8¢ | 7.5% |
| 8–10¢ | 12.2% |
| 10–12¢ | 13.5% |
| 12–16¢ | 17.5% |
Every wallet in the cohort has 12–16¢ as its highest-win-rate band. The previous “8–9¢ sweet spot” finding was an artifact of capping the analysis to ≤10¢. Acting on this is what motivated today’s cap bump 0.10 → 0.13 (commit a2de509).
Cohort ranking — the actual top tier (30d, our band+window)
| rank | wallet | n_30d | win_30d | roi_30d | 24h roi |
|---|---|---|---|---|---|
| 1 | 0xb27bc932… | 249 | 13.3% | +48.1% | +295.5% |
| 2 | 0x75cc3b… | 393 | 11.5% | +40.1% | +36.2% |
| 3 | 0xeebde7a0… | 1,250 | 9.1% | +13.4% | +145.8% |
| 4 | 0xb55fa129… | 1,395 | 9.0% | +4.4% | +126.7% |
| 5 | 0xce25e214… | 1,279 | 8.6% | −1.3% | +176.3% |
Our 24h posture (post-cap-bump, pre any further tuning): ~7.4% win / −5.3% ROI on 94 trades. Below mid-tier. The “we’re in a bad regime” excuse is dead — the cohort is crushing the same 24h window we’re underperforming in.
Cohort behavior — what 4/5 top wallets actually do
For Agents
Updated picture from the 5-wallet cohort study, replacing yesterday’s “all top traders are buy-only hold-to-resolution” framing (which conflated
0x75cc3b’s style with the cohort norm):
- 4 of 5 are BUY-only, zero SELLs over 30 days:
0xeebde7a0,0xce25e214,0xb55fa129,0xb27bc932. Pure binary-bet / hold-to-resolution. This is the template our hold-to-resolution paper model matches. - 1 of 5 sells:
0x75cc3b(28.1% of activity is SELLs). They are the cohort outlier, not the template. Their behavior — rotating positions, 4-asset diversification, deeper-late entries — is more sophisticated than the median top trader’s.
The original reverse-engineering picked 0x75cc3b as the case study because they had the densest trade history (~39k 5m-updown buys). That made them the easiest to RE, not the most representative. Our hold-to-resolution paper model matches the 4 simple top wallets, not 0x75cc3b’s pattern.
Deep-dive on the real #1 (0xb27bc932) — three candidate tunes
For Agents
Three patterns surfaced from
0xb27bc932’s 30-day history (our band+window). NONE are validated across the cohort yet — they are observations from a single wallet’s data. Each could be either a real cohort-wide pattern or0xb27bc932-specific overfit. Next step before shipping: validate each across the other 4 top wallets. If a pattern holds across 4+ wallets, ship. If only0xb27bc932, kill (overfit risk, same lesson as the killed directional filter).
(a) Asset + side bias (highest-impact if universal)
- BTC:
buy_updominates — 29–36% win, +109–213% ROI in 10–16¢ band; +$362 cumulative PnL - ETH:
buy_downdominates — 22–25% win, +118–157% ROI in 10–16¢ band; +$285 cumulative - The opposite sides contribute much less PnL
0xb27bc932skips SOL + XRP entirely- Risk: this could be specific to whatever signal
0xb27bc932uses to generate trades, not a universal market feature. Same overfit shape as the directional filter we killed.
(b) Sub-tiw goldmine inside [0.66, 0.93] (highest confidence)
| tiw | n | win% | ROI |
|---|---|---|---|
| 0.66–0.80 | 260 | ~16% | mixed |
| 0.80–0.85 | 76 | 17.1% | +42% |
| 0.85–0.90 | 57 | 45.6% | +284% |
| 0.90–0.93 | 55 | 30.9% | +225% |
The later they enter, the better — by a lot. We currently fire uniformly across [0.66, 0.93]; tightening to [0.80, 0.93] (or even [0.85, 0.93]) is the obvious tune if this holds across the cohort.
(c) Hour-of-day clustering (medium confidence — small per-hour n)
- Consistently hot hours UTC: 8, 9, 10, 11, 14 (21–59% win, +60% to +421% ROI). Matches European morning + US pre-open / morning, when crypto liquidity peaks.
- Bad hours: 3–7, 15, 17, 23 (often 0% win / −100% ROI).
- Per-hour samples are 10–51 trades, so per-hour rates have wide CIs. The broad pattern is real; the precise threshold is noisy.
Things validated as DEAD this session (2026-05-30)
Killed and stay killed — anti-patterns for a reversion strategy
Don’t re-invent these; the lesson is the bullet, not the specific filter.
Directional filter (kill — regime-flip fragile)
Filtering by historical side performance kills on regime flip. Killed earlier; restated here as the anchor lesson for the cohort-validation requirement on the 0xb27bc932 patterns above.
Per-asset rolling-ROI gate (kill — anti-correlated in reversion)
Tested 3 variants retrospectively against existing paper trades:
- Asset-only −25% rolling ROI threshold
- Asset × side −25% rolling ROI threshold
- Asset × side −50% (aggressive) rolling ROI threshold
All three variants performed worse than the unfiltered baseline. Blocked trades had +$320 cumulative profit — the gate would have skipped exactly the rebound that drives the next win.
Generalizable lesson: in a reversion strategy, recent per-category performance is anti-correlated with future performance. Filtering on recent losses removes the setup for the next win. Don’t add “skip recent losers” gates of any shape to this strategy.
CRITICAL gotcha — the 0.93 tiw cap must be LONGSHOT-ONLY
The tiw cap is a per-lane gate, never a global gate
In
evaluate_v2the 0.93 cap wraps only thetry_longshotcall.try_momentumis still called unconditionally afterward. A global tiw cap would silently kill the momentum lane, because momentum’s entire signal is the last-30s book move (tiw ≈ 0.9–1.0). Momentum is disabled today, but the gate must stay lane-specific so re-enabling it doesn’t require re-discovering this.
The exact shape at strategy_v2.rs:210-216:
if tiw <= cfg.longshot_max_time_in_window
&& let Some(signal) = try_longshot(market, cfg)
{
return Some(signal);
}
try_momentum(market, now_mono_ns, cfg)There is a regression test pinning this: momentum_not_suppressed_by_longshot_max_time_in_window (strategy_v2.rs:676). Do not “simplify” this into a top-level early-return on tiw.
Honest analytical correction — 0.93–1.0 is a SIGNAL problem, not execution
For Agents
Initial hypothesis: the 0.93–1.0 loss was an execution gap (maker-vs-taker / slippage), because
0x75cc3bprofits +117% in that window while we lose −100%. The confirm refuted it. Our 0.93–1.0 fills are clean (avg slip ratio 0.999) and the loss is 0 wins across all price bands. Slippage cannot turn a win into a loss — a winning share pays $1 regardless of entry price. So 0.93–1.0 is a signal problem: our reversion bet has no time to play out in the last ~21 seconds.
The corollary matters for what’s copyable: 0x75cc3b’s +117% in the 0.93–1.0 window is a different mechanism — likely HFT / market-making last-second behavior — that we have not reverse-engineered and may not be copyable with a taker signal at all. We’re not leaving money on the table by capping at 0.93; we’re declining a game we can’t play with our tooling.
| claim | status |
|---|---|
| 0.93–1.0 loss is an execution/slippage gap | refuted — fills are clean (slip ratio 0.999) |
| Our 0.93–1.0 loss is “0 wins everywhere” | confirmed — signal has no time to revert |
0x75cc3b’s +117% there is the same edge we run | no — different (likely HFT/MM) mechanism, not taker-copyable |
Structural ceiling — what’s achievable, what isn’t
Where the rig can plausibly sit, and where it can't
tier example wallet profile achievable for us? Top 0xb27bc932consistent +48% / 30d; 24h spikes to +295%Likely HFT-grade execution + microstructure reads (order-book depth, flow imbalance, sub-second timing). No — we don’t ingest any of that signal. Mid 0xb55fa129style: +4–20% ROI, 9–15% winBasic late-window cheap-tail buy-only, hold-to-resolution. Yes — this is what’s achievable with focused tuning of the existing rig. Deep-late 0.93-1.0mechanism(parked — see Open thread below) Likely maker/live execution. Separate project, separate infra.
Open thread / future work — the parked 0.93-1.0 deep-late mechanism
The top trader's 0.93–1.0 edge is an un-reverse-engineered mechanism — a real-but-separate research lead, deliberately parked
Wallet
0x75cc3bmakes +117% ROI (13.2% win over 151 trades) in the deep-late 0.93–1.0 window — the last ~7% (~21 seconds) of the 5m window, and their single best sub-window. But our longshot loses there (0/41), and we confirmed it is not an execution problem (clean fills, avg slip ratio 0.999). Our “buy the cheap side + bet on reversion” thesis simply has no time to play out in the last ~21s. So0x75cc3bis running a different mechanism in that window that we did not reverse-engineer — most likely HFT / market-making / last-second-move capture, not a reversion bet.This is a genuine lead, captured here only so it isn’t lost. It is not worth chasing now, for three reasons:
- Capturing it would likely require maker/limit execution — a live order-placement system, not our current paper-taker model.
- It may not be copyable with a taker signal at all — last-second MM capture is a different game from taking the offer.
- Defer until the current paper-validation window completes. Don’t fork attention onto a new mechanism while the tuned longshot lane is still proving out.
Tuning history
Earlier session: 5¢ floor, 1.3× slippage guard, 0.93 tiw cap
All three are in crates/pm-arb/src/strategy_v2.rs (config constants) with the slippage guard enforced in crates/pm-arb/src/market_state.rs. CLI overrides exist in main.rs.
Floor the entry price at 5¢. The 4–5¢ signal band is −22.7% ROI over 97 trades — and 0x75cc3b’s own 4¢ band is −91%, so this isn’t us; it’s a genuinely dead sub-band. Floored it out.
DEFAULT_LONGSHOT_MIN_PRICE:0.04 → 0.05(strategy_v2.rs:11)- Enforced in
try_longshot:if ask < cfg.longshot_min_price { return None; }(strategy_v2.rs:228)
Slippage guard at 1.3×. Fills that slip >1.3× the signal price are net-negative; the clean edge (and the top trader’s) lives entirely in fills ≤1.3×. Drop the fill when it slips past the multiplier.
DEFAULT_MAX_SLIPPAGE_MULT:2.0 → 1.3(strategy_v2.rs:20)- Enforced in
market_state.rs::try_resolve_pending(market_state.rs:342):
if entry_price > pending.price_at_signal * cfg.max_slippage_mult {
state.pending_fill = None;
return FillOutcome::SlippageDrop { refund_cents: reserved_cents };
}(entry_price is the ask at the resolved fill time; price_at_signal is the ask when the signal fired. The capital is refunded, not lost — the trade simply never happens.)
Cap time-in-window at 0.93. Our longshot was 0/41 (−$410) in the 0.93–1.0 window across all price bands (see the analytical correction above for why). Capped it.
DEFAULT_LONGSHOT_MAX_TIME_IN_WINDOW = 0.93(strategy_v2.rs:9)- Gate in
evaluate_v2(see the gotcha above for why it wraps only the longshot call).
2026-05-30: cap bump 0.10 → 0.13, daily notional bump 700, mid-history instrumentation
- Cap bump:
DEFAULT_LONGSHOT_CAP_PRICE0.10 → 0.13(commita2de509). Justified by the 3,206-trade cohort study above: cohort sweet spot is 12–16¢, not 8–9¢. With the 1.3× slippage guard, max accepted fill is now ~16.9¢. - Daily notional bump:
max_daily_notional_usd$500 → $700(commit86dbbdc). The rig was idling after ~02:00 UTC each day. - Mid-history instrumentation: new columns
up_mid_30s_ago,down_mid_30s_ago,up_mid_60s_ago,down_mid_60s_agoonpm_paper_trades(commitb677ce2). Recording-only — no behavior change. Backfills as new trades land; enables the trend-filter backtest in task#13.
Today’s operational learnings (gotchas worth durable docs)
sqlx migration tracking can drift silently — make migrations idempotent
The
pm_self_trade_alertsfeature was broken in prod for hours because of a chain of migration issues:
- A prior migration (
20260527203948) was missing its tracking row in_sqlx_migrations.- sqlx therefore tried to re-run the prior migration on boot, which failed because the table already existed.
- As a result, the new
20260530000000_pm_self_trade_alerts.sqlmigration was never even attempted, and thepm_self_trade_alertstable was never created.- The feature silently failed at runtime — every code path tried to INSERT into a non-existent table.
Lessons:
- All sqlx migrations must be idempotent:
CREATE TABLE IF NOT EXISTS,ADD COLUMN IF NOT EXISTS,DROP POLICY IF EXISTS <name> ON <table>; CREATE POLICY <name> …. Yesterday’s20260530000000_pm_self_trade_alerts.sqlwasn’t, which compounded the problem.- The boot code in
main.rs:2376-2380swallows the sqlxMigrateError, so failures degrade silently instead of crashing loudly. Task#15: fix this — log at ERROR level + non-zero exit so systemd restarts.
Recovery pattern — Supabase MCP for direct prod surgery
Fixed the broken migration state today without a redeploy by going directly to prod via Supabase MCP:
CREATE TABLE IF NOT EXISTS pm_self_trade_alerts(…) — created the missing table directly.- Computed SHA-384 checksums for the missing migration row(s) and inserted them into
_sqlx_migrationsso sqlx considered the chain “applied” on next boot.This is the standard unblock when sqlx migration tracking drifts in prod. Use sparingly (it bypasses the migration pipeline by definition), but it’s the only way to fix the chain without a deploy.
Auto-catch (per @deploy Round 36) only applies to pm-arb
Pushes to
pm-arbare auto-caught and redeployed. polymarket-fetch services need explicit redeploys. Don’t expect a push-to-polymarket-fetchto ship to the live VM by itself.
Tasks active (related to this strategy)
#3— Forward-validate longshot tunes (in progress, data accumulating).#9— Bound scheduled-run’spm_trades WHERE proxy_wallet=$1query (tech debt; pulls 24k–112k rows/wallet/cycle).#13— Trend-filter backtest (in progress; waiting on 2–3 days of mid-history data from the new columns).#15— Stop swallowing sqlxMigrateErrorinmain.rs:2376-2380.#16—0xb27bc932deep-dive findings need cohort validation before any are shipped.
Validation discipline pattern (stays valid)
The kill list above (directional filter, per-asset rolling-ROI gate) shares a shape: a filter built from a single wallet’s history, or from in-strategy recent performance, is overfit-prone in a reversion strategy. Cohort-wide validation across 4+ top wallets is the bar before shipping any per-asset / per-side / per-hour / per-tiw refinement. The three candidate tunes from 0xb27bc932 (asset+side bias, sub-tiw goldmine, hour-of-day) are all currently sitting under this bar.
Related
- top-trader-edge — the analysis this implements; the two-lane thesis, price×phase heatmap, and cross-wallet validation
- short-term-accuracy — the earlier (superseded) latency-arb framing of the same wallets
- lag-probe — measured book-vs-Binance lag; informs execution infra, not the longshot signal
- polymarket-fetch — overview,
pm_tradesschema, CLI, deploy state