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