Files
jaxperro 2f4f2ccbbe research silo: edge factory — calibrated sim, Study A/B, forward ledger (#2, #16, #17)
research/ is a hard silo (README rules): read-only tape, no bot imports,
own launchd (com.jaxperro.research-nightly 09:15, after daily ingest).

- tape.py: proxy-resolution (the 742/742-validated method), niche + crypto
  strike/expiry/sprint parsers, tick loaders
- sim.py: FAK execution replayer; hold_s=3 fitted on 29 real labeled live
  attempts (79% fill/miss classification), price noise 2-4c, measured
  OPTIMISM BIAS -2c/fill carried into every verdict threshold
- requote.py: crater refill timing per niche (crypto 94% <4s, esports 83%
  <10s, sports needs ~25s, geo/politics minutes) -> params/requote_timing.json
- study_flow.py + robustness: in-play surge momentum. Identity NULL result:
  10 pooled controls +23.85/fill == informed +23.68 -> hypothesis revised at
  freeze, surge-EV primary, identity secondary (#16)
- study_oracle.py: oracle digital fair value. 86% craters, winner's-curse
  inversion at big edges, nothing frozen (no cell at 30 fills) (#17)
- forward.py + nightly.sh: re-scores frozen studies on last 3 tape days,
  appends forward_ledger.jsonl; verdicts ONLY from post-freeze rows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:34:22 -04:00

219 lines
9.3 KiB
Python

#!/usr/bin/env python3
"""Study A — flow-state signal: trade the informed herd's lean, not any one
wallet's print.
Hypothesis (pre-registered, issue TBD): when the trailing net $-flow of the
tape-scored informed set crosses a threshold in one in-play sports/esports
market, the market's resolution probability exceeds its price by enough to
clear real execution (calibrated sim: lag, FAK crater, fees, ~2c optimism
bias) — because the herd's aggregate lean IS the event detector, arriving
before makers reprice.
Discipline:
* informed set as-of T uses ONLY tape < T (scoring + resolutions as-of T).
* FIT day and grid are fixed below; selection rule: highest after-fee EV
per trigger at p50 lag with >= MIN_FILLS fills. Params freeze into
params/study_flow.json; the holdout day is scored ONCE with frozen
params; forward days accrue via forward.py.
* Controls: 3 activity-matched shuffled sets through the identical
pipeline — the edge must vanish when the wallets are random.
Signal: per token (sports/esports niche only), rolling W-second sum of
signed informed flow (+buy$ / -sell$). Trigger when sum >= F with the
triggering print inside PRICE_BAND; COOLDOWN_S per token. Entry at the
triggering print's price through sim; hold to resolution; stake $100 flat.
"""
import json
import os
import time
import tape
import sim as simmod
HERE = os.path.dirname(os.path.abspath(__file__))
PARAMS_F = os.path.join(HERE, "params", "study_flow.json")
# pre-registered exploration grid + universe (do not widen after the fact)
GRID = {"top_n": [50, 150], "window_s": [60, 300], "flow_usd": [300, 1000]}
PRICE_BAND = (0.10, 0.90)
NICHES = {"sports", "esports"}
COOLDOWN_S = 900
STAKE = 100.0
MIN_FILLS = 30
SET_MIN_Z, SET_MIN_BETS = 2.5, 6
def informed_set(db, before_ts, top_n):
"""Top-N wallets by improbability z using ONLY tape before before_ts."""
tape.build_resolved(db, t_end=before_ts)
rows = db.execute(f"""
WITH bets AS (
SELECT tr.wallet,
any_value(tk.payout) payout,
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
FROM trades tr JOIN res_tok tk ON tr.asset = tk.asset
WHERE tr.ts <= {before_ts}
GROUP BY tr.wallet, tr.asset
HAVING net >= 5 AND vwap BETWEEN 0.05 AND 0.95
)
SELECT wallet,
count(*) n,
sum(CASE WHEN payout=1.0 THEN 1 ELSE 0 END) wins,
sum(vwap) exp_w, sum(vwap*(1-vwap)) var_s,
sum(net*(payout - vwap)) pnl
FROM bets GROUP BY wallet
HAVING n >= {SET_MIN_BETS} AND var_s > 0 AND pnl > 0
""").fetchall()
scored = []
for w, n, wins, exp_w, var_s, pnl in rows:
z = (wins - exp_w) / (var_s ** 0.5)
if z >= SET_MIN_Z:
scored.append((z, w))
scored.sort(reverse=True)
return [w for _, w in scored[:top_n]]
def matched_random_set(db, before_ts, size, seed):
"""Activity-matched control: wallets with >= 20 trades before before_ts,
deterministic pseudo-shuffle by md5(wallet||seed) — no RNG state."""
rows = db.execute(f"""
SELECT wallet FROM trades WHERE ts <= {before_ts}
GROUP BY wallet HAVING count(*) >= 20
ORDER BY md5(wallet || '{seed}') LIMIT {size}""").fetchall()
return [w for (w,) in rows]
def signals(db, wallets, t_lo, t_hi, window_s, flow_usd):
"""Rolling-window triggers over the informed set's prints."""
if not wallets:
return []
rows = db.execute("""
SELECT tr.asset, tr.ts, tr.side, tr.price, tr.size, any_value(tr.title)
FROM trades tr
WHERE tr.ts > ? AND tr.ts <= ?
AND tr.wallet IN (SELECT unnest(?::varchar[]))
GROUP BY tr.asset, tr.ts, tr.side, tr.price, tr.size, tr.tx
ORDER BY tr.asset, tr.ts""", [t_lo, t_hi, wallets]).fetchall()
trig, cur, buf, last_trig = [], None, [], {}
for asset, ts, side, price, size, title in rows:
if asset != cur:
cur, buf = asset, []
if tape.niche(title) not in NICHES:
continue
usd = price * size * (1 if side == "BUY" else -1)
buf.append((ts, usd))
while buf and buf[0][0] < ts - window_s:
buf.pop(0)
flow = sum(u for _, u in buf)
if flow >= flow_usd and PRICE_BAND[0] <= price <= PRICE_BAND[1] \
and ts - last_trig.get(asset, 0) >= COOLDOWN_S:
last_trig[asset] = ts
trig.append({"asset": asset, "ts": ts, "p_ref": price,
"flow": round(flow), "title": title})
return trig
def score(db, triggers, lag_s, hold_s):
"""Sim each trigger; outcome from res_tok (resolved-by-tape-end only)."""
s = simmod.Sim(db, lag_s=lag_s, hold_s=hold_s)
res = dict(fills=0, misses=0, pending=0, pnl=0.0, wins=0, staked=0.0)
for t in triggers:
pay = db.execute("SELECT payout::DOUBLE FROM res_tok WHERE asset = ?",
[t["asset"]]).fetchone()
r = s.try_buy(t["asset"], t["ts"], t["p_ref"], stake_usd=STAKE)
if not r["filled"]:
res["misses"] += 1
continue
if pay is None: # fired, filled, not yet resolved
res["pending"] += 1
continue
res["fills"] += 1
res["staked"] += r["cost"]
pnl = r["shares"] * (pay[0] - r["price"]) - r["fee"]
res["pnl"] += pnl
res["wins"] += pay[0] == 1.0
if res["fills"]:
res["ev_per_fill"] = round(res["pnl"] / res["fills"], 2)
res["hit"] = round(res["wins"] / res["fills"], 3)
res["pnl"] = round(res["pnl"], 2)
return res
def run_cell(db, as_of, t_lo, t_hi, top_n, window_s, flow_usd, hold_s, wallets=None):
S = wallets if wallets is not None else informed_set(db, as_of, top_n)
tape.build_resolved(db) # scoring truth = full tape
trig = signals(db, S, t_lo, t_hi, window_s, flow_usd)
out = {"triggers": len(trig), "set_size": len(S)}
for lag, tag in ((simmod.LAG_P50, "p50"), (simmod.LAG_P90, "p90")):
out[tag] = score(db, trig, lag, hold_s)
return out, trig
def main():
db = tape.connect()
cal = json.load(open(os.path.join(HERE, "params", "sim_calibration.json")))
hold_s = cal["hold_s"]
lo, hi = db.execute("SELECT min(ts), max(ts) FROM trades").fetchone()
day = lambda d, h=0: time.mktime(time.strptime(f"2026-07-{d:02d}", "%Y-%m-%d")) \
- time.timezone + h * 3600
fit_lo, fit_hi = day(19), day(20) # fit day: Jul 19 UTC
hold_lo, hold_hi = day(20), hi # holdout: Jul 20 (partial)
print("== FIT (Jul 19, set as-of Jul 19 00:00 UTC) ==")
results = []
for tn in GRID["top_n"]:
for w in GRID["window_s"]:
for f in GRID["flow_usd"]:
r, _ = run_cell(db, fit_lo, fit_lo, fit_hi, tn, w, f, hold_s)
ev = r["p50"].get("ev_per_fill")
results.append(((tn, w, f), r))
print(f"top{tn:<4} W={w:<4} F=${f:<5} -> trig {r['triggers']:>4} "
f"fills {r['p50']['fills']:>3} miss {r['p50']['misses']:>3} "
f"EV/fill {ev if ev is not None else '—'} "
f"hit {r['p50'].get('hit', '—')}")
eligible = [(p, r) for p, r in results
if r["p50"]["fills"] >= MIN_FILLS and "ev_per_fill" in r["p50"]]
if not eligible:
print("\nNO cell reached MIN_FILLS — study inconclusive at this tape size.")
return
best_p, best_r = max(eligible, key=lambda pr: pr[1]["p50"]["ev_per_fill"])
tn, w, f = best_p
print(f"\nFROZEN: top_n={tn} window={w}s flow=${f} "
f"(fit EV/fill {best_r['p50']['ev_per_fill']}, hit {best_r['p50']['hit']})")
print("\n== CONTROLS (fit day, matched random sets) ==")
controls = []
for seed in (1, 2, 3):
S = matched_random_set(db, fit_lo, tn, seed)
r, _ = run_cell(db, fit_lo, fit_lo, fit_hi, tn, w, f, hold_s, wallets=S)
controls.append(r)
print(f"seed {seed}: trig {r['triggers']} fills {r['p50']['fills']} "
f"EV/fill {r['p50'].get('ev_per_fill', '—')} "
f"hit {r['p50'].get('hit', '—')}")
print("\n== HOLDOUT (Jul 20 partial, set as-of Jul 20 00:00 UTC) ==")
hr, htrig = run_cell(db, hold_lo, hold_lo, hold_hi, tn, w, f, hold_s)
print(f"trig {hr['triggers']} fills {hr['p50']['fills']} "
f"miss {hr['p50']['misses']} pending {hr['p50']['pending']} "
f"EV/fill {hr['p50'].get('ev_per_fill', '—')} "
f"hit {hr['p50'].get('hit', '—')} (p90 lag: EV "
f"{hr['p90'].get('ev_per_fill', '—')})")
json.dump({"frozen": {"top_n": tn, "window_s": w, "flow_usd": f,
"hold_s": hold_s, "price_band": PRICE_BAND,
"niches": sorted(NICHES), "cooldown_s": COOLDOWN_S,
"stake": STAKE,
"set_min_z": SET_MIN_Z, "set_min_bets": SET_MIN_BETS},
"fit": best_r, "fit_grid": [{"params": p, **r} for p, r in results],
"controls": controls, "holdout": hr,
"frozen_at": time.strftime("%Y-%m-%d %H:%M UTC", time.gmtime()),
"pending_triggers": htrig},
open(PARAMS_F, "w"), indent=1, default=float)
print(f"\nfroze {PARAMS_F}")
if __name__ == "__main__":
main()