Appearance
Personalize your experience
Depth Chart
Order BookCumulative depth on both sides of the book, aligned to the ladder in every position, with big-order bubbles, liquidity flashes, prints and a slippage readout.
Overview
The depth chart plots the resting size from the outward: to the left of the mid and asks to the (or below and above in the orientation). One size is shared by both , so any asymmetry you see is a real difference in liquidity.
Alignment with the ladder
| Depth position | Orientation | Aligned to |
|---|---|---|
| Left / | : up, bids down | The ladder's . Each level is drawn on its row, the mid on the spread row, and the size grows away from the ladder. |
| Top / Bottom | : left, asks | The ladder's centre divider. Each half of the book spans its own half of the chart. |
| Off | — | Hidden. |
What is drawn
Controls
| Control | Action |
|---|---|
| Cycle step → → line | |
| LIN / SQRT / LOG | Size scale. (Beside the ladder, linear is used so the matches the bars.) |
| Toggle the per-level size bars | |
| − + | Finer / coarser price . "·A" marks Auto (raw ). |
| − zoom + | Narrow or widen the price around the mid (10–100%) |
| Wheel / pinch | Zoom; double-click or the reset button returns to 100% |
| Keyboard | + / − zoom, 0 resets, Esc clears the |
| Click / tap | Pick a price; it is pinned in both panes |
Hover readout
- Side and level (BID L12), price.
- Size at the level, depth and that depth's share of the side.
- .
- average: the average fill for a market order that takes everything from the to this level, and its slippage in bp.
- Distance from mid, wall score, and your working size if you have orders there.
Stability
The axes ease toward their targets instead of snapping: they grow quickly (τ = 70 ms) so nothing clips, and slowly (τ = 260 ms). Each level's depth also eases (τ = 120 ms) and is clamped to a running maximum, so the stays monotone while it moves. On a symbol or change the eased state resets, so a new book never tweens from the old one.
const tau = target > current ? 70 : 260; // grow fast, shrink calmly
next = current + (target - current) * (1 - Math.exp(-dt / tau));
// per level, kept monotone outward from the touch
let running = 0;
for (const entry of levels) running = Math.max(running, eased(entry)), entry.total = running;Standalone HTML implementation
A dependency-free canvas version of the depth with wall bubbles.
1<!doctype html>2<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width,initial-scale=1">6<title>Depth chart — order book view</title>7<style>8 html, body { margin: 0; height: 100%; background: #0b0e14; overflow: hidden; }9 canvas { display: block; width: 100%; height: 100%; }10 #status { position: fixed; left: 8px; bottom: 6px; font: 600 10px ui-monospace, Menlo, monospace;11 color: #cbd5e1; background: rgba(15,23,42,.85); border: 1px solid rgba(148,163,184,.3);12 border-radius: 4px; padding: 2px 6px; pointer-events: none; }13 #status i { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 5px; background: #f59e0b; }14</style>15</head>16<body>17<canvas id="c"></canvas>18<div id="status"><i></i>connecting…</div>19<script>20// ─── Canvas + palette ───────────────────────────────────────────────────────21const C = document.getElementById('c'), X = C.getContext('2d');22let W = 0, H = 0;23function fit() {24 const r = C.getBoundingClientRect(), d = Math.min(devicePixelRatio || 1, 2);25 W = r.width; H = r.height; C.width = W * d; C.height = H * d; X.setTransform(d, 0, 0, d, 0, 0);26}27addEventListener('resize', fit); fit();28const BID = '#0ecb81', ASK = '#f6465d', GRID = 'rgba(148,163,184,.16)';29const TEXT = '#e2e8f0', MUTED = '#64748b', ACCENT = '#8b5cf6', WARN = '#f59e0b';3031// ─── Feed settings ──────────────────────────────────────────────────────────32// Real-time Binance spot book. Change SYMBOL / GROUP for another market33// (e.g. 'ETHUSDT' with GROUP 0.1). If the exchange cannot be reached the page34// falls back to a synthetic book and says so in the status badge.35const SYMBOL = 'BTCUSDT';36const GROUP = 1; // aggregation step (price bucket)37const LEVELS = 30; // levels per side after grouping38const TICK = GROUP; // views size cells / buckets by the grouped step3940// ─── Real-time Binance book: REST snapshot + 100ms diff stream ──────────────41// Binance's documented procedure: buffer the diff stream, fetch a snapshot,42// drop events with u <= lastUpdateId, then apply in order; a gap43// (U != previous u + 1) means resnapshot. qty 0 deletes a level.44const live = { bids: new Map(), asks: new Map(), lastU: 0, lastE: 0, synced: false, buffer: [], ok: false, ups: [] };45const prints = [];46let source = 'connecting';47const statusEl = document.getElementById('status');48function setStatus(text, color) { statusEl.innerHTML = '<i style="background:' + color + '"></i>' + text; }4950function applyDiff(e) {51 for (const [p, q] of e.b) { const v = +q; v === 0 ? live.bids.delete(+p) : live.bids.set(+p, v); }52 for (const [p, q] of e.a) { const v = +q; v === 0 ? live.asks.delete(+p) : live.asks.set(+p, v); }53 live.lastU = e.u; live.lastE = e.E;54 const now = Date.now(); live.ups.push(now); while (now - live.ups[0] > 1000) live.ups.shift();55}56async function resnapshot() {57 live.synced = false;58 const r = await fetch('https://api.binance.com/api/v3/depth?symbol=' + SYMBOL + '&limit=1000');59 if (!r.ok) throw new Error('HTTP ' + r.status);60 const s = await r.json();61 live.bids = new Map(s.bids.map(([p, q]) => [+p, +q]));62 live.asks = new Map(s.asks.map(([p, q]) => [+p, +q]));63 live.lastU = s.lastUpdateId;64 const pending = live.buffer.filter(e => e.u > s.lastUpdateId); live.buffer = [];65 let first = true;66 for (const e of pending) {67 if (first && !(e.U <= live.lastU + 1 && e.u >= live.lastU + 1)) continue;68 first = false; applyDiff(e);69 }70 live.synced = true; live.ok = true; source = 'live';71}72// Snapshot failures fall back to the synthetic book and keep retrying; the page73// switches back to live as soon as a snapshot lands.74function trySnapshot() {75 resnapshot().catch(() => { fallback(); setTimeout(trySnapshot, 5000); });76}77function connect() {78 const s = SYMBOL.toLowerCase();79 let ws;80 try { ws = new WebSocket('wss://stream.binance.com:9443/stream?streams=' + s + '@depth@100ms/' + s + '@aggTrade'); }81 catch (e) { fallback(); return; }82 ws.onopen = () => trySnapshot();83 ws.onmessage = m => {84 const { stream, data } = JSON.parse(m.data);85 if (stream.endsWith('@aggTrade')) {86 // m = buyer is maker → the aggressor sold87 prints.push({ t: data.T, side: data.m ? 'sell' : 'buy', qty: +data.q, price: +data.p });88 if (prints.length > 400) prints.shift();89 return;90 }91 if (!live.synced) { live.buffer.push(data); if (live.buffer.length > 600) live.buffer.shift(); return; }92 if (data.u <= live.lastU) return;93 if (data.U > live.lastU + 1) { live.buffer = [data]; trySnapshot(); return; }94 applyDiff(data);95 };96 ws.onerror = () => { if (!live.ok) fallback(); };97 ws.onclose = () => { live.synced = false; setTimeout(connect, live.ok ? 2000 : 8000); };98}99setTimeout(() => { if (!live.ok) fallback(); }, 15000);100connect();101102// ─── Synthetic fallback (same shape as the live book) ───────────────────────103let center = 76500;104const base = { bid: [], ask: [] };105for (let i = 0; i < LEVELS; i++) { base.bid.push(0.4 + i * 0.5 + Math.random() * 3); base.ask.push(0.4 + i * 0.5 + Math.random() * 3); }106function fallback() {107 if (source === 'synthetic' || live.ok) return;108 source = 'synthetic';109 setStatus('SYNTHETIC · live feed unreachable, retrying', '#94a3b8');110}111function tickSynthetic() {112 if (Math.random() < 0.35) center += (Math.random() < 0.5 ? -1 : 1) * GROUP;113 for (const s of ['bid', 'ask']) for (let i = 0; i < LEVELS; i++) {114 let q = base[s][i] * (0.92 + Math.random() * 0.16);115 if (Math.random() < 0.003) q *= 5;116 if (q > 40) q *= 0.7;117 base[s][i] = q;118 }119 if (Math.random() < 0.6) {120 const side = Math.random() < 0.5 ? 'buy' : 'sell';121 prints.push({ t: Date.now(), side, qty: +(Math.random() ** 3 * 8 + 0.01).toFixed(3), price: side === 'buy' ? center + GROUP : center });122 if (prints.length > 400) prints.shift();123 }124}125function tick() { if (source === 'synthetic') tickSynthetic(); }126127// ─── Snapshot: group raw levels into LEVELS buckets per side ────────────────128let prev = new Map();129const r8 = v => Math.round(v * 1e8) / 1e8;130function group(map, side) {131 const out = new Map();132 for (const [p, q] of map) {133 const k = r8((side === 'bid' ? Math.floor(p / GROUP + 1e-9) : Math.ceil(p / GROUP - 1e-9)) * GROUP);134 out.set(k, (out.get(k) || 0) + q);135 }136 return [...out.entries()].sort((a, b) => (side === 'bid' ? b[0] - a[0] : a[0] - b[0])).slice(0, LEVELS);137}138function rawSynthetic(side) {139 const out = [];140 for (let i = 0; i < LEVELS; i++) {141 if ((i === 7 || i === 13) && Math.random() < 0.45) continue; // two flickering levels142 out.push([side === 'bid' ? center - i * GROUP : center + GROUP + i * GROUP, base[side][i]]);143 }144 return out;145}146function snapshot() {147 let rb, ra;148 if (source === 'live') {149 if (!live.synced) return null;150 rb = group(live.bids, 'bid'); ra = group(live.asks, 'ask');151 setStatus('LIVE · Binance ' + SYMBOL + ' · ' + live.ups.length + ' upd/s · ' + Math.max(0, Date.now() - live.lastE) + 'ms', '#22c55e');152 } else if (source === 'synthetic') {153 rb = rawSynthetic('bid'); ra = rawSynthetic('ask');154 } else return null;155 if (!rb.length || !ra.length) return null;156 const build = (levels, s) => {157 let tot = 0, notl = 0;158 return levels.map(([price, q]) => {159 const key = s + price;160 tot += q; notl += q * price;161 return { price, qty: q, total: tot, notional: q * price, cumNotional: notl,162 delta: prev.has(key) ? q - prev.get(key) : q, isNew: !prev.has(key) };163 });164 };165 const bids = build(rb, 'bid'), asks = build(ra, 'ask');166 const next = new Map();167 bids.forEach(l => next.set('bid' + l.price, l.qty));168 asks.forEach(l => next.set('ask' + l.price, l.qty));169 prev = next;170 const all = [...bids, ...asks], avg = all.reduce((s, l) => s + l.qty, 0) / all.length;171 all.forEach(l => { l.isWall = l.qty > avg * 3; });172 return { bids, asks, bestBid: bids[0].price, bestAsk: asks[0].price,173 mid: (bids[0].price + asks[0].price) / 2, t: Date.now(), prints };174}175176// ─── Helpers ────────────────────────────────────────────────────────────────177function txt(s, x, y, a = 'left', c = TEXT) { X.fillStyle = c; X.textAlign = a; X.textBaseline = 'middle'; X.fillText(s, x, y); }178function fmt(v, d = 2) { return Math.abs(v) >= 1e4 ? v.toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) : v.toFixed(d); }179function alpha(hex, a) { return hex + Math.round(Math.max(0, Math.min(1, a)) * 255).toString(16).padStart(2, '0'); }180function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }181182// ─── View ───────────────────────────────────────────────────────────────────183// Cumulative depth staircase: bids step down-left from the touch, asks up-right.184// One size axis for both sides, so any asymmetry is a real liquidity imbalance.185function draw(X, W, H, b) {186 const pad = 30, lo = b.bids.at(-1).price, hi = b.asks.at(-1).price;187 const max = Math.max(b.bids.at(-1).total, b.asks.at(-1).total) * 1.1;188 const xp = p => pad + ((p - lo) / (hi - lo)) * (W - pad * 2);189 const yv = v => H - pad - (v / max) * (H - pad * 2);190 X.strokeStyle = GRID;191 for (let i = 0; i <= 4; i++) {192 const y = yv((max / 4) * i); X.beginPath(); X.moveTo(pad, y); X.lineTo(W - pad, y); X.stroke();193 txt(((max / 4) * i).toFixed(0), pad - 4, y, 'right', MUTED);194 }195 for (const [lv, col] of [[b.bids, BID], [b.asks, ASK]]) {196 const pts = [[xp(lv[0].price), yv(0)]];197 let py = yv(0);198 for (const l of lv) { pts.push([xp(l.price), py]); py = yv(l.total); pts.push([xp(l.price), py]); }199 const g = X.createLinearGradient(0, H - pad, 0, pad);200 g.addColorStop(0, alpha(col, 0.45)); g.addColorStop(1, alpha(col, 0.04));201 X.beginPath(); pts.forEach(([x, y], i) => (i ? X.lineTo(x, y) : X.moveTo(x, y)));202 X.lineTo(pts.at(-1)[0], yv(0)); X.closePath(); X.fillStyle = g; X.fill();203 X.beginPath(); pts.forEach(([x, y], i) => (i ? X.lineTo(x, y) : X.moveTo(x, y)));204 X.shadowColor = col; X.shadowBlur = 8; X.strokeStyle = col; X.lineWidth = 1.5; X.stroke(); X.shadowBlur = 0;205 for (const l of lv) if (l.isWall) { // big-order bubbles206 X.beginPath(); X.arc(xp(l.price), yv(l.total), 4 + Math.sqrt(l.qty), 0, 7);207 X.strokeStyle = col; X.lineWidth = 1.2; X.stroke();208 }209 }210 X.setLineDash([3, 3]); X.strokeStyle = MUTED;211 X.beginPath(); X.moveTo(xp(b.mid), pad); X.lineTo(xp(b.mid), H - pad); X.stroke(); X.setLineDash([]);212 txt(fmt(b.mid), xp(b.mid), H - pad / 2, 'center');213}214215// ─── Loop: redraw 4× a second (the live book updates every 100ms) ───────────216let last = 0;217(function loop(now) {218 requestAnimationFrame(loop);219 if (now - last < 250) return;220 last = now; tick();221 const b = snapshot();222 if (!b) return;223 X.clearRect(0, 0, W, H);224 X.font = '11px ui-monospace, Menlo, monospace';225 draw(X, W, H, b);226})(0);227</script>228</body>229</html>See also
Related articles
Related to Depth Chart
- Advanced Order Book
Trading / Order Book
A live market-depth workspace: a stepped price ladder, a depth chart kept in sync with it, sixteen alternative views of the book, a trades bar and one-row…
- Order Book Settings
Trading / Order Book
Every option in the order book settings modal, by tab. Changes apply live and are saved in this browser.
- Price Ladder & Stepped Depth
Trading / Order Book
The classic DOM, with each row's bar showing the cumulative depth out to that level. Together the rows draw the same staircase as the depth chart.
- 3D Depth Canyon
Trading / Order Book / Views
The depth chart extruded through time. Cumulative bid and ask depth form two valley walls, and the mid runs along the valley floor.
- Market Impact
Trading / Order Book / Views
The slippage curve: what a market order of any size would cost right now.
- 3D Book Towers
Trading / Order Book / Views
The book as a city of towers: one tower per price level for the latest fourteen slices of the book, older slices fading back.
- 3D Liquidity Terrain
Trading / Order Book / Views
The recorded book as a lit landscape: time across, price in depth, resting size as height. Walls are mountains, thin books are plains.
- 3D Ridgeline
Trading / Order Book / Views
Every price level as its own ridge of resting size through time, as in the book terrain chart: far ridges behind, near ridges in front, newest on the right.
- 3D Trade Cloud
Trading / Order Book / Views
Every print as a pillar at its time and price, its height the trade size, coloured by the aggressor, over the mid path.