{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Rebuild the Binance order book yourself — and check it\n",
    "\n",
    "One hour of SOL/USDT from [l2ws.com](https://l2ws.com): full-depth L2 diffs, our independent top-of-book record (`ob_*`), and the trade tape. No key needed for this sample.\n",
    "\n",
    "We replay the diffs from the last REST snapshot, then compare our best bid/ask with `ob_*` **at the same exchange event** (`lob_update_id == final_id`). Expected result: 100 % match."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "SAMPLE = \"https://l2ws.com/static/samples/sol-usdt-l2-hour\"\n",
    "import pandas as pd, pyarrow.parquet as pq, io, urllib.request, heapq, time\n",
    "def load(name):\n",
    "    raw = urllib.request.urlopen(urllib.request.Request(f\"{SAMPLE}/{name}.parquet\", headers={\"User-Agent\": \"Mozilla/5.0\"})).read()\n",
    "    t = pq.read_table(io.BytesIO(raw))\n",
    "    return t.to_pandas(), {k.decode(): v.decode() for k, v in (t.schema.metadata or {}).items()}\n",
    "l2, meta = load(\"l2_SOLUSDT\"); ob, _ = load(\"ob_SOLUSDT\"); tr, _ = load(\"trades_SOLUSDT\")\n",
    "pmul = 10 ** int(meta.get(\"price_decimals\", 2)); vmul = 10 ** int(meta.get(\"vol_decimals\", 8))\n",
    "print(f\"l2 {len(l2):,} rows · ob {len(ob):,} · trades {len(tr):,} · price scale 1/{pmul}\")"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# Replay: reset on snapshot, apply diffs in (epoch, ingest_seq) order, skip diffs older than the snapshot.\n",
    "l2 = l2.sort_values([\"epoch\", \"ingest_seq\"], kind=\"stable\")\n",
    "ref = {(int(e), int(s)): (int(round(b * pmul)), int(round(a * pmul))) for e, s, b, a in ob[[\"epoch\", \"ingest_seq\", \"bid_px\", \"ask_px\"]].itertuples(index=False)}\n",
    "bids, asks, hb, ha = {}, {}, [], []\n",
    "def best():\n",
    "    while hb and bids.get(-hb[0], 0) <= 0: heapq.heappop(hb)\n",
    "    while ha and asks.get(ha[0], 0) <= 0: heapq.heappop(ha)\n",
    "    return (-hb[0] if hb else None), (ha[0] if ha else None)\n",
    "checks = ok = 0; prev = None; skip_until = -1; stale = False; t0 = time.time()\n",
    "for e, s, snap, side, p, v, fid in l2[[\"epoch\", \"ingest_seq\", \"is_snapshot\", \"side\", \"price_int\", \"volume_int\", \"final_id\"]].itertuples(index=False):\n",
    "    key = (int(e), int(s))\n",
    "    if key != prev:\n",
    "        if prev in ref and not stale:\n",
    "            checks += 1; ok += best() == ref[prev]\n",
    "        stale = False\n",
    "        if snap: bids.clear(); asks.clear(); hb.clear(); ha.clear(); skip_until = int(fid)\n",
    "        elif int(fid) <= skip_until: stale = True\n",
    "        prev = key\n",
    "    if stale: continue\n",
    "    book, heap, sign = (bids, hb, -1) if side == 0 else (asks, ha, 1)\n",
    "    if v <= 0: book.pop(int(p), None)\n",
    "    else:\n",
    "        if int(p) not in book: heapq.heappush(heap, sign * int(p))\n",
    "        book[int(p)] = int(v)\n",
    "if prev in ref and not stale: checks += 1; ok += best() == ref[prev]\n",
    "b, a = best()\n",
    "print(f\"checked {checks:,} book states · matched {ok:,} ({ok / checks:.4%}) · {time.time() - t0:.1f}s\")\n",
    "print(f\"book at the end of the hour: bid {b / pmul} / ask {a / pmul} · {len(bids):,} bid levels, {len(asks):,} ask levels\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If `matched` is 100 %, you just reproduced our order book tick by tick from raw diffs — the same check our nightly quality job runs for every symbol and every day (`/status.json → days[].book_full_replay`).\n",
    "\n",
    "Want all days, SQL over the whole archive, or a backtest on it? `pip install cruscy`, get a free key at https://l2ws.com/account."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}