Files
jaxperro 8958279ee8 cleanup: archive everything the live system no longer touches
- retired-infra/: Railway config, Mac launchd runner, GH-Actions cron
  (worker is Fly.io arn now; Actions cron last fired 2026-07-02)
- live-research/: the June selection experiments (strategy/followability/
  pnl_basket/pnl_focused/backtest_june/clean_test + outputs)
- us-venue/: the scrapped Polymarket-US listability probe (+ its
  env-gated ONLY_CONDS replay filter stays in portfolio.py — generally
  useful for subset replays)
- root sweeps hunt/huntwide/oos/copyback/watch.json -> archive/
- untracked logs/CSVs of dead experiments -> archive/local/ (gitignored)
- READMEs updated: Fly migration, geoblock gotcha, new file map

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 17:18:46 -04:00

129 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Wide insider hunt — source candidate wallets from many markets across the
volume spectrum, dedup, score each wallet's full record once, and tally the
insider-grade ones (z-score / p-value) with the markets they showed up in."""
import csv
import json
import ssl
import urllib.request
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor, as_completed
import insider
ctx = ssl._create_unverified_context()
GAMMA = "https://gamma-api.polymarket.com"
N_MARKETS = 100 # markets to source traders from
TOP_TRADERS = 20 # top traders (by notional) per market
MAX_WALLETS = 400 # cap on unique wallets to score (expensive step)
def get(u):
req = urllib.request.Request(u, headers={"User-Agent": "Mozilla/5.0"})
return json.loads(urllib.request.urlopen(req, timeout=30, context=ctx).read())
def source_markets():
"""A spread of active binary markets: high-volume AND mid/niche (where
insiders operate). Walk the volume ranking and sample across it."""
mkts, offset = [], 0
for _ in range(8):
try:
g = get(f"{GAMMA}/markets?limit=100&offset={offset}&active=true"
f"&closed=false&order=volume24hr&ascending=false")
except Exception:
break
if not g:
break
for m in g:
try:
oc = json.loads(m.get("outcomes", "[]"))
except Exception:
oc = []
if [o.lower() for o in oc] == ["yes", "no"] and m.get("conditionId"):
mkts.append((m["conditionId"], m.get("question", "?")[:48]))
if len(g) < 100:
break
offset += 100
# sample evenly across the ranking so we get a volume spread, not just whales
if len(mkts) > N_MARKETS:
step = len(mkts) / N_MARKETS
mkts = [mkts[int(i * step)] for i in range(N_MARKETS)]
return mkts
def main():
key = insider.sm_load_key()
print(f"sourcing traders from up to {N_MARKETS} markets...", flush=True)
markets = source_markets()
print(f" {len(markets)} binary markets", flush=True)
wallet_markets = defaultdict(set)
wallet_name = {}
def grab(cm):
cond, title = cm
try:
cands, _ = insider.market_traders(cond, top=TOP_TRADERS)
return title, cands
except Exception:
return title, []
with ThreadPoolExecutor(max_workers=12) as ex:
for title, cands in ex.map(grab, markets):
for c in cands:
wallet_markets[c["wallet"]].add(title)
wallet_name.setdefault(c["wallet"], c["username"])
print(f" {len(wallet_markets)} unique candidate wallets", flush=True)
# prioritize wallets seen in more markets (more active → enough resolved bets)
wallets = sorted(wallet_markets, key=lambda w: len(wallet_markets[w]), reverse=True)[:MAX_WALLETS]
print(f"scoring {len(wallets)} wallets...", flush=True)
rows, done = [], 0
with ThreadPoolExecutor(max_workers=12) as ex:
futs = {ex.submit(insider.analyze, {"wallet": w, "username": wallet_name[w]}): w
for w in wallets}
for f in as_completed(futs):
done += 1
try:
r = f.result()
except Exception:
r = None
if r:
r["markets"] = sorted(wallet_markets[r["wallet"]])
rows.append(r)
if done % 50 == 0:
print(f" {done}/{len(wallets)}", flush=True)
rows.sort(key=lambda r: r["z"], reverse=True)
with open("huntwide.csv", "w", newline="") as fp:
w = csv.writer(fp)
w.writerow(["z", "pval", "wins", "n", "pre24_pct", "trades", "username", "wallet", "markets"])
for r in rows:
w.writerow([r["z"], r["pval"], r["wins"], r["n"], r["pre24_pct"],
r["trades"], r["username"], r["wallet"], " | ".join(r["markets"][:6])])
extra = [r for r in rows if r["z"] >= 5]
grade = [r for r in rows if 4 <= r["z"] < 5]
sharp = [r for r in rows if 3 <= r["z"] < 4]
print(f"\n{'='*80}")
print(f"SCORED {len(rows)} wallets with >=15 resolved bets")
print(f" extraordinary (z>=5): {len(extra)} insider-grade (z 4-5): {len(grade)} "
f"strong sharp (z 3-4): {len(sharp)}")
print(f"{'='*80}")
for label, group in (("EXTRAORDINARY (z>=5)", extra), ("INSIDER-GRADE (z 4-5)", grade)):
if not group:
continue
print(f"\n{label}:")
for r in group:
print(f" z={r['z']:>4} p={insider.fmt_p(r['pval']):>8} {r['wins']}/{r['n']} "
f"pre24={r['pre24_pct']:>3}% trades={r['trades']:>6} {r['username'][:20]}")
print(f" markets: {', '.join(r['markets'][:4])}")
print(f"\nfull table → huntwide.csv")
if __name__ == "__main__":
main()