mirror of
https://github.com/jaxperro/winning-wallet-finder.git
synced 2026-07-27 15:57:47 +00:00
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>
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user