Files
winning-wallet-finder_github/research/event_leadlag.py
T
jaxperro e6287c788d research: event_leadlag (T9) — POSITIVE: same-outcome siblings reprice slowly after leader bursts
2,191 episodes (400 highest-volume event/outcome groups, name-matched
semantics only): follower drift +4.12c mean in leader direction at +5m
(43% >+2c vs 17% adverse; p50=0 — thin siblings often don't print).
Tradable leg chain-true: n=2,028 · EV +$9.73/$100 buying the follower at
its last print in the leader's direction. STATED OPTIMISM: stale-print
entry (the T4 lesson — prints are not books); resting asks may have
repriced without printing. Stage-2 = execution realism (live book reads
or a paper scanner leg). Theme with T6: edge lives where repricing is
SLOW — the far side of the requote wall.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:16:48 -04:00

125 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""T9 EXPLORATORY (2026-07-23) — same-event lead-lag: when an event's most
-traded market moves hard in-play, do sibling markets carrying THE SAME
OUTCOME NAME reprice with a fillable lag?
Semantic mapping problem solved narrowly: direction is only claimed where
the follower has an outcome with the exact same (lowercased) name as the
leader's moved outcome (team/player name) — moneyline vs map/set/half
winner vs series markets. No claim on O/Us or unrelated props.
v0 method: cond→event + cond→{outcome→asset} from orders_matched. Leader
per event = most prints. Burst = leader outcome's print moving >= 10c
within 120s (in-play), cooldown 600s/event. Follower read at burst t:
last print p0; drift = p(t+300s) p0 in the leader-move direction;
tradable leg = buy follower at p0, grade to chain (payouts_for).
Kill: drift <= fees (~2c) at n>=300 episodes, or chain EV <= 0."""
import sys
import time
sys.path.insert(0, "/Users/jaxmakielski/polymarket-smart-money/research")
import tape # noqa: E402
import forward as fwd # noqa: E402
MOVE_C = 0.10
MOVE_WIN = 120
DRIFT_WIN = 300
COOLDOWN = 600
BAND = (0.05, 0.95)
def main():
db = tape.connect()
tape.build_resolved(db)
print("building event/outcome maps…", flush=True)
db.execute("""
CREATE TEMP TABLE om AS
SELECT json_extract_string(payload,'$.eventSlug') ev,
json_extract_string(payload,'$.conditionId') cond,
lower(json_extract_string(payload,'$.outcome')) outc,
json_extract_string(payload,'$.asset') asset,
count(*) n
FROM aux WHERE type='orders_matched'
AND json_extract_string(payload,'$.eventSlug') IS NOT NULL
GROUP BY 1,2,3,4""")
# events with >=2 conds sharing an outcome name (the mappable set)
pairs = db.execute("""
WITH x AS (SELECT ev, outc, count(DISTINCT cond) nc, sum(n) vol
FROM om WHERE outc NOT IN ('yes','no','over','under','')
GROUP BY 1,2 HAVING count(DISTINCT cond) >= 2)
SELECT ev, outc FROM x ORDER BY vol DESC LIMIT 400""").fetchall()
print(f"mappable (event, outcome) groups: {len(pairs)}", flush=True)
episodes = []
for gi, (ev, outc) in enumerate(pairs):
toks = db.execute("""SELECT cond, asset, n FROM om
WHERE ev=? AND outc=?""", [ev, outc]).fetchall()
if len(toks) < 2:
continue
toks.sort(key=lambda r: -r[2])
lead_asset = toks[0][1]
followers = [r[1] for r in toks[1:3]] # top-2 followers
prints = db.execute("""SELECT ts, price::DOUBLE FROM trades
WHERE asset=? ORDER BY ts""", [lead_asset]).fetchall()
last_ep = 0.0
for i in range(1, len(prints)):
ts, p = prints[i]
if ts - last_ep < COOLDOWN:
continue
j = i - 1
while j >= 0 and ts - prints[j][0] <= MOVE_WIN:
j -= 1
if j < 0 or j == i - 1:
base = prints[max(j, 0)][1]
else:
base = prints[j + 1][1]
mv = p - base
if abs(mv) < MOVE_C:
continue
last_ep = ts
for fa in followers:
r0 = db.execute("""SELECT price::DOUBLE FROM trades
WHERE asset=? AND ts<=? ORDER BY ts DESC LIMIT 1""",
[fa, ts]).fetchone()
r1 = db.execute("""SELECT price::DOUBLE FROM trades
WHERE asset=? AND ts<=? ORDER BY ts DESC LIMIT 1""",
[fa, ts + DRIFT_WIN]).fetchone()
if not r0 or not r1:
continue
p0, p1 = r0[0], r1[0]
if not (BAND[0] <= p0 <= BAND[1]):
continue
sgn = 1 if mv > 0 else -1
episodes.append({"ev": ev, "a": fa, "ts": ts, "sgn": sgn,
"p0": p0, "drift": (p1 - p0) * sgn})
if (gi + 1) % 100 == 0:
print(f" … {gi+1}/{len(pairs)} groups · "
f"{len(episodes)} episodes", flush=True)
print(f"episodes: {len(episodes)}", flush=True)
if not episodes:
return
d = sorted(e["drift"] for e in episodes)
n = len(d)
print(f"follower drift(+{DRIFT_WIN}s, leader direction): "
f"mean {sum(d)/n*100:+.2f}c · p50 {d[n//2]*100:+.2f}c · "
f"frac>+2c {sum(x > 0.02 for x in d)/n:.0%} · "
f"frac<-2c {sum(x < -0.02 for x in d)/n:.0%}", flush=True)
pays = fwd.payouts_for(db, [e["a"] for e in episodes])
graded = []
for e in episodes:
p = pays.get(e["a"])
if p is None or p == 0.5:
continue
side_px = e["p0"] if e["sgn"] > 0 else 1 - e["p0"]
side_pay = p if e["sgn"] > 0 else 1 - p
if not (BAND[0] <= side_px <= BAND[1]):
continue
graded.append(100.0 / side_px * (side_pay - side_px))
if graded:
print(f"tradable leg (buy follower in leader direction, chain): "
f"n={len(graded)} · EV/$100 "
f"{sum(graded)/len(graded):+.2f}", flush=True)
if __name__ == "__main__":
main()