Files
winning-wallet-finder/live/tape_sharps.py
T
jaxperro 008b3138cc tape research: first sharp screen over the RTDS firehose (#2)
live/tape_sharps.py — insider.py's improbability z transplanted onto the
3-day tape (13.8M fills, ALL wallets, no survivorship bias): proxy-resolve
tokens from terminal-VWAP convergence (0.97/0.03, 2h quiet, sibling veto),
net held positions per wallet-token, z = (W - Σp)/sqrt(Σp(1-p)) over
proxy-resolved held bets, then payouts.py chain-truth overlay on the
shortlist (refunds count as neither). Copyable/algo split: discrete-entry
conviction bettors (<= 6 fills/bet, <= 90 bets, med >= $50) vs continuous
flow accounts that score huge but can't be mirrored at 3-17s lag.

First run (tape through 2026-07-20 16:59): 2,360 wallets screened, 561
score z >= 2 with pnl > 0, 25 copyable candidates at z 4.0-5.5 — chain
validation 742 payout vectors, ZERO proxy flips. Benchmark sanity: benched
sharps (LSB1 +3.28, EdwardIN +2.38) land positive, benched losers negative.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:36:01 -04:00

296 lines
13 KiB
Python

#!/usr/bin/env python3
"""Tape-era sharp screen over the RTDS firehose (issue #2, first flow study).
The tape (rtds.duckdb `trades`, every CLOB fill since 2026-07-17) sees ALL
fills — including wallets that lose and vanish — so unlike the data-api
leaderboards there is NO survivorship bias to unwind ([[polymarket-smart-money]]:
displayed win rates ~92% collapse to ~50% once hidden losers count). The
screen is insider.py's improbability methodology transplanted onto tape:
1. RESOLVE FROM THE TAPE. A token is proxy-resolved when its final-30min
VWAP converged (>= 0.97 won / <= 0.03 lost) AND it stopped trading
QUIET_H before tape end AND no sibling token of the same condition
disagrees (two "winners" in one cond = still-live or bad data -> drop).
Chain truth (payouts.py) then overlays the shortlist's conditions, so
50/50 refunds and operator quirks can't survive to the report
([[polymarket-resolution-truth]]).
2. HELD NET POSITIONS, not trades. Per wallet-token: net = buys - sells;
only |net| that survives to the token's last tape print is a bet (a
scalper who round-trips out is not "holding" the resolution). Entry is
the buy VWAP; P&L = net * (payout - vwap).
3. IMPROBABILITY z. Over a wallet's proxy-resolved held bets: each entry
at price p wins with prob p under the null; z = (W - Σp)/sqrt(Σp(1-p)).
The tape's 3-day window means n is small — z >= 2 with n >= MIN_BETS
and positive P&L is a CANDIDATE, not a verdict; the bench forward
window is the verdict (bench review, issue #13).
4. CONTEXT COLUMNS for triage: niche mix (title keywords), notional,
price band, burst flag (wallet's first tape print < 48h before its
biggest bet — fresh-or-returning wallet swinging immediately, the
insider fingerprint), and the follow/bench overlap (excluded from
candidates, printed as benchmark rows so the screen can be sanity
checked against wallets we already believe in).
Outputs live/tape_sharps.json (full scored rows) and a console report.
python3 tape_sharps.py # screen + chain-validate top 25
python3 tape_sharps.py --no-chain # tape-only (no RPC)
python3 tape_sharps.py --min-bets 6 # loosen for exploration
"""
import argparse
import json
import math
import os
import time
import duckdb
HERE = os.path.dirname(os.path.abspath(__file__))
DB = os.path.join(HERE, "rtds.duckdb")
OUT = os.path.join(HERE, "tape_sharps.json")
# proxy-resolution + bet thresholds (tunable; defaults are the 2026-07-20
# first-run calibration — see FINDINGS addendum in the issue)
WIN_T, LOSE_T = 0.97, 0.03 # final-30min VWAP convergence
QUIET_H = 2 # token must be quiet this long before tape end
MIN_NET_SH = 5.0 # net shares that count as a held bet
MIN_NET_USD = 20.0 # ...and its buy notional (dust bots out)
P_LO, P_HI = 0.05, 0.95 # entry band for the z math (insider.py clamps)
BURST_H = 48 # "fresh in tape" window for the burst flag
NICHES = [ # (label, TITLE ILIKE patterns) — first match wins
("esports", ["%lol:%", "%dota%", "%cs2%", "%csgo%", "%valorant%", "%esports%",
"% vs %game %", "%bilibili%", "%map %winner%"]),
("tennis", ["%tennis%", "%atp%", "%wta%", "%wimbledon%", "%open (m)%", "%set %winner%"]),
("sports", ["% vs. %", "% vs %", "% @ %", "%mlb%", "%nba%", "%nhl%", "%ufc%",
"%world cup%", "%f1%", "%grand prix%"]),
("crypto", ["%bitcoin%", "%btc%", "%ethereum%", "%eth %", "%solana%", "%xrp%",
"%price of%", "%above%on july%", "%above%on august%"]),
("politics", ["%election%", "%president%", "%senate%", "%governor%", "%mayor%",
"%nominee%", "%impeach%", "%tariff%", "%fed %", "%rate cut%"]),
("geo", ["%iran%", "%israel%", "%russia%", "%ukraine%", "%china%", "%taiwan%",
"%ceasefire%", "%strike%", "%nato%"]),
]
def known_wallets():
"""follow set + benches + the live bot itself -> {addr_lower: label}."""
out = {"0x455e252e45ee46d6c4cc1c8fadd3899d68f245a1": "OUR-BOT"}
try:
for w in json.load(open(os.path.join(HERE, "copybot.paper.json")))["wallets"]:
out[w["wallet"].lower()] = f"follow:{w.get('name', w['wallet'][:8])}"
except Exception:
pass
for fn, tag in (("watch_sharps.json", "bench"), ("watch_skilled.json", "skilled")):
try:
for w in json.load(open(os.path.join(HERE, fn))):
out.setdefault(w["wallet"].lower(), f"{tag}:{w.get('name', '?')}")
except Exception:
pass
return out
def niche_case():
whens = []
for label, pats in NICHES:
ors = " OR ".join(f"lower(title) LIKE '{p}'" for p in pats)
whens.append(f"WHEN ({ors}) THEN '{label}'")
return "CASE " + " ".join(whens) + " ELSE 'other' END"
def screen(db, min_bets):
"""One SQL pass: proxy-resolve tokens, net held positions, wallet rollup."""
t_end = db.execute("SELECT max(ts) FROM trades").fetchone()[0]
quiet = t_end - QUIET_H * 3600
db.execute(f"""
CREATE TEMP TABLE alltok AS
WITH last AS (
SELECT asset, any_value(cond) cond, max(ts) last_ts
FROM trades WHERE cond IS NOT NULL AND cond != '' GROUP BY asset
), term AS ( -- final-30min VWAP per token
SELECT t.asset,
sum(t.price * t.size) / nullif(sum(t.size), 0) term_vwap
FROM trades t JOIN last l ON t.asset = l.asset
WHERE t.ts >= l.last_ts - 1800 GROUP BY t.asset
)
SELECT l.asset, l.cond, l.last_ts, tm.term_vwap,
l.last_ts > {quiet} AS alive,
CASE WHEN tm.term_vwap >= {WIN_T} THEN 1.0
WHEN tm.term_vwap <= {LOSE_T} THEN 0.0 END AS payout
FROM last l JOIN term tm ON l.asset = tm.asset
""")
# cond veto: any sibling still trading (in-play comebacks exist), or two
# proxy-winners (multi-outcome not actually settled) -> drop the cond
db.execute("""
CREATE TEMP TABLE badcond AS
SELECT cond FROM alltok GROUP BY cond
HAVING bool_or(alive)
OR sum(CASE WHEN payout = 1.0 THEN 1 ELSE 0 END) > 1
""")
db.execute("""
CREATE TEMP TABLE tok AS
SELECT * FROM alltok WHERE NOT alive AND payout IS NOT NULL
""")
db.execute(f"""
CREATE TEMP TABLE bets AS
SELECT tr.wallet, tr.asset, any_value(tk.cond) cond,
any_value(tk.payout) payout,
any_value(tk.term_vwap) term_vwap,
sum(CASE WHEN tr.side = 'BUY' THEN tr.size ELSE -tr.size END) net,
sum(CASE WHEN tr.side = 'BUY' THEN tr.size * tr.price END)
/ nullif(sum(CASE WHEN tr.side = 'BUY' THEN tr.size END), 0) vwap,
sum(CASE WHEN tr.side = 'BUY' THEN tr.size * tr.price END) buy_usd,
count(*) n_fills,
any_value({niche_case()}) niche,
min(tr.ts) first_ts
FROM trades tr
JOIN tok tk ON tr.asset = tk.asset
WHERE tk.payout IS NOT NULL
AND tk.cond NOT IN (SELECT cond FROM badcond)
AND tr.ts <= tk.last_ts -- nothing after the last print
GROUP BY tr.wallet, tr.asset
HAVING net >= {MIN_NET_SH}
AND buy_usd >= {MIN_NET_USD}
AND vwap BETWEEN {P_LO} AND {P_HI}
""")
rows = db.execute(f"""
WITH w AS (
SELECT wallet,
count(*) n,
sum(CASE WHEN payout = 1.0 THEN 1 ELSE 0 END) wins,
sum(vwap) exp_wins,
sum(vwap * (1 - vwap)) var_sum,
sum(net * (payout - vwap)) pnl,
sum(buy_usd) notional,
avg(vwap) avg_p,
avg(n_fills) avg_fills,
median(buy_usd) med_bet,
mode(niche) top_niche,
count(DISTINCT niche) niches,
min(first_ts) first_seen
FROM bets GROUP BY wallet HAVING n >= {min_bets}
),
life AS (SELECT wallet, min(ts) tape_first FROM trades GROUP BY wallet)
SELECT w.*, life.tape_first FROM w JOIN life USING (wallet)
""").fetchall()
cols = ["wallet", "n", "wins", "exp_wins", "var_sum", "pnl", "notional",
"avg_p", "avg_fills", "med_bet", "top_niche", "niches",
"first_seen", "tape_first"]
out = []
for r in rows:
d = dict(zip(cols, r))
d["z"] = ((d["wins"] - d["exp_wins"]) / math.sqrt(d["var_sum"])
if d["var_sum"] > 0 else 0.0)
d["win_pct"] = 100.0 * d["wins"] / d["n"]
out.append(d)
return out, t_end
def chain_validate(cands, db):
"""Overlay payouts.truth on each candidate bet; rescore. Refund (0.5)
counts as neither win nor loss; unknown keeps the tape proxy."""
import payouts
conds = sorted({c for w in cands for c in w["_conds"]})
payouts.ensure(conds)
for w in cands:
n = wins = exp = var = pnl = 0.0
flip = 0
for (cond, asset, net, vwap, proxy) in w["_bets"]:
t = payouts.truth(cond, asset)
pay = proxy if t is None else t
if pay == 0.5: # refund: stake back, no win/no loss
pnl += 0.0
continue
if t is not None and t != proxy:
flip += 1
n += 1
wins += pay
exp += vwap
var += vwap * (1 - vwap)
pnl += net * (pay - vwap)
w.update(n_chain=int(n), wins_chain=int(wins), pnl_chain=round(pnl, 2),
flips=flip,
z_chain=round((wins - exp) / math.sqrt(var), 2) if var > 0 else 0.0)
return cands
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--min-bets", type=int, default=8)
ap.add_argument("--top", type=int, default=25, help="chain-validate this many")
ap.add_argument("--no-chain", action="store_true")
ap.add_argument("--max-bets", type=int, default=90, help="copyable cadence cap")
ap.add_argument("--max-fills", type=float, default=6.0, help="avg fills/bet cap")
ap.add_argument("--min-med-usd", type=float, default=50.0, help="median bet floor")
args = ap.parse_args()
db = duckdb.connect(DB, read_only=True)
known = known_wallets()
scored, t_end = screen(db, args.min_bets)
scored.sort(key=lambda d: -d["z"])
# COPYABLE means the bot could actually mirror it at 3-17s lag: discrete
# entries (few fills per bet, not continuous flow), human cadence (<=
# max-bets held bets in the 3-day tape), conviction-sized (median bet
# clears the $25 follow floor with margin). Everything else that scores
# is ALGO FLOW — real edge, uncopyable execution (they ARE the crater).
def copyable(d):
return (d["avg_fills"] <= args.max_fills and d["n"] <= args.max_bets
and d["med_bet"] >= args.min_med_usd)
fresh = [d for d in scored if d["z"] >= 2.0 and d["pnl"] > 0
and d["wallet"].lower() not in known]
interesting = [d for d in fresh if copyable(d)][:args.top]
algos = [d for d in fresh if not copyable(d)][:8]
for d in interesting:
rows = db.execute("""
SELECT cond, asset, net, vwap, payout FROM bets WHERE wallet = ?
""", [d["wallet"]]).fetchall()
d["_conds"] = [r[0] for r in rows]
d["_bets"] = rows
if interesting and not args.no_chain:
chain_validate(interesting, db)
for d in interesting:
d.pop("_conds", None); d.pop("_bets", None)
bench = [d for d in scored if d["wallet"].lower() in known]
stamp = time.strftime("%Y-%m-%d %H:%M UTC", time.gmtime(t_end))
def fmt(d):
age_h = (t_end - d["tape_first"]) / 3600
burst = "BURST" if age_h <= BURST_H else f"{age_h:.0f}h"
chain = (f" · chain z={d['z_chain']} {d['wins_chain']}/{d['n_chain']} "
f"${d['pnl_chain']:+,.0f} ({d['flips']} flips)"
if "z_chain" in d else "")
return (f"{d['wallet'][:10]}… z={d['z']:+.2f} {d['wins']}/{d['n']} "
f"({d['win_pct']:.0f}%) avg_p {d['avg_p']:.2f} "
f"med ${d['med_bet']:,.0f} f/b {d['avg_fills']:.1f} "
f"pnl ${d['pnl']:+,.0f} vol ${d['notional']:,.0f} "
f"{d['top_niche']}({d['niches']}) {burst}{chain}")
print(f"tape through {stamp} · {len(scored)} wallets with >= "
f"{args.min_bets} proxy-resolved held bets\n")
print(f"— COPYABLE candidates (z >= 2, pnl > 0, <= {args.max_bets} bets, "
f"<= {args.max_fills:.0f} fills/bet, med >= ${args.min_med_usd:.0f}) —")
for d in interesting:
print(fmt(d))
print(f"\n— algo flow (scores, but uncopyable execution; {len(algos)} of "
f"{len(fresh) - len(interesting)}) —")
for d in algos:
print(fmt(d))
print("\n— benchmark: known wallets through the same screen —")
for d in sorted(bench, key=lambda x: -x["z"])[:15]:
print(f"[{known[d['wallet'].lower()]}] {fmt(d)}")
json.dump({"tape_end": t_end, "params": {
"min_bets": args.min_bets, "win_t": WIN_T, "lose_t": LOSE_T,
"quiet_h": QUIET_H, "min_net_sh": MIN_NET_SH,
"min_net_usd": MIN_NET_USD, "p_band": [P_LO, P_HI]},
"candidates": interesting, "benchmark": bench,
"screened": len(scored)},
open(OUT, "w"), indent=1, default=float)
print(f"\nwrote {OUT}")
if __name__ == "__main__":
main()