Files
winning-wallet-finder_github/huntwide.py
T
jaxperro f176ba134a Rebrand to Winning Wallet Finder; commit validation scripts; dev-friendly docs
- Add copyback.py (in-sample copy backtest), oos.py (out-of-sample test),
  huntwide.py (wide insider sweep) — completes the detect→hunt→validate→watch
  pipeline.
- Rewrite README around Winning Wallet Finder: the z-score idea explained, how
  the pieces fit, quickstart, data sources, live-watcher setup, honest verdict.
- Extend FINDINGS with the insider-detection results and the in-sample vs
  out-of-sample copy verdict (+545% in-sample collapsed to one-wallet variance
  out-of-sample).
- Refresh config.example.json to the current schema (discord/alchemy/watch).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 16:59:18 -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()