<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oliver Zehentleitner</title>
    <description>The latest articles on DEV Community by Oliver Zehentleitner (@oliverzehentleitner).</description>
    <link>https://dev.to/oliverzehentleitner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2124155%2F0d79bc59-db7b-4879-bd1a-36dc2f79d1c2.png</url>
      <title>DEV Community: Oliver Zehentleitner</title>
      <link>https://dev.to/oliverzehentleitner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oliverzehentleitner"/>
    <language>en</language>
    <item>
      <title>Your Binance L2 Order Book Can Be Gap-Free and Still Be Wrong</title>
      <dc:creator>Oliver Zehentleitner</dc:creator>
      <pubDate>Thu, 11 Jun 2026 11:51:41 +0000</pubDate>
      <link>https://dev.to/oliverzehentleitner/your-binance-l2-order-book-can-be-gap-free-and-still-be-wrong-1kk8</link>
      <guid>https://dev.to/oliverzehentleitner/your-binance-l2-order-book-can-be-gap-free-and-still-be-wrong-1kk8</guid>
      <description>&lt;p&gt;A Binance order book looks simple until you try to keep one correct for hours, days, or weeks.&lt;/p&gt;

&lt;p&gt;At first, the task appears straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;subscribe to Binance depth updates over WebSocket,&lt;/li&gt;
&lt;li&gt;fetch an order book snapshot over REST,&lt;/li&gt;
&lt;li&gt;apply every update in sequence,&lt;/li&gt;
&lt;li&gt;expose the resulting bids and asks to your strategy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is enough to produce something that &lt;em&gt;looks&lt;/em&gt; like a live Binance L2 order book.&lt;/p&gt;

&lt;p&gt;It is not enough to prove that the book is correct.&lt;/p&gt;

&lt;p&gt;A local order book can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no detected sequence gap,&lt;/li&gt;
&lt;li&gt;a healthy WebSocket connection,&lt;/li&gt;
&lt;li&gt;plausible best bids and asks,&lt;/li&gt;
&lt;li&gt;valid-looking quantities,&lt;/li&gt;
&lt;li&gt;and thousands of price levels that no longer exist on Binance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article covers the full lifecycle of a trustworthy Binance order book: initialization, gap detection, retention, resynchronization, observability, redundancy, and the point where an in-process cache should become shared infrastructure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A wrong order book is worse than no order book. No data stops you. Bad data lies to you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nowqvjf4ner9jqgt6uj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nowqvjf4ner9jqgt6uj.png" alt=" " width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Binance L2 order book?
&lt;/h2&gt;

&lt;p&gt;A Level 2, or L2, order book represents aggregated liquidity at individual price levels.&lt;/p&gt;

&lt;p&gt;For each side of the market, it contains pairs such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;price, quantity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bids"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"104250.10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.842"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"104250.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.307"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"asks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"104250.20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.514"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"104250.30"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.191"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The highest bid and lowest ask form the top of book.&lt;/p&gt;

&lt;p&gt;The full price ladder is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spread and slippage calculations,&lt;/li&gt;
&lt;li&gt;market-depth analysis,&lt;/li&gt;
&lt;li&gt;liquidity monitoring,&lt;/li&gt;
&lt;li&gt;order-flow research,&lt;/li&gt;
&lt;li&gt;market making,&lt;/li&gt;
&lt;li&gt;arbitrage,&lt;/li&gt;
&lt;li&gt;execution planning,&lt;/li&gt;
&lt;li&gt;and trading-system risk controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Binance does not continuously send a complete order book to every client. It gives you separate pieces that your application must combine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Binance actually gives you
&lt;/h2&gt;

&lt;p&gt;To construct a local Binance order book, you normally use:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a REST depth snapshot,&lt;/li&gt;
&lt;li&gt;a WebSocket diff-depth stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The REST response gives you a bounded view of the order book at a specific &lt;code&gt;lastUpdateId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The WebSocket stream then sends incremental changes containing fields such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;U  first update ID in the event
u  final update ID in the event
pu previous event's final update ID on relevant futures streams
b  bid updates
a  ask updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your code is responsible for turning those two independent data sources into one coherent state.&lt;/p&gt;

&lt;p&gt;That requires more than applying JSON messages to a dictionary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first trap: the snapshot race condition
&lt;/h2&gt;

&lt;p&gt;A common implementation does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch snapshot
connect WebSocket
apply updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That ordering is unsafe.&lt;/p&gt;

&lt;p&gt;Updates can occur after the snapshot was created but before the WebSocket subscription becomes active. Those updates are lost, and the local Binance order book starts with a silent gap.&lt;/p&gt;

&lt;p&gt;The safer sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connect WebSocket
buffer depth events
fetch REST snapshot
discard obsolete buffered events
find the first event that continues from the snapshot
replay the remaining buffer
continue with live updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="nf"&gt;start_depth_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;on_event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_depth_snapshot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;last_update_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lastUpdateId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nb"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;buffer&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;u&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;last_update_id&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;first_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;find_first_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;U&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;last_update_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;u&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;apply_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first_event&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;
    &lt;span class="nf"&gt;apply_depth_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact protocol differs slightly between Binance Spot and Futures, so do not blindly reuse a Spot implementation for Futures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequence continuity is a trust boundary
&lt;/h2&gt;

&lt;p&gt;Once the Binance order book is synchronized, every event must continue the sequence you already processed.&lt;/p&gt;

&lt;p&gt;For Spot, the steady-state check is commonly based on the next expected update ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;U&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;last_update_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;request_full_resync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For relevant Futures streams, Binance provides &lt;code&gt;pu&lt;/code&gt;, which references the previous event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;last_update_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;request_full_resync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;apply_updates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;last_update_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;u&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A broken sequence is not merely a warning.&lt;/p&gt;

&lt;p&gt;It means your local order book no longer has a proven relationship to the exchange state.&lt;/p&gt;

&lt;p&gt;Do not log the problem and keep serving the same book. Mark it as out of sync, stop trusting it, and initialize it again from a fresh snapshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying bid and ask updates
&lt;/h2&gt;

&lt;p&gt;Each depth event contains price-level changes.&lt;/p&gt;

&lt;p&gt;The basic rule is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply_level&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part is easy.&lt;/p&gt;

&lt;p&gt;The dangerous assumption is that correct update application plus correct sequence validation guarantees a correct long-running Binance order book.&lt;/p&gt;

&lt;p&gt;It does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second trap: a gap-free order book can still rot
&lt;/h2&gt;

&lt;p&gt;While maintaining the UNICORN Binance Local Depth Cache, I investigated a report that bids and asks kept growing beyond their expected size.&lt;/p&gt;

&lt;p&gt;The implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;started from a valid snapshot,&lt;/li&gt;
&lt;li&gt;processed updates in order,&lt;/li&gt;
&lt;li&gt;removed levels when Binance sent quantity &lt;code&gt;0&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;and detected broken update continuity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It still accumulated stale price levels.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;A bounded REST snapshot defines the initial view your cache can validate. The diff stream can later introduce price levels outside the active region that your application intends to maintain.&lt;/p&gt;

&lt;p&gt;A naive implementation inserts every streamed level and removes it only when a future update explicitly sets its quantity to zero.&lt;/p&gt;

&lt;p&gt;But a reliable cleanup event does not necessarily arrive for every price level your cache has collected. Levels can move outside the bounded region you are validating and remain in local memory indefinitely.&lt;/p&gt;

&lt;p&gt;The result is a growing archive of plausible-looking historical price levels.&lt;/p&gt;

&lt;p&gt;I call them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;orphaned levels,&lt;/li&gt;
&lt;li&gt;ghost levels,&lt;/li&gt;
&lt;li&gt;or ghost orders.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are especially dangerous because they do not look corrupted.&lt;/p&gt;

&lt;p&gt;A stale level such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;104000.00 BTCUSDT bid quantity 0.73
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;looks completely reasonable.&lt;/p&gt;

&lt;p&gt;Your best bid and best ask may still be correct while deeper liquidity, cumulative volume, order-book shape, and slippage estimates are increasingly wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a 25-hour Binance order book test showed
&lt;/h2&gt;

&lt;p&gt;To isolate this behavior, I ran two BTCUSDT depth caches side by side for 25.10 hours.&lt;/p&gt;

&lt;p&gt;Both received the same WebSocket data and were audited against REST snapshots at the same times.&lt;/p&gt;

&lt;p&gt;The only relevant difference was retention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;naive cache&lt;/strong&gt;: apply updates and delete only on &lt;code&gt;quantity == 0&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fixed cache&lt;/strong&gt;: apply the same updates but actively prune the maintained book back to its intended top-1000 corridor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;BTC moved only 1.88% during the test window. This was a calm market, not a flash crash or extreme volatility event.&lt;/p&gt;

&lt;p&gt;At the final audit:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;th&gt;Local bids&lt;/th&gt;
&lt;th&gt;Local asks&lt;/th&gt;
&lt;th&gt;Bid levels matching REST&lt;/th&gt;
&lt;th&gt;Ask levels matching REST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Naive&lt;/td&gt;
&lt;td&gt;20,758&lt;/td&gt;
&lt;td&gt;9,116&lt;/td&gt;
&lt;td&gt;24.09%&lt;/td&gt;
&lt;td&gt;39.82%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pruned&lt;/td&gt;
&lt;td&gt;1,011&lt;/td&gt;
&lt;td&gt;1,078&lt;/td&gt;
&lt;td&gt;87.83%&lt;/td&gt;
&lt;td&gt;91.74%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The naive Binance order book started almost perfectly aligned.&lt;/p&gt;

&lt;p&gt;Then it rotted.&lt;br&gt;
&lt;a href="https://oliver-zehentleitner.github.io/binance-depthcache-forensics/report_naive.html" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuz88ak5kfokxsphu2c64.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open interactive 3D chart: &lt;a href="https://oliver-zehentleitner.github.io/binance-depthcache-forensics/report_naive.html" rel="noopener noreferrer"&gt;report_naive.html&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Warning: large Plotly file, about 78 MB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://oliver-zehentleitner.github.io/binance-depthcache-forensics/report_fixed.html" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flx1raw2nzctgxj6uueza.png" alt=" " width="799" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open interactive 3D chart: &lt;a href="https://oliver-zehentleitner.github.io/binance-depthcache-forensics/report_fixed.html" rel="noopener noreferrer"&gt;report_fixed.html&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Warning: large Plotly file, about 41 MB.&lt;/p&gt;

&lt;p&gt;The important lesson is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gap-free does not mean correct forever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sequence checks answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did I miss a depth event?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A retention policy answers a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which price levels am I still able and willing to claim as part of my maintained L2 order book?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A production implementation needs both.&lt;/p&gt;
&lt;h2&gt;
  
  
  Add an explicit retention policy
&lt;/h2&gt;

&lt;p&gt;If your application claims to maintain a bounded top-&lt;em&gt;N&lt;/em&gt; Binance order book, enforce that boundary.&lt;/p&gt;

&lt;p&gt;A simplified pruning function could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;prune_side&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;descending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ordered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;descending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_quantity&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applied to both sides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;prune_side&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;prune_side&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;descending&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For bids, higher prices are better.&lt;/p&gt;

&lt;p&gt;For asks, lower prices are better.&lt;/p&gt;

&lt;p&gt;The exact retention strategy depends on your use case, market, performance requirements, and the depth corridor you promise to consumers. The critical part is that the policy must exist.&lt;/p&gt;

&lt;p&gt;An initial snapshot size alone is not a retention policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resync must be normal behavior
&lt;/h2&gt;

&lt;p&gt;Many trading systems treat resynchronization as an exceptional failure.&lt;/p&gt;

&lt;p&gt;It should be treated as part of normal order book lifecycle management.&lt;/p&gt;

&lt;p&gt;A resync may be required after:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a missing update ID,&lt;/li&gt;
&lt;li&gt;an invalid first buffered event,&lt;/li&gt;
&lt;li&gt;a WebSocket reconnect,&lt;/li&gt;
&lt;li&gt;a buffer overflow,&lt;/li&gt;
&lt;li&gt;an internal processing delay,&lt;/li&gt;
&lt;li&gt;an invariant violation,&lt;/li&gt;
&lt;li&gt;a crossed local book,&lt;/li&gt;
&lt;li&gt;or an operator-requested refresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A robust state machine should expose states such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INITIALIZING
SYNCHRONIZED
OUT_OF_SYNC
RESYNCING
STOPPED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumers should never have to infer those states from stale timestamps or log files.&lt;/p&gt;

&lt;p&gt;Before returning bids or asks, the service should know whether the Binance order book is currently trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not silently serve stale order book data
&lt;/h2&gt;

&lt;p&gt;Imagine an API consumer requests BTCUSDT asks while the cache is resynchronizing.&lt;/p&gt;

&lt;p&gt;The convenient behavior is to return the last known data with HTTP 200.&lt;/p&gt;

&lt;p&gt;The honest behavior is to return an explicit trust-state error.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#6000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DepthCache 'BTCUSDT' for 'binance.com' is out of sync!"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The consumer can then choose to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retry,&lt;/li&gt;
&lt;li&gt;temporarily reduce confidence,&lt;/li&gt;
&lt;li&gt;use a redundant replica,&lt;/li&gt;
&lt;li&gt;disable a strategy,&lt;/li&gt;
&lt;li&gt;or stop trading that market.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That decision belongs to the consumer.&lt;/p&gt;

&lt;p&gt;The order book layer's responsibility is to preserve the truth about its own state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate more than update IDs
&lt;/h2&gt;

&lt;p&gt;Sequence validation is essential, but useful runtime invariants go further.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Invariant&lt;/th&gt;
&lt;th&gt;Possible meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;best_bid &amp;gt;= best_ask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;crossed or corrupted local order book&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unexpected update-ID transition&lt;/td&gt;
&lt;td&gt;missing, duplicated, or reordered event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;event buffer exceeds capacity&lt;/td&gt;
&lt;td&gt;consumer cannot keep up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stream silence exceeds threshold&lt;/td&gt;
&lt;td&gt;dead or stalled connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;maintained depth grows beyond policy&lt;/td&gt;
&lt;td&gt;missing or ineffective pruning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cache age exceeds threshold&lt;/td&gt;
&lt;td&gt;stale data path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;replica states disagree for too long&lt;/td&gt;
&lt;td&gt;one cache may be unhealthy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A crossed book should be handled carefully because fast-moving markets and independently timed observations can complicate comparisons. But inside one consistently applied local state, impossible relationships are valuable warning signals.&lt;/p&gt;

&lt;p&gt;The broader rule is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make corruption observable before it reaches the strategy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why one correct local order book is still not enough
&lt;/h2&gt;

&lt;p&gt;For one bot and one market, an in-process Binance L2 order book can be a reasonable design.&lt;/p&gt;

&lt;p&gt;Then the system grows.&lt;/p&gt;

&lt;p&gt;You add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;another strategy,&lt;/li&gt;
&lt;li&gt;a dashboard,&lt;/li&gt;
&lt;li&gt;a monitoring service,&lt;/li&gt;
&lt;li&gt;an alerting process,&lt;/li&gt;
&lt;li&gt;a backtesting or research consumer,&lt;/li&gt;
&lt;li&gt;a Node.js application,&lt;/li&gt;
&lt;li&gt;a Go execution service,&lt;/li&gt;
&lt;li&gt;or a script that only needs the top five asks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If each application reconstructs the same Binance order book independently, each one now owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket lifecycle handling,&lt;/li&gt;
&lt;li&gt;REST snapshot initialization,&lt;/li&gt;
&lt;li&gt;buffering,&lt;/li&gt;
&lt;li&gt;sequence validation,&lt;/li&gt;
&lt;li&gt;pruning,&lt;/li&gt;
&lt;li&gt;resync,&lt;/li&gt;
&lt;li&gt;memory management,&lt;/li&gt;
&lt;li&gt;and exchange rate-limit consumption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A reconnect storm duplicates the same initialization work.&lt;/p&gt;

&lt;p&gt;Two processes can temporarily hold different views of the same market.&lt;/p&gt;

&lt;p&gt;Restarting application logic also destroys and rebuilds market-data state that did not need to be coupled to that application.&lt;/p&gt;

&lt;p&gt;At this point, the order book is no longer an implementation detail of the bot.&lt;/p&gt;

&lt;p&gt;It is infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat the Binance order book as a shared service
&lt;/h2&gt;

&lt;p&gt;The architectural shift is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;one synchronized order book layer
many independent consumers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot A -&amp;gt; Binance -&amp;gt; local BTCUSDT cache
bot B -&amp;gt; Binance -&amp;gt; local BTCUSDT cache
dashboard -&amp;gt; Binance -&amp;gt; local BTCUSDT cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Binance
   |
shared synchronized depth-cache layer
   |          |          |
 bot A      bot B     dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides a single place to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;snapshot and WebSocket synchronization,&lt;/li&gt;
&lt;li&gt;sequence validation,&lt;/li&gt;
&lt;li&gt;retention,&lt;/li&gt;
&lt;li&gt;resync,&lt;/li&gt;
&lt;li&gt;cache-state reporting,&lt;/li&gt;
&lt;li&gt;replicas,&lt;/li&gt;
&lt;li&gt;failover,&lt;/li&gt;
&lt;li&gt;monitoring,&lt;/li&gt;
&lt;li&gt;and client access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The consuming application can remain focused on strategy or analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical implementation with UBDCC
&lt;/h2&gt;

&lt;p&gt;I built the open-source UNICORN Binance DepthCache Cluster, or UBDCC, for this architecture.&lt;/p&gt;

&lt;p&gt;UBDCC runs synchronized Binance DepthCaches as a standalone service and exposes them over HTTP/JSON.&lt;/p&gt;

&lt;p&gt;It is written in Python, but clients do not need to be Python applications.&lt;/p&gt;

&lt;p&gt;Anything that can call an HTTP endpoint can consume the same Binance order book:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python,&lt;/li&gt;
&lt;li&gt;JavaScript,&lt;/li&gt;
&lt;li&gt;Go,&lt;/li&gt;
&lt;li&gt;Rust,&lt;/li&gt;
&lt;li&gt;Java,&lt;/li&gt;
&lt;li&gt;C#,&lt;/li&gt;
&lt;li&gt;PHP,&lt;/li&gt;
&lt;li&gt;Bash,&lt;/li&gt;
&lt;li&gt;dashboards,&lt;/li&gt;
&lt;li&gt;spreadsheets,&lt;/li&gt;
&lt;li&gt;or internal services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A local test setup starts with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;ubdcc
ubdcc start &lt;span class="nt"&gt;--dcn&lt;/span&gt; 4
ubdcc-dashboard start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a BTCUSDT DepthCache with two replicas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s1"&gt;'http://127.0.0.1:42081/create_depthcaches'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "exchange": "binance.com",
    "markets": ["BTCUSDT"],
    "desired_quantity": 2
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query the first five asks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s1"&gt;'http://127.0.0.1:42081/get_asks?exchange=binance.com&amp;amp;market=BTCUSDT&amp;amp;limit_count=5'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query the first five bids:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s1"&gt;'http://127.0.0.1:42081/get_bids?exchange=binance.com&amp;amp;market=BTCUSDT&amp;amp;limit_count=5'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With two replicas, the same market exists on separate DepthCache nodes. If one node fails, another synchronized replica can serve the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9srx52j61aseva2nj1v1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9srx52j61aseva2nj1v1.png" alt=" " width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;UBDCC is not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a trading strategy,&lt;/li&gt;
&lt;li&gt;an execution engine,&lt;/li&gt;
&lt;li&gt;a backtesting framework,&lt;/li&gt;
&lt;li&gt;or a promise of profitable trading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a market-data layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local library, service, or cluster?
&lt;/h2&gt;

&lt;p&gt;Not every project needs distributed infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a local in-process Binance order book when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;you have one process,&lt;/li&gt;
&lt;li&gt;only one consumer needs the data,&lt;/li&gt;
&lt;li&gt;temporary loss during restart is acceptable,&lt;/li&gt;
&lt;li&gt;and you are prepared to own synchronization and retention logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use a standalone shared service when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;several applications need the same markets,&lt;/li&gt;
&lt;li&gt;applications use different programming languages,&lt;/li&gt;
&lt;li&gt;rebuilding caches during every deployment is wasteful,&lt;/li&gt;
&lt;li&gt;or you want one observable trust state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use replicas or a cluster when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;order book availability matters,&lt;/li&gt;
&lt;li&gt;one process must not be a single point of failure,&lt;/li&gt;
&lt;li&gt;you maintain many markets,&lt;/li&gt;
&lt;li&gt;initialization pressure must be distributed,&lt;/li&gt;
&lt;li&gt;or consumers require explicit failover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture should follow the reliability contract you actually need.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist for a trustworthy Binance L2 order book
&lt;/h2&gt;

&lt;p&gt;Before trusting a local or remote Binance order book, verify that the implementation can answer all of these:&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Does it subscribe before fetching the snapshot?&lt;/li&gt;
&lt;li&gt;Does it buffer WebSocket events while the snapshot is in flight?&lt;/li&gt;
&lt;li&gt;Does it discard stale events correctly?&lt;/li&gt;
&lt;li&gt;Does it find the correct first applicable update?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Continuity
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Does it validate Spot and Futures sequences using the correct fields?&lt;/li&gt;
&lt;li&gt;Does any continuity failure trigger a full resync?&lt;/li&gt;
&lt;li&gt;Can the event buffer overflow silently?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  State management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Is synchronization state explicit and queryable?&lt;/li&gt;
&lt;li&gt;Are reads rejected or marked while the cache is out of sync?&lt;/li&gt;
&lt;li&gt;Are reconnect and resync events observable?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Long-running correctness
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Is there an explicit retention boundary?&lt;/li&gt;
&lt;li&gt;Are old levels pruned?&lt;/li&gt;
&lt;li&gt;Does maintained depth remain bounded?&lt;/li&gt;
&lt;li&gt;Is the complete book audited periodically, not only best bid and ask?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Can the cache recover without restarting the strategy?&lt;/li&gt;
&lt;li&gt;Are metrics and health states exposed?&lt;/li&gt;
&lt;li&gt;Can replicas fail independently?&lt;/li&gt;
&lt;li&gt;Can consumers distinguish fresh data from stale data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any answer is “I do not know,” that is where to investigate next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Most Binance order book tutorials stop after they have combined one REST snapshot with one WebSocket stream.&lt;/p&gt;

&lt;p&gt;That is the beginning, not the end.&lt;/p&gt;

&lt;p&gt;A production-grade Binance L2 order book needs at least four separate guarantees:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Correct bootstrap&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The snapshot and buffered events form one continuous initial state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous sequence validation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Missing or reordered updates cause a loud resync.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explicit retention&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The cache does not silently become a museum of historical price levels.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Observable trust state&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Consumers know whether the book is synchronized before using it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And once several applications need the same data, there is a fifth:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shared, redundant infrastructure&lt;/strong&gt;
Order book reconstruction should not be duplicated inside every bot.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The difficult part is not receiving Binance depth data.&lt;/p&gt;

&lt;p&gt;The difficult part is knowing when that data still deserves to be called an order book.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code and related research
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-depth-cache-cluster" rel="noopener noreferrer"&gt;UNICORN Binance DepthCache Cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/oliver-zehentleitner/unicorn-binance-local-depth-cache" rel="noopener noreferrer"&gt;UNICORN Binance Local Depth Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.technopathy.club/your-binance-order-book-is-wrong-here-s-why" rel="noopener noreferrer"&gt;Your Binance Order Book Is Wrong — Here's Why&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.technopathy.club/your-binance-depthcache-is-rotting-here-s-the-proof-in-25-hours" rel="noopener noreferrer"&gt;Your Binance DepthCache is rotting — here's the proof in 25 hours&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.technopathy.club/ubdcc-deep-dive-building-a-trust-layer-for-binance-order-books" rel="noopener noreferrer"&gt;UBDCC Deep-Dive: Building a Trust Layer for Binance Order Books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.technopathy.club/from-pip-install-to-a-redundant-binance-order-book-cluster-ubdcc-dashboard-quickstart" rel="noopener noreferrer"&gt;UBDCC local quickstart&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions, corrections, failed test cases, and contradictory results are welcome in the comments.&lt;/p&gt;




&lt;p&gt;I hope you found this informative and useful.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.binance.com/en/square/profile/oliver-zehentleitner" rel="noopener noreferrer"&gt;Binance Square&lt;/a&gt;, &lt;a href="https://github.com/oliver-zehentleitner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://x.com/unicorn_oz" rel="noopener noreferrer"&gt;X&lt;/a&gt;, and &lt;a href="https://www.linkedin.com/in/oliver-zehentleitner/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, or join &lt;a href="https://t.me/unicorndevs" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; for updates on my latest publications. Constructive feedback is always appreciated.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and happy coding! ¯\_(ツ)_/¯&lt;/p&gt;

</description>
      <category>python</category>
      <category>cryptocurrency</category>
      <category>opensource</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
