#24 v0: three-book reconciliation — backtest story is 82-112% unseen+missed; +312% is decoration, rankings only

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jaxperro
2026-07-23 19:14:59 -04:00
parent ecd9f49593
commit bfd725621d
5 changed files with 166 additions and 2 deletions
+20
View File
@@ -721,6 +721,26 @@ def main():
mf = (OUT[:-5] if OUT.endswith(".json") else OUT) + "_missed.json"
json.dump(mfull, open(os.path.join(HERE, mf) if not os.path.isabs(mf)
else mf, "w"), separators=(",", ":"))
# companion 2: EVERY replayed bet with join keys (cond/asset) — the
# three-book reconciliation study (#24) joins these against both bots'
# fills/misses to decompose backtest-vs-reality per named gap bucket
bfull = []
_mids = {id(m) for m in missed}
for b in stream:
st_ = ("missed" if id(b) in _mids else
"open" if b.get("kind") == "open" or b.get("cur") is not None
else "sold" if b.get("sold") else "resolved")
bfull.append({"cond": b.get("cond"), "asset": b.get("asset"),
"name": b["name"], "p": b.get("p"),
"their": b.get("their"), "stake": b.get("stake"),
"entry_t": int(b.get("entry_t") or 0), "status": st_,
"capped": bool(b.get("capped")),
"wp": b.get("wp"),
"pnl": round(b["pnl"], 2) if b.get("pnl") is not None
else None})
bf = (OUT[:-5] if OUT.endswith(".json") else OUT) + "_bets.json"
json.dump(bfull, open(os.path.join(HERE, bf) if not os.path.isabs(bf)
else bf, "w"), separators=(",", ":"))
save_slug_cache()
print(f"portfolio[{DAYS}d rolling]: equity ${equity:,.0f} ({(equity-BANK)/BANK*100:+.0f}%) | banked ${reserve:,.0f} "
f"| realized ${realized:+,.0f} | fees ${fees_paid:,.0f} | next stake ${cur_stake():,.0f} "
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env python3
"""#24 v0 (2026-07-23) — THREE-BOOK RECONCILIATION: where does the
follow-only backtest's story (+312%) diverge from the bots' reality?
Every backtest bet lands in one bucket per book (paper, live):
UNSEEN the bot has no record of the signal at all (fills, misses,
nothing) universe/screen gap pooled in v0 (cursor gaps,
pre-boot signals, filter drift). The backtest replays these
from the cache as if they were copyable; reality never saw
them.
MISSED the bot detected and skipped/failed (FAK crater, floor,
depth, cash). The backtest's 100%-fill assumption pays this.
FILLED both took it the FILL-PRICE gap prices the difference
between the backtest's feeless their-price entry and the
bot's actual my_price + taker fee.
$100-normalized per signal (sizing paths deliberately excluded in v0
sizing/exit/fee-path buckets are v1, per the #24 spec). Chain-graded
(payouts_for scorer law); refunds excluded. Window: parity era only
(PARITY_T0, same boundary as live/edge.py) so live comparisons are
honest. Companion input: live/portfolio_follow_bets.json (portfolio.py
--follow-only). NOT pre-registered measurement of our own books."""
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import tape # noqa: E402
import forward as fwd # noqa: E402
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
PARITY_T0 = 1784260140 # keep == live/edge.py
FEE = 0.03
def load_jsonl(path):
out = []
try:
for ln in open(os.path.join(ROOT, path)):
try:
out.append(json.loads(ln))
except Exception:
pass
except FileNotFoundError:
pass
return out
def bot_views():
"""{book: {"fills": {token: my_price}, "missed": set(token)}}"""
v = {}
for book, ff in (("paper", "copybot_fills.jsonl"),
("live", "copybot_fills.live.jsonl")):
fills = {}
for r in load_jsonl(ff):
if (r.get("side") == "SELL" or r.get("untracked")
or not r.get("my_price")):
continue
fills[str(r["token"])] = r["my_price"]
v[book] = {"fills": fills, "missed": set()}
for book, feed in (("paper", "live/copybot_live_full.json"),
("live", "live/copybot_live_real_full.json")):
try:
d = json.load(open(os.path.join(ROOT, feed)))
for m in d.get("missed") or []:
if m.get("token"):
v[book]["missed"].add(str(m["token"]))
except Exception:
pass
for r in load_jsonl("copybot_missed_archive.live.jsonl"):
if r.get("token"):
v["live"]["missed"].add(str(r["token"]))
for r in load_jsonl("copybot_missed_archive.jsonl"):
if r.get("token"):
v["paper"]["missed"].add(str(r["token"]))
return v
def main():
bets = json.load(open(os.path.join(ROOT, "live",
"portfolio_follow_bets.json")))
bets = [b for b in bets if b.get("entry_t", 0) >= PARITY_T0
and b.get("asset") and b.get("p")]
print(f"backtest bets in the parity era with join keys: {len(bets)}",
flush=True)
db = tape.connect()
tape.build_resolved(db)
pays = fwd.payouts_for(db, [b["asset"] for b in bets])
v = bot_views()
graded = []
for b in bets:
wp = pays.get(b["asset"], b.get("wp"))
if wp is None or wp == 0.5:
continue
b["_wp"] = wp
b["_ev_bt"] = (100.0 / b["p"]) * (wp - b["p"]) # feeless maker conv.
graded.append(b)
print(f"chain-graded (refunds/pending excluded): {len(graded)}\n",
flush=True)
for book in ("paper", "live"):
f, mset = v[book]["fills"], v[book]["missed"]
buckets = {"FILLED": [], "MISSED": [], "UNSEEN": []}
pg = 0.0
for b in graded:
a = b["asset"]
if a in f:
buckets["FILLED"].append(b)
myp = f[a]
ev_real = (100.0 / myp) * (b["_wp"] - myp) \
- FEE * (100.0 / myp) * min(myp, 1 - myp)
pg += b["_ev_bt"] - ev_real
elif a in mset:
buckets["MISSED"].append(b)
else:
buckets["UNSEEN"].append(b)
tot_bt = sum(b["_ev_bt"] for b in graded)
print(f"== {book.upper()} (n={len(graded)} signals · backtest "
f"$100-EV {tot_bt:+,.0f}) ==")
for k in ("FILLED", "MISSED", "UNSEEN"):
bs = buckets[k]
ev = sum(b["_ev_bt"] for b in bs)
share = 100 * ev / tot_bt if tot_bt else 0
print(f" {k:>7}: n={len(bs):>4} · backtest EV in bucket "
f"{ev:+9.0f} ({share:+5.1f}% of story)")
print(f" fill-price gap on FILLED (their_p feeless -> my_p+fee): "
f"{pg:+,.0f}")
un = buckets["UNSEEN"]
if un:
byw = {}
for b in un:
byw[b["name"]] = byw.get(b["name"], 0) + 1
top = sorted(byw.items(), key=lambda kv: -kv[1])[:4]
print(f" unseen by wallet: {top}")
print(flush=True)
json.dump({"n": len(graded)}, open(os.path.join(
HERE, "copy_reconcile.json"), "w"))
if __name__ == "__main__":
main()