# research/ — the edge factory (SILO) Standing rules (user directive 2026-07-20: "built in a silo to not affect anything on the live bot"): - **Nothing here is imported by, or imports, the bot** (`copybot.py`, `copytrade.py`, their configs). The Fly workers run pinned entrypoints; this directory is inert to them. - **Tape access is read-only** (`duckdb.connect(..., read_only=True)`). - The ONE shared write: `live/cache.duckdb::resolutions` via `live/payouts.py` — append-only immutable chain facts, the same store the daily pipeline already feeds. Nothing else in `live/` is touched. - The existing paper bot is the live test's CONTROL — graduated edges get their own paper harness here, never that one. - Studies are pre-registered (GitHub issue per study: hypothesis, params, verdict + kill criteria) BEFORE their forward window opens. Exploration happens on already-collected tape; **verdicts only come from `forward_ledger.jsonl` rows dated after the params freeze commit.** - **SCORER LAW (2026-07-22, FINDINGS "round three"): every scorer resolves outcomes through `forward.payouts_for()` — tape proxy first, CTF chain overlay for the remainder. A "pending" bucket is NEVER ignorable on Polymarket: market liveness encodes the outcome (a losing bet's winning sibling keeps trading, so losses hide in pending while wins score). This bug inflated Study A from a true −$6/fill to +$46/fill.** - **Every study gets one instrument that does not share the scorer's assumptions** — the surge paper harness (chain-graded from day one) is what caught the bug. Divergence between instruments is signal, not noise. - **DATA LAW (2026-07-22): tests are additive, never subtractive.** Raw streams (tape, attempts, markouts, settles, meta snapshots) are append-only; analysis opens them read-only; no script rewrites or rotates a raw stream. Bot-state trims exist ONLY where an append-only log on the volume carries the same rows durably (settles) or the nightly pull archives them (attempts/markouts). A study that would need to mutate a stream records a NEW stream instead. Layout: tape.py read-only loaders · tape proxy-resolution (terminal-VWAP + sibling veto — exact per-bet, but resolution TIMING is win-biased; see SCORER LAW) · title parsers · chain_overlay() sim.py execution replayer calibrated on OUR live fills ledger (lag, FAK no-match, protected band, 3% taker fee) study_flow.py Study A — surge momentum (#16 CLOSED 2026-07-23; A2 chain grade −$7.54/fill × 1,344 independently confirms) study_oracle.py Study B — crypto oracle fair value (#17: E0.04 killed; E≥0.07/0.1 ledger-positive but the harness chain grade 2026-07-23 reads −$8.51/−$5.90 per fill at real latency — taker arm evidence-dead, tiers accrue to their formal bars, maker pivot is the successor hypothesis) copy_edge_slices.py T5 — parity-era copy edge by niche/lag/band/wallet (esports carries it; feeds #14) copy_maker_entry.py T3 — resting-bid copy entries beat taker FAKs (+$17.45 vs +$12.86/signal; basis of #20) sell_mirror_study.py sharp exits are bankroll ops, not signal (basis of #21 hold-through) maker_sharps.py T2 — 673 improbably-winning MAKERS in orders_matched (86% invisible to taker screens); follow-on: inventory-lean signal sibling_sum_scan.py T4 — print-substrate sum-arb scan (artifact- dominated; needs standing-book data; parked) maker_lean.py T6 — WALK-FORWARD POSITIVE: follow small maker inventory leans (+$2.51/lean, 59% hit, positive all 3 days) → STUDY C #22 PRE-REGISTERED (params/study_lean.json frozen 052eda0; follow arm verdict-gated, fade $2k+ FAILED its event-concentration gate — report-only; nightly rows via forward.py score_lean, study:"lean") event_leadlag.py T9 — POSITIVE: same-outcome siblings reprice slowly after leader bursts (+$9.73/$100 chain, n=2,028; stale-print entry optimism stated) → STUDY D #23: wwf-lagbot harness (below) is the Stage-2 execution-realism instrument settle_discount.py T7 — settlement-discount niche: industrialized by incumbents (+2-3%/hold for the good ones); residual unproven (63% of vol outside tape-resolution); parked at our scale miss_audit.py T8 — guard counterfactual: guards earn their keep; crater-rejects were GOOD misses (retry EV follow-up queued) copy_age_slices.py T10 — market-age gradient inconclusive at n<=15/cell (6-24h positive both books; age~market-type confound); reruns as fills accrue maker_quote_sim.py T1 — crypto maker quoting at fair−m: STAGE-1 KILL 2026-07-23 (best cell +$0.37/fill at m=4c/R=1s under queue-front optimism; every R=4s cell −$7-9/fill — staleness IS the adverse selection; ~31-50k graded fills/cell) copy_maker_patience.py T11 — #20's 60s maker TTL is ALREADY OPTIMAL: touches land in 1-2s, fill rate flat after 5m, and the marginal late fills lower EV (loser fill-rate 100% at 5m+ — patience past a minute only harvests adverse selection) maker_unwind.py T12 — maker-sharp unwinds are NOT exit signals: STAY +$5.95/unwind (n=265, 2.6x the bar) vs FADE -$5.22 — #21 one species over; Study C keeps NO exit rule (concentration caveat in the header; --grade-only re-grades the dump) depth_imbalance.py T14 — book imbalance at fill is a BAND: extremes adverse both ways, balanced 0.25-0.47 least-bad in BOTH harnesses (portable #23 guard candidate); slope "gate" = quantile-tie artifact — check feature variance first sharp_halflife.py T15 — published-set churn is violent: 49% of detected sharps gone in 1 day, ~10% at 28d (real as-of git history of watch_sharps.json); EV-by-age = churn's meaning — pending its 81k-cond chain-ensure requote.py crater→requote timing (feeds the bots' per-niche retry) forward.py scores frozen studies on new tape days → forward_ledger (payouts_for chain overlay mandatory; controls + sub5c exploratory arm — 0-for-38, dead) informed_set.py publishes params/informed_set.json nightly (surgebot input) surgebot.py A2 MEASUREMENT harness (wwf-surgebot → jaxperro.com/test): every cooldown-passed trigger paper-FAK'd at $100 against the live asks; v1's cash-gated $100/5% book (halted at its −50% line 2026-07-22) is replayed offline instead — surge_book_replay.py → surge_book.json. v1 artifacts frozen: surge_paper_ledger.jsonl + /data/surge_state.json. oraclebot.py Study B real-time PAPER harness (wwf-oraclebot → jaxperro.com/test): fair value tick-by-tick on the venue's own settlement feed, all E-tiers tracked, three settle layers (own-feed tick → CLOB flags → nightly chain truth) lagbot.py Study D real-time PAPER harness (wwf-lagbot → jaxperro.com/test, deployed 2026-07-23, SEM_VER l1): leader bursts ≥10¢/120s → paper-FAK $100 on ≤2 lagging same-outcome siblings at ≤ stale+4¢; DOWN-moves buy the sibling's COMPLEMENT token; cooldown 600s/event, 30-min warmup (leader maps built live from the stream). Every attempt logs the ask premium over the stale print — the observational kill-switch (median ≥+8¢ over 3 days = mirage) can kill #23 without a paper sample grade_surge.py / grade_oracle.py / grade_lag.py nightly chain-truth re-grades → surge_meas / oracle_paper / lag_paper ledgers (grade_lag also prints EV per episode AND %-of-staked — partial fills can't flatter the verdict — plus the median-premium kill-switch read); all three pull the raw volume streams to local .pull copies markout_flow.py exploratory markout-exit curve (chain-truth verdict: NO scalp inside the dead surge signal — losers bleed from minute one; v0's res_tok version was round-3-biased) meta_snap.py nightly gzipped snapshot of ALL active markets (~12k/day, research/meta/, local-only) — τ knowable at trigger for every tape trigger; token→outcome maps close the label-gap artifact class params/ frozen study parameters (committed = frozen) nightly.sh launchd runner (fires 09:15, then WAITS for fresh tape — Stage 0 keeps the tape ~15-min fresh whenever the Mac is awake; deadline 8h, then scores anyway and logs staleness) Data streams (the moat — all append-only, per DATA LAW): Fly volumes (accrue 24/7, Mac-independent; daily Fly snapshots ×5): wwf-recorder /data full firehose → Parquet folds (25GB, ~3+ weeks of Mac-offline headroom before the 85% disk guard) wwf-surgebot /data surge_attempts.jsonl (every attempt + top-5 asks + top-3 bids + latency) · surge_markouts.jsonl (book re-reads +60/300/1800s per fill) · surge_settles .jsonl (durable settles) · surge2_state.json wwf-oraclebot /data oracle_attempts / oracle_markouts / oracle_settles .jsonl + oracle_state.json (same shapes) wwf-lagbot /data lag_attempts.jsonl (every attempt + top-5 asks + premium vs stale + latency) · lag_settles.jsonl (durable settles) · lag_state.json Mac (nightly pulls + git): ledgers + params committed; raw .pull copies and meta/ gzips local. forward.py backfills any tape-covered day the ledger has never seen — a Mac gap > RESCORE_DAYS leaves no holes.