From f19cc8d168ff4fd842268d6a55798ca286d3940f Mon Sep 17 00:00:00 2001 From: jaxperro Date: Wed, 8 Jul 2026 17:56:20 -0400 Subject: [PATCH] copybot: capture order-book snapshot (spread + 5c depth) on every copy New 'book' field in the fills ledger: {bb, ba, spread, bid5c, ask5c} from CLOB /book at fill time. The calibration experiment's weakest assumption is the flat SLIP haircut; weeks of these snapshots give an empirical fill model for the backtest and a depth gate before real money sizes up (today's probe: a stake vs $77 of bids within 5c). Best-effort, never blocks a copy. Co-Authored-By: Claude Fable 5 --- copybot.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/copybot.py b/copybot.py index efa7b7d7..4c1758c0 100644 --- a/copybot.py +++ b/copybot.py @@ -454,6 +454,35 @@ class Copybot: for b in bets.values() if b.get("pnl") is None) return st.get("cash", 0) - (self.cfg["bankroll_usd"] + adj + realized + flows) + def _book_snapshot(self, token): + """Top-of-book spread + $-depth within 5c of touch, captured per copy + into the fills ledger. The calibration experiment's weakest model + assumption is the flat SLIP haircut — 2026-07-08 probe: an open + position showed a 9c spread with $77 of bids within 5c vs our $40 + stakes. Weeks of these snapshots = an EMPIRICAL fill model for the + backtest and a depth gate before sizing up. NB captured just AFTER + our own fill, so ask-side depth is net of what we took — fine for a + first-order model. Best-effort: never blocks or fails a copy.""" + try: + req = urllib.request.Request(f"{CLOB_API}/book?token_id={token}", + headers={"User-Agent": "Mozilla/5.0"}) + b = json.loads(urllib.request.urlopen(req, timeout=6, context=SSL_CTX).read()) + bids = b.get("bids") or [] + asks = b.get("asks") or [] + bb = max((float(x["price"]) for x in bids), default=None) + ba = min((float(x["price"]) for x in asks), default=None) + + def depth(side, ref, sgn): + if ref is None: + return None + return round(sum(float(x["size"]) * float(x["price"]) for x in side + if sgn * (float(x["price"]) - ref) >= -0.05), 2) + return {"bb": bb, "ba": ba, + "spread": round(ba - bb, 4) if bb is not None and ba is not None else None, + "bid5c": depth(bids, bb, 1), "ask5c": depth(asks, ba, -1)} + except Exception: + return None + def _record_lag(self, wallet, t, fill): """Gap 1 — log the detection lag and price slippage of a copy: their fill time/price vs ours. Appends to copybot_fills.jsonl and tracks running @@ -474,6 +503,7 @@ class Copybot: "shares": round(fill["shares"], 2), "cost": round(fill["shares"] * my_p, 2), "fee": fill.get("fee", 0), "mode": "live" if self.engine.ex.live else "paper", + "book": self._book_snapshot(fill["token"]), } try: with open(os.path.join(self.here, self.fill_log), "a") as fh: