diff --git a/research/maker_sharps.py b/research/maker_sharps.py new file mode 100644 index 00000000..38781b6d --- /dev/null +++ b/research/maker_sharps.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""T2 EXPLORATORY (2026-07-23) — maker-sharp selection: mine the maker side +of every match (aux orders_matched, ~6M wallet-attributed rows the screens +have never touched). Do improbably-winning MAKERS exist — the species +farming the crater wall — and are they a distinct, followable cohort? + +Method mirrors the taker screen (study_flow.informed_set): per wallet-asset +net maker position (maker BUY = resting bid filled), entry vwap, resolved +via tape proxy (chain-validated 742/742 method); wallet improbability +z = (wins − Σp)/sqrt(Σp(1−p)) on n≥6 resolved bets with net≥5sh, vwap in +[0.05,0.95], pnl>0; z≥2.5 qualifies. Readouts: cohort size, top wallets, +overlap vs the taker informed set + watch_sharps (distinct species?), +pooled per-bet EV of qualifying makers' resolved bets. NOT pre-registered; +a forward table row + copyability (lag) study only if a cohort exists.""" +import json +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import tape # noqa: E402 + +HERE = os.path.dirname(os.path.abspath(__file__)) +SET_MIN_Z, SET_MIN_BETS = 2.5, 6 + + +def main(): + db = tape.connect() + tape.build_resolved(db) + rows = db.execute(""" + WITH mk AS ( + SELECT lower(json_extract_string(payload,'$.proxyWallet')) wallet, + json_extract_string(payload,'$.asset') asset, + json_extract_string(payload,'$.side') side, + cast(json_extract(payload,'$.price') AS DOUBLE) price, + cast(json_extract(payload,'$.size') AS DOUBLE) size, + any_value(json_extract_string(payload,'$.name')) OVER + (PARTITION BY lower(json_extract_string(payload,'$.proxyWallet'))) nm + FROM aux WHERE type = 'orders_matched' + ), bets AS ( + SELECT wallet, any_value(nm) nm, mk.asset, + any_value(tk.payout) payout, + sum(CASE WHEN side='BUY' THEN size ELSE -size END) net, + sum(CASE WHEN side='BUY' THEN size*price END) + / nullif(sum(CASE WHEN side='BUY' THEN size END),0) vwap + FROM mk JOIN res_tok tk ON mk.asset = tk.asset + GROUP BY wallet, mk.asset + HAVING net >= 5 AND vwap BETWEEN 0.05 AND 0.95 + ) + SELECT wallet, any_value(nm), count(*) n, + sum(CASE WHEN payout=1.0 THEN 1 ELSE 0 END) wins, + sum(vwap) exp_w, sum(vwap*(1-vwap)) var_s, + sum(net*(payout - vwap)) pnl, + avg(vwap) avg_entry, sum(net*vwap) staked + FROM bets GROUP BY wallet + HAVING n >= ? AND var_s > 0 + """, [SET_MIN_BETS]).fetchall() + print(f"maker wallets with >= {SET_MIN_BETS} resolved conviction-ish " + f"positions: {len(rows)}") + scored = [] + for w, nm, n, wins, exp_w, var_s, pnl, avg_e, staked in rows: + z = (wins - exp_w) / (var_s ** 0.5) + if z >= SET_MIN_Z and pnl > 0: + scored.append((z, w, nm, n, wins, pnl, avg_e, staked)) + scored.sort(reverse=True) + print(f"qualifying maker-sharps (z>={SET_MIN_Z}, pnl>0): {len(scored)}") + # overlap with the taker screens + taker = set() + try: + d = json.load(open(os.path.join(HERE, "params", "informed_set.json"))) + taker = {x.lower() for x in d["wallets"]} + except Exception: + pass + watch = set() + try: + for r in json.load(open(os.path.join( + os.path.dirname(HERE), "live", "watch_sharps.json"))): + watch.add(r["wallet"].lower()) + except Exception: + pass + ol_t = sum(1 for z, w, *_ in scored if w in taker) + ol_w = sum(1 for z, w, *_ in scored if w in watch) + print(f"overlap: {ol_t} in taker informed set (n={len(taker)}) · " + f"{ol_w} in watch_sharps (n={len(watch)})") + pooled_pnl = sum(s[5] for s in scored) + pooled_n = sum(s[3] for s in scored) + print(f"cohort pooled: {pooled_n} resolved bets · " + f"${pooled_pnl:+,.0f} maker pnl\n") + print(f"{'z':>5} {'name':<20} {'bets':>5} {'wins':>5} {'hit':>5} " + f"{'pnl':>10} {'avg entry':>9}") + for z, w, nm, n, wins, pnl, avg_e, staked in scored[:15]: + print(f"{z:5.1f} {(nm or w[:12]):<20} {n:>5} {int(wins):>5} " + f"{wins/n:>5.2f} {pnl:>+10,.0f} {avg_e:>9.2f}") + + +if __name__ == "__main__": + main() diff --git a/research/sibling_sum_scan.py b/research/sibling_sum_scan.py new file mode 100644 index 00000000..7d06f49a --- /dev/null +++ b/research/sibling_sum_scan.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""T4 EXPLORATORY (2026-07-23) — sibling-sum consistency: how often do a +binary market's two tokens price to YES+NO != $1 beyond fees, and does it +persist long enough to execute? Model-free structural arb: + sum < 1 − fees → buy both, merge to $1 (venue split/merge is native) + sum > 1 + fees → split $1, sell both +v0 substrate is PRINTS (co-active minutes: both legs printed in the same +UTC minute — staleness-safe, undercounts violations that sat in books +without printing). Fees modeled taker-side both legs (worst case; merge/ +split itself is free). Persistence = consecutive violating minutes. +Phase 2 (only if rich): a live book scanner. NOT pre-registered.""" +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import tape # noqa: E402 + +FEE = 0.03 +MIN_PROFIT = 0.005 # 0.5c/share after fees + + +def main(): + db = tape.connect() + print("pairing binary conds (exactly 2 assets on tape)…") + db.execute(""" + CREATE TEMP TABLE pairs AS + SELECT cond, min(asset) a1, max(asset) a2 + FROM (SELECT DISTINCT cond, asset FROM trades + WHERE cond IS NOT NULL AND cond != '') + GROUP BY cond HAVING count(DISTINCT asset) = 2""") + n_pairs, = db.execute("SELECT count(*) FROM pairs").fetchone() + print(f"binary markets: {n_pairs:,}") + rows = db.execute(f""" + WITH m AS ( + SELECT t.cond, cast(floor(t.ts/60) AS BIGINT) mnt, t.asset, + arg_max(t.price, t.ts) px, + any_value(t.title) title + FROM trades t JOIN pairs p ON t.cond = p.cond + GROUP BY 1, 2, 3 + ), co AS ( + SELECT cond, mnt, any_value(title) title, + min(px) pa, max(px) pb, sum(px) s, count(*) legs + FROM m GROUP BY cond, mnt HAVING count(*) = 2 + ) + SELECT cond, mnt, title, pa, pb, s, + (1 - s) - {FEE}*(least(pa,1-pa) + least(pb,1-pb)) buy_profit, + (s - 1) - {FEE}*(least(pa,1-pa) + least(pb,1-pb)) sell_profit + FROM co""").fetchall() + n_co = len(rows) + buys = [r for r in rows if r[6] > MIN_PROFIT] + sells = [r for r in rows if r[7] > MIN_PROFIT] + print(f"co-active market-minutes: {n_co:,}") + print(f"buy-both arbs (sum<1−fees): {len(buys):,} " + f"({100*len(buys)/max(n_co,1):.2f}%)") + print(f"split-sell arbs (sum>1+fees): {len(sells):,} " + f"({100*len(sells)/max(n_co,1):.2f}%)") + for tag, vs, idx in (("BUY-BOTH", buys, 6), ("SPLIT-SELL", sells, 7)): + if not vs: + continue + prof = sorted(r[idx] for r in vs) + print(f"\n{tag}: profit/share p50 {prof[len(prof)//2]*100:.1f}c · " + f"p90 {prof[int(len(prof)*.9)]*100:.1f}c · " + f"max {prof[-1]*100:.1f}c") + # persistence: consecutive violating minutes per cond + by_cond = {} + for r in vs: + by_cond.setdefault(r[0], []).append(r[1]) + runs = [] + for mins in by_cond.values(): + mins.sort() + run = 1 + for i in range(1, len(mins)): + if mins[i] == mins[i-1] + 1: + run += 1 + else: + runs.append(run) + run = 1 + runs.append(run) + runs.sort() + print(f" persistence: {len(by_cond)} markets · runs p50 " + f"{runs[len(runs)//2]}min · p90 {runs[int(len(runs)*.9)]}min " + f"· max {runs[-1]}min") + top = sorted(vs, key=lambda r: -r[idx])[:5] + for r in top: + print(f" {r[idx]*100:5.1f}c/sh · sum {r[5]:.3f} · {r[2][:56]}") + + +if __name__ == "__main__": + main()