Crypto 5-min Up/Down — Oracle-Lag Strategy (Canonical Spec & Multi-Asset Replication)
Canonical, asset-agnostic spec for the Polymarket 5-minute Up/Down oracle-lag strategy: the validated edge, the exact signal/sizing, the producer-only architecture and money-path invariants, and the per-asset replication + rollout protocol. Supersedes scattered phase notes as the single source of truth for what the strategy is and how to clone it to a new asset.
For Agents
Status: UP lane LIVE (real money), DOWN lane dry-validating (no money) as of 2026-06-28. Repo
wowjeeez/pmv2-crypto-algoat/Users/levander/coding/pmv2/crypto_algo. Isolated Supabase schemacrypto_shortterm(NEVER touch pmv2public/pm_*). Runs onpolymarket-infraVM in Zurich (Frankfurt was geolocked by Polymarket).
Thesis (the edge)
Polymarket’s 5-min Up/Down binary markets settle on a slow on-chain oracle (Chainlink BTC/USD on Polygon). A faster reference feed (Binance spot) lets us predict the next Chainlink print before Polymarket’s book reprices. We trade that oracle lag: when the fast feed implies the outcome with high conviction and there’s still time before resolution, buy the corresponding token at its book ask before the market catches up.
See crypto-shortterm-strategy-design for the full P_fair model and crypto-shortterm-data-sources for the three feeds.
The edge — validated
- UP: 86.6% backtest win-rate, 87.1% live (method sound — live matches backtest).
- DOWN: 88.4% win-rate over 1,930 windows (backtest,
scratchpad/down_backtest.py). - Edge is symmetric (Up and Down both work).
Signal spec (EXACT)
- Calibrated probability
p_cal= P(Up outcome), from a frozen 20-bincalibration.json(include_str!in the binary — do NOT refit for the live asset). - Entry regime:
τ ∈ [60,150)s(time-to-resolution) AND|p_cal − 0.5| ≥ 0.30(conviction).- Up:
p_cal ≥ 0.80→ buy the Up token at the Up-token book ask. - Down:
p_cal ≤ 0.20→ buy the Down token at the Down-token book ask — NEVER the synthetic complement, never1 − up_bid.
- Up:
- One fire per window (single-fire guardrail;
emitted_signalsPK =window_id).
Sizing & PnL
- Fixed shares (currently 10) via
EMIT_SIZE_MULTIPLIER(global, default 1.0 = 5 shares; deploy set =2 → 10 shares ~200 Up). Sizing mechanics: crypto-shortterm-phase1-bet-sizing. - Binary token redeems 0 on loss. So: profit = shares×(1−price), loss = −shares×price. Small wins, big losses → the whole model depends on the high win-rate.
- Breakeven win-rate = the fill price. EV per share =
win_rate − price. At 88.4% we’re EV+ for any fill below ~0.884.
Architecture (Pattern B — producer-only, two binaries one image)
- observe (
shortterm-crypto-algo-observe): records Binance trades + on-chain Chainlink BTC/USD (Polygon dRPC) + Polymarket L2 books for both Up and Down tokens → fair value →crypto_shortterm.observations. - emit (
shortterm-crypto-algo-emit): on a validated regime fires a pmv2-contracts schema_2 EntryOrder to NATSpmv2.order.{source}.entry(JetStream + PubAck), records tocrypto_shortterm.emitted_signals. pmv2-contracts pinned at rev1f05fff(do not bump). GAR imageapps/pmv2-crypto-algo:latest. - @positionmanager consumes stream
PMV2_ORDERS(subjectspmv2.order.<source>.entry) and executes on-chain. See position_manager. - source = NATS subject segment, derived 100% from
order.source. One source per lane:crypto_5m_algo_up,crypto_5m_algo_down.
Money-path invariants (do NOT regress)
Money-path invariants — do NOT regress
- C1 persist-before-publish: emit runs the
emitted_signalsINSERT (ON CONFLICT (window_id) DO NOTHING, check rows_affected) BEFOREpublish_entry. DB is the dedup authority → no duplicate real-money trade; worst case a detectable missed trade. Lives in unit-testedfire_decision().- C2 fail-fast: emit + observe ABORT startup if the migration columns are absent → the migration MUST be applied BEFORE redeploy, else the binary crash-loops and halts the live lane.
- BookView: Observation carries
up/downBookView; testedfire_params(side,…)guarantees each side reads its own book/token (test fails on cross-wire).
Implementation + rationale: crypto-shortterm-down-side-money-path-fixes-2026-06-28 (C1 supersedes the old publish-then-mark ordering in crypto-shortterm-emit-guardrails).
The fill = the ONLY per-market unknown
The signal/EV math is settled and asset-agnostic. What is NOT known for any NEW token/market is the real order-book fill (ask, spread, depth) — there’s zero book history until you record it. Validate it per market: synthetic estimate was Down ask ≈ 1 − up_bid ≈ 0.82; the dry-validate measures the real book.
Per-market gate
Going live on a new lane requires: signal backtested above the fill price, AND the real book dry-validated as tradeable (fill ≈ synthetic-or-tighter, tight spread, ample depth), AND Andras’s explicit
source→liveflip. Never autonomous.
Edge gate — fire on edge, not conviction (the fix — shipped, default-off)
Backtested threshold (2026-06-29):
EMIT_MAX_ASK = 0.85. Over the full 21-day history the −EV region isask ≥ 0.90(buckets: [.80,.85)+5.0¢, [.85,.90)+2.9¢, [.90,.95) −2.7¢, [.95,1.0) −3.7¢/share). 0.85 is +EV in all 6 walk-forward folds with safe margin below the ~0.89 break-even; 0.88 maximizes total dollars; 0.90 is REFUTED (readmits the −EV tail — the one-regime live data had hinted 0.90, the full backtest overturned it). The τ-cap is REFUTED: tightening τ raises win-rate but ask rises faster → EV falls; keep the regime at[60,150)s. Momentum / conviction / rollover-range all dead across the full history. Shipped:EMIT_MAX_ASKenv cap inemit.rs(commita27b852), default-off — activation (set 0.85 + redeploy) is a live-Up-lane change pending @deploy/Andras. Caveat: all these EVs use ask-at-emit = UPPER BOUND; real fills lose to latency (still unmeasured) + the 0.02 buffer + slippage — a small live tranche is the honest final check, and generalizes to every asset lane: cap the −EV ask tail, fire on edge not conviction.
Rollout protocol (new lane / market)
- Apply the DB migration FIRST (adds the columns C2 checks).
- Add the NATS permit for the new
<source>.>subject (@deploy owns NKey permits). - Create the Polymarket dry source row (@positionmanager).
- Redeploy emit/observe at the pinned commit, keep env.
- Dry-validate a few hours: emit fires DryRehearse (builds+signs the order, does NOT POST — no money).
- Assess the real book; if tradeable, propose small-live → Andras arms.
Multi-asset replication template (BTC → ETH/SOL/XRP/…)
The strategy generalizes to any asset that has (a) a Polymarket 5-min Up/Down market, (b) a slow on-chain Chainlink settlement oracle, and (c) a fast Binance reference feed.
Reuse as-is (chain-agnostic)
- Whole architecture (observe/emit two-binary image; NATS producer → @positionmanager).
- Money-path invariants C1 / C2 / BookView.
- Regime structure:
τ∈[60,150)s+|p_cal−0.5|≥0.30+ buy the conviction side at its own token-ask. - Single-fire-per-window guardrail; fixed-share sizing model.
- Rollout + dry-validate protocol; all gotchas below.
Re-derive per asset (do NOT copy BTC’s)
- Binance symbol (e.g.
ETHUSDT,SOLUSDT) — the fast feed. - The Chainlink {asset}/USD feed address on Polygon — must be the exact oracle Polymarket uses to RESOLVE that market (verify against the market’s resolution source; wrong feed = wrong edge).
calibration.json(20-bin) — REFIT per asset. BTC’s calibration encodes BTC’s vol/lag and will be miscalibrated for another asset. Collect that asset’s own observations (observe-only), fit, then freeze.- Regime thresholds (
τbounds, conviction cut): start with BTC’s as a prior, but re-backtest per asset; higher-vol assets may need different cuts. - New sources
{asset}_5m_algo_up/_down; new PM permits, dry rows, daily caps.
Validate per asset before any live money
- Backtest the regime on that asset’s observations — target win-rate comfortably above the expected fill price (BTC hit 86–88%).
- Dry-validate the real token-book fill a few hours.
- Andras’s explicit arm.
Per-chain subagent task shape (what to start now)
- Stand up an observe-only recorder for the asset (Binance symbol + verified Chainlink feed) → accrue
observations. No emit, no money. - After enough data: fit
calibration.json, backtest the regime, confirm the edge. - If it holds: build the lane (sources/permits/dry rows), dry-validate the fill.
- Assess → propose small-live → Andras arms.
(Step 1 — observe-only data collection — can begin immediately and in parallel across assets; trading is gated on 2–4.)
Gotchas (already bit us)
- Polymarket token IDs are u256 (77-digit) — never
parse::<u128>. (See crypto-shortterm-polymarket-token-overflow-fix.) - Gamma: tick =
orderPriceMinTickSize, min size =orderMinSize;conditionIdis a real0x…. EMIT_MAX_FIRES(=1000) hydrates fromemitted_signalsat boot; TRUNCATE alone doesn’t reset it (restart needed) — do NOT truncate, keep the rows. (See crypto-emit-max-fires-cap-clear-2026-06-25.)- PM mode =
min(global, source), ignoresenabled— live needs BOTH global+source=live. - Geolock: must run from an allowed region (Zurich works; Frankfurt was blocked).
- Fixed-share, not fixed-dollar: every win grosses shares×$1; loss = −shares×price.
Current live state (2026-06-28)
- main
90808d0. UP lane live (10 shares, $200 cap). DOWN lane: VALIDATED 2026-06-29 (HOLD reversed — the 2026-06-28 “fails-as-built” call was a small-sample artifact). A 21-day / 4,049-fire / ~19-regime walk-forward backtest (reconstructed from recordedp_fair+ the frozen calibration, 99.5% match to live fires) confirms a durable edge: win-rate ~87% (Up 86.7% / Down 88.5%, stable 0.84–0.93 day-to-day). Live’s 90–92% was a 2–4pt regime tailwind on the win-rate. The ask/EV cap is the value driver and roughly doubles per-bet EV (uncapped Up +5.3¢/share → capped +9.8¢). Down real-money arm still = Andras’s switch.
Related
- crypto-shortterm — project overview / entry point.
- crypto-shortterm-handover-2026-06-26 — running-state snapshot (mirrors the repo
HANDOVER.md). - crypto-shortterm-crypto-5m-algo-both-sides-2026-06-27 — both-sides decision + the
crypto_5m_algo_up/_downsource split plan. - crypto-shortterm-down-side-money-path-fixes-2026-06-28 — C1 / C2 / BookView implementation (
fire_decision,probe_schema_column,fire_params). - crypto-shortterm-emit-guardrails — money-safety guardrails (the publish-then-mark ordering C1 supersedes).
- crypto-shortterm-phase1-bet-sizing —
EMIT_SIZE_MULTIPLIERsizing mechanics; cap is a reject-GATE not a resizer. - crypto-shortterm-phase1-execution-decision — producer→executor seam, latency budget, staged live test.
- crypto-shortterm-phase1-latency-budget — 128ms publish→fill budget the live arm measures against.
- crypto-shortterm-data-sources — Chainlink settlement / Binance predictor / Polymarket book feeds + APIs.
- crypto-shortterm-strategy-design — Rev 2 oracle-lag
P_fairmodel. - crypto-shortterm-backtesting — causally-typed validation funnel.
- crypto-shortterm-polymarket-token-overflow-fix — u256 token-id overflow gotcha.
- crypto-emit-max-fires-cap-clear-2026-06-25 —
EMIT_MAX_FIRESboot-hydration gotcha. - position_manager — the @positionmanager executor (pmv2 fleet).