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

251 lines
9.3 KiB
Python

#!/usr/bin/env python3
"""Study B — crypto oracle fair value vs the book.
The tape's `crypto_prices` aux stream IS the venue's settlement feed
(Binance-sourced, ms-stamped, ~1/s per symbol since 2026-07-19 19:55). Every
strike/sprint crypto market is a digital option on that feed, so fair value
is computable tick-by-tick with no basis risk:
above K, expiry T: fair(Yes) = Phi( ln(S_t/K) / (sigma*sqrt(tau)) )
between K1..K2: Phi(ln(K2/S)/sv) - Phi(ln(K1/S)/sv)
sprint (window t0..t1): strike = S_{t0} read from the same feed
Down/No tokens: 1 - fair(up-side). sigma = trailing 30min realized
vol of 1s log-returns (drift negligible at these horizons).
Signal: at a market print, edge = fair - print >= E for that token ->
simulated FAK entry (calibrated sim), hold to resolution (tape truth).
IMPORTANT scope honesty: tick coverage is ~21h, so there is no holdout —
this run only CHOOSES E (grid below) and freezes it; ALL belief is deferred
to the forward ledger. Also grades the live bot's own crypto fills against
fair value at their fill times (objective score of the 0xbadaf319-class
copies)."""
import bisect
import json
import math
import os
import statistics as st
import time
import tape
import sim as simmod
HERE = os.path.dirname(os.path.abspath(__file__))
PARAMS_F = os.path.join(HERE, "params", "study_oracle.json")
EDGE_GRID = [0.04, 0.07, 0.10]
VOL_WIN_S = 1800
TAU_MIN, TAU_MAX = 60, 12 * 3600
COOLDOWN_S = 300
STAKE = 100.0
MIN_FILLS = 30
UP_WORDS = {"up", "yes"}
DOWN_WORDS = {"down", "no"}
def phi(x):
return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))
class TickSeries:
def __init__(self, ticks):
self.ts = [t for t, _ in ticks]
self.px = [p for _, p in ticks]
def at(self, t):
i = bisect.bisect_right(self.ts, t) - 1
return self.px[i] if i >= 0 else None
def vol_1s(self, t, win=VOL_WIN_S):
"""stdev of 1s log returns over the trailing window (per-sqrt-second)."""
lo = bisect.bisect_left(self.ts, t - win)
hi = bisect.bisect_right(self.ts, t)
if hi - lo < 60:
return None
rets = []
for i in range(lo + 1, hi):
dt = self.ts[i] - self.ts[i - 1]
if dt <= 0:
continue
r = math.log(self.px[i] / self.px[i - 1]) / math.sqrt(dt)
rets.append(r)
return st.pstdev(rets) if len(rets) >= 30 else None
def fair_value(mkt, up_side, S, sigma, t):
tau = mkt["t1"] - t
if not (TAU_MIN <= tau <= TAU_MAX) or not S or not sigma:
return None
sv = sigma * math.sqrt(tau)
if sv <= 0:
return None
k = mkt["kind"]
if k == "sprint":
if mkt.get("s0") is None:
return None
f_up = phi(math.log(S / mkt["s0"]) / sv)
elif k == "above":
f_up = phi(math.log(S / mkt["k1"]) / sv)
elif k == "below":
f_up = 1.0 - phi(math.log(S / mkt["k1"]) / sv)
elif k == "between":
f_up = phi(math.log(mkt["k2"] / S) / sv) - phi(math.log(mkt["k1"] / S) / sv)
else:
return None
return f_up if up_side else 1.0 - f_up
def outcome_map(db):
"""asset -> lowercase outcome name, from the orders_matched aux stream."""
rows = db.execute("""
SELECT json_extract_string(payload,'$.asset'),
lower(any_value(json_extract_string(payload,'$.outcome')))
FROM aux WHERE type = 'orders_matched'
AND json_extract_string(payload,'$.outcome') != ''
GROUP BY 1""").fetchall()
return {a: o for a, o in rows if a and o}
def crypto_universe(db, outcomes, series):
"""Parseable crypto tokens with a knowable side + tick coverage."""
rows = db.execute("""
SELECT asset, any_value(title), min(ts), max(ts)
FROM trades GROUP BY asset""").fetchall()
out = []
for asset, title, lo, hi in rows:
mkt = tape.crypto_parse(title or "")
if not mkt or mkt["sym"] not in series:
continue
o = outcomes.get(asset, "")
up = o in UP_WORDS or (o == "" and mkt["kind"] != "sprint")
if o and o not in UP_WORDS | DOWN_WORDS:
continue # unknown side label — skip honestly
if mkt["kind"] == "sprint":
if not o:
continue # sprints NEED the Up/Down label
mkt["s0"] = series[mkt["sym"]].at(mkt["t0"])
if mkt["s0"] is None:
continue
out.append({"asset": asset, "mkt": mkt, "up": up, "title": title})
return out
def run_study(db, hold_s):
series = {s: TickSeries(tape.load_ticks(db, s))
for s in ("btcusdt", "ethusdt", "solusdt", "xrpusdt",
"bnbusdt", "dogeusdt")}
tick_lo = min(s.ts[0] for s in series.values() if s.ts)
outcomes = outcome_map(db)
tape.build_resolved(db)
uni = crypto_universe(db, outcomes, series)
payout = {a: p for a, p in db.execute(
"SELECT asset, payout::DOUBLE FROM res_tok").fetchall()}
print(f"crypto universe: {len(uni)} tokens with side + ticks "
f"({sum(1 for u in uni if u['asset'] in payout)} resolved in-tape)")
events = [] # candidate mispricings at prints
for u in uni:
prints = db.execute("""SELECT ts, price FROM trades
WHERE asset = ? AND ts >= ? ORDER BY ts""",
[u["asset"], tick_lo]).fetchall()
s = series[u["mkt"]["sym"]]
last_ev = 0.0
for ts, px in prints:
if ts - last_ev < COOLDOWN_S:
continue
S = s.at(ts)
sig = s.vol_1s(ts)
f = fair_value(u["mkt"], u["up"], S, sig, ts)
if f is None:
continue
edge = f - float(px)
if edge > 0.02: # collect loosely; grid filters below
last_ev = ts
events.append({"asset": u["asset"], "ts": ts, "p_ref": float(px),
"fair": round(f, 4), "edge": round(edge, 4),
"kind": u["mkt"]["kind"], "title": u["title"]})
print(f"candidate mispricing events (edge > 2c): {len(events)}")
sim = simmod.Sim(db, hold_s=hold_s)
grid = {}
for E in EDGE_GRID:
sel = [e for e in events if e["edge"] >= E]
fills = wins = 0
pnl = staked = 0.0
misses = pending = 0
for e in sel:
r = sim.try_buy(e["asset"], e["ts"], e["p_ref"], stake_usd=STAKE)
if not r["filled"]:
misses += 1
continue
pay = payout.get(e["asset"])
if pay is None:
pending += 1
continue
fills += 1
staked += r["cost"]
pnl += r["shares"] * (pay - r["price"]) - r["fee"]
wins += pay == 1.0
grid[E] = {"events": len(sel), "fills": fills, "misses": misses,
"pending": pending,
"ev_per_fill": round(pnl / fills, 2) if fills else None,
"hit": round(wins / fills, 3) if fills else None,
"pnl": round(pnl, 2)}
print(f"E >= {E:.2f}: {grid[E]}")
eligible = [(E, g) for E, g in grid.items()
if g["fills"] >= MIN_FILLS and g["ev_per_fill"] is not None]
frozen_E = max(eligible, key=lambda eg: eg[1]["ev_per_fill"])[0] \
if eligible else None
return {"grid": grid, "frozen_edge": frozen_E, "n_universe": len(uni),
"tick_lo": tick_lo}, events
def grade_our_fills(db):
"""Fair-value edge of the live bot's own crypto fills at fill time."""
series = {}
graded = []
for ln in open(os.path.join(tape.ROOT, "copybot_fills.live.jsonl")):
r = json.loads(ln)
if r.get("side") == "SELL" or r.get("untracked"):
continue
mkt = tape.crypto_parse(r.get("title") or "")
if not mkt:
continue
sym = mkt["sym"]
if sym not in series:
series[sym] = TickSeries(tape.load_ticks(db, sym))
s = series[sym]
if not s.ts or r["ts"] < s.ts[0] or mkt["kind"] == "sprint":
continue
S, sig = s.at(r["ts"]), s.vol_1s(r["ts"])
up = (r.get("outcome") or "").lower() in UP_WORDS
f = fair_value(mkt, up, S, sig, r["ts"])
if f is None:
continue
graded.append({"title": r["title"][:60], "outcome": r.get("outcome"),
"px": r["my_price"], "fair": round(f, 3),
"edge": round(f - r["my_price"], 3),
"wallet": r.get("name")})
return graded
def main():
db = tape.connect()
cal = json.load(open(os.path.join(HERE, "params", "sim_calibration.json")))
res, events = run_study(db, cal["hold_s"])
graded = grade_our_fills(db)
print(f"\nour crypto fills graded vs fair value: {len(graded)}")
for g in graded:
print(f" {g['edge']:+.3f} {g['wallet']:<12} {g['outcome']:<4} "
f"@{g['px']:.3f} fair {g['fair']:.3f} {g['title']}")
json.dump({**res, "our_fills_graded": graded,
"frozen_at": time.strftime("%Y-%m-%d %H:%M UTC", time.gmtime()),
"note": "NO holdout exists (21h ticks) — belief deferred "
"entirely to forward_ledger"},
open(PARAMS_F, "w"), indent=1, default=float)
print(f"\nfroze {PARAMS_F}")
if __name__ == "__main__":
main()