Open it: https://liquiditymapperfast.github.io/ — free, no sign-up, MIT licence. The Guide button inside explains everything in about ten minutes.
A market-map workbench for one coin at a time across many venues: BTC, or any of about 150 coins listed on most of them (see Coins below). It runs entirely in the browser: the page reads the exchanges' public feeds itself, in a Web Worker, so it works from any static host (GitHub Pages included) with no server. A local server remains as an optional source with a week of recorded history. The workbench draws a liquidity heatmap built from recorded order-book depth, a price profile, an aggregated order-book ladder, depth and open-interest panes, and a footprint of executed volume. The look follows the reference screenshots in example_images/ (a local folder, not in git); a right-hand profile column and an OI row complete it.
The default venues are the largest ones whose public order book is also deep, fresh and reliable: Binance, Bybit, OKX, Bitget, Hyperliquid and Deribit perpetuals, plus the spot markets of Binance, Coinbase, Bybit, OKX and Bitget (Research_Findings.md has the measurements and the rule; Gate.io and MEXC are big by reported volume but are left shallow on purpose: Gate.io publishes no book beyond about 15 bp and reports about twice the volume it was seen to trade, and MEXC's deep book is three to five times Binance's near the price on a quarter of its trading, so it would dominate the map; see the 2026-10-12 section there). In the browser the Venues picker offers these eleven and MEXC, which is off until chosen: a page can read only its best 20 levels (its REST sends no CORS header), and its resting book is several times heavier than its trading (Research_Findings.md, 2026-10-08). Each has a connector in src/shared/venues.ts and the choice is kept in this browser's localStorage. With the local server the picker offers 29 (the 20 feed venues plus the spot markets of Binance, Bybit, OKX and Bitget, and Binance US, HitBTC, Poloniex, BitMart and Bitunix, which run on small self-contained connectors) and up to 32 feed venues can be enabled; its Recommended button ticks the default set, and nothing changes until Apply. The choice is saved beside the history database (v2-feed-venues.json for the feed venues, v2-venues.json for the connector venues) and restored when the server restarts, so an existing install keeps its venues until Recommended is applied (a recommended venue added by a later build starts once, in the browser and on the server, since the saved choice records which venues it had seen); on the first run the recommended venues are selected (HLM_DEFAULT_VENUES=all selects every supported venue and starts every connector, HLM_DEFAULT_VENUES=configured keeps the venues set by the *_ENABLED flags and starts no connector venue instead). A fresh start needs about 30 s before market metadata allows the selection to apply. Everything reads public exchange feeds from your own machine (the browser, or the local server); no order is placed and nothing is sent to any service of ours. Optional HyperTracker credentials stay on the server, which is why those layers are listed as upcoming in the browser-only page.
cd HyperLiquidMap
npm install
npm run dev:client # the page alone, reading the exchanges from your browser: http://localhost:5173
npm run build:site # typecheck + the static site in dist/ (HLM_SOURCEMAP=off leaves out source maps)
npm run preview:site # serve dist/ to look at it
npm run dev # build + local server with a week of recorded history on http://127.0.0.1:8787
npm run dev:fixture # the server with offline demo feedsWhich source the page uses. A static host has no server behind it, so the page reads the exchanges itself (src/app/browser-source.ts, the engine in src/shared/engine.ts running in src/app/browser/feeds.worker.ts). Served by the local server, the page finds api/v2/state on its own origin and uses the server instead, with its recorded history. ?source=browser or ?source=server forces one; ?persist=0 stops a browser session from keeping recordings. The browser keeps the last 24 hours of recorded depth, executions, large trades, liquidations and open-interest readings in IndexedDB, written by one tab at a time (a Web Lock), and the heatmap marks where its recording begins: a page can only record while it is open. See docs/deployment.md for publishing.
PORT, HISTORY_DB (SQLite; the depth recorder writes depth-v2.sqlite beside it) and QUOTA_FILE choose where state lives. To try a change without disturbing a running instance, start a second server on another PORT with its own HISTORY_DB. The server answers only requests addressed to localhost or an IP address and, for a browser, only from its own page, so another website you visit cannot drive it (HLM_ALLOWED_HOSTS=name1,name2 adds public names for a deliberate deployment; docs/deployment.md covers going public, GitHub Pages and what can be protected). HLM_SOURCEMAP=off leaves source maps out of a published build.
npm test builds and runs the Node suite, npm run typecheck runs the three strict TypeScript projects, npm run test:wasm runs the Rust kernel tests, and npm run build:wasm regenerates the committed WASM glue (needs wasm32-unknown-unknown and wasm-bindgen-cli 0.2.129).
| Gesture | Effect |
|---|---|
| Wheel on the chart | Zoom the time axis about the live edge while the map follows the market (so it swells and shrinks around now instead of sliding); about the pointer with Alt, or on a map you have moved by hand |
| Wheel or drag on the price scale | Zoom the price axis about the current price while it is on the map (the pointer turns to a resize arrow over the scale; the wheel over the profile column beside it zooms price too); drag up zooms in, down zooms out; Alt + wheel zooms about the pointer |
| Shift + wheel | Zoom the other axis (price on the chart, time on the price scale) |
| Drag / Shift+drag | Pan both axes / time only |
Double-click, R, Home, Recenter |
Return to the live edge |
Go to (map corner, G) |
A date and time on the page's clock: the map is centred on it at its zoom (and fitted to the candles there). It says how far back the depth and the candles go (the chart holds its last 500 candles: at 1m that is about eight hours, so further back there is depth and footprint but no candles); an earlier moment goes to the start of them, a later one to the live edge |
| Replay (Go to → Replay from here) | Plays what was recorded back as if it were live, from the moment chosen, at 1×, 10×, 60× or 300×, with a bar on the map (pause, speed, the moment, Live). The map, its candles (the one under way rebuilt from the recorded price a second), the trades, the panes under it and the flow column show what was known then; what came after is covered. Not shown: the order book (recorded only as the map's minute columns), the profile column and the dot rows; the footprint, bar statistics and open interest of a candle appear once it has ended. Sounds and alerts are silent; a replay that catches up is live again |
| Price line | The dashed line at the last price and its tag on the price axis are in the colour of the candle under way (rising or falling; the order book's price row too), and under the tag a box counts down to the candle's close (UTC-aligned, as the exchanges close them) |
| Layouts (status bar) | Save the panes (order, heights, column widths) and what the chart shows (timeframe, panes, every lamp feature's settings, heatmap, order book) under a name, apply, update or delete one, go back to the default, and export or import them as a file. Not the coin, the venues, the theme, the time zone, the sounds, the VWAP anchors or the Volume profile's zone. Kept in this browser. On a shorter window the panes under the map shrink in proportion so the map keeps at least 30 % of the column (and its own minimum), and get their heights back on a taller one; this also applies when the window is resized |
Keys (Keys in the status bar, or ?, lists them) |
1–7 the timeframes 1m to 1d, R / Home recenter, + / − zoom time, F T L A P K V H switch Footprint, Trades, Liquidations, Absorption, Volume profile, Key levels, VWAP and Highlights on or off, X arms Range (Esc cancels), S screenshot. Not while typing in a field or a window is open, nor with Ctrl, Alt or ⌘ held |
| Right-drag | Right / up zoom the time / price axis in; left / down zoom out |
| Order book: wheel, drag the price column, drag the book, double-click | The wheel zooms the book about the current price (Alt + wheel, or a book you have scrolled off the price, zooms about the pointer) by changing the price step per row (the Group select follows; Auto shows the step it is using); dragging the price column up zooms in and down zooms out, about the same point as the wheel (the price stays put on the middle row while the book is centred; Alt, or a book scrolled off the price, zooms about the pointer); a spin of the wheel makes at most four steps a second, one per event however many clicks it folds in, and a touchpad at most three (a gentle swipe is still a step), so one flick is a step or two and not the whole list; dragging the book moves it; double-click returns to Auto and the mark. Shift + wheel still scrolls Single mode sideways |
| Splitters, pane grips | Resize the order-book column and the lower panes; drag a pane's grip to reorder; sizes persist |
| Hover (profile column or order book) | Mirror: the band from the mark to the pointer and the equally wide band on the other side are framed with border lines in each side's colour and labelled with their cumulative size and distance, the rest dims, and a box says what each band holds and which side has more ("Opposite side has 1.13x more"); move outward to watch the balance change. In Single mode the order book compares within the hovered venue's book. The chart itself shows no comparison. Toggle with Mirror |
| Hover | Shared time cursor across chart, depth, OI and Delta; price and liquidity readout on the chart |
| Flow column: hover, wheel or drag, double-click | A time cursor runs down the column and a box says what the row under the pointer did (the lanes' running delta there, net flow, buys against sells over the ranking window, share of volume; the top row also says when spot and perpetuals move apart); the wheel or a drag scrolls the exchange rows under the pinned top rows when they do not all fit; double-click scrolls back to the top. Map, 5m ... 24h in its header choose the time span, the gear the ranking, row heights and re-ranking |
| Flow, Book (top bar) | Show or hide the two side columns at once; on a phone Flow is a tab under the map. The venue chips show eleven venues and put the rest in a "+N" menu grouped by exchange; the bar along the bottom of a desktop page shows the connection, venues live, the price and its age, since when it records and where the data comes from, with the language and theme buttons |
| Hover (order-book cell or bar) | A box says that venue's size, its share of the level, its rank at the price and its distance from the mark; over the USD column or a plain bar it says the whole level with the venues behind it. The Mirror's box steps aside while the pointer is directly on one (its band stays) and returns anywhere else |
| Hover (footprint row) | The row is boxed and a card gives its price span, what was sold and bought, the delta, which side was heavier and by how much, its share of the candle, whether it is the point of control, and the candle's own volume and delta (a trade bubble or a trapped-traders pop-up takes precedence) |
| Hover (bar-stats cell) | The cell is boxed and its candle's column and its row are tinted; a card says the statistic, the candle, the value, the candle before, where it ranks among the candles in view, whether it was flagged unusual, and what the statistic means |
The toolbar selects the market, timeframe (1m–1d), layer (Liquidity, Liquidation levels, Stop loss, Take profit), heatmap source (aggregated or one venue), Spot / Perp / Both, the venues (the Venues button says how many are on and opens their switches and the feeds dialog), heatmap colour (Size ramp or two-hue Sides style, a legend showing the USD range with one Contrast slider, Auto, Smooth auto / off), the Profile / Depth / OI / Delta / Candles / LT / Mirror / Volume toggles (the page opens with the map, the flow column and the order book only, the panes under the map one switch away; the two columns begin at the golden section of the window, 19 % of it each, and follow it until a column is dragged), Footprint (a lamp button with its own panel), Trades, Liquidations and Absorption (marks on the map), Volume profile, Key levels and VWAP (lines on the map), Highlights and Sound (what is unusual), each a split button: the button switches the feature on or off and its lamp says which, and the small key joined to its right (drawn sliders) opens the feature's settings, which stay as they were while the feature is switched; and the theme menu (hover a theme to preview it, click to keep it). On a desktop the toolbar is a console of framed decks (Market, Time, Heatmap, Panels, Chart, Alerts and the page's own), at most two rows and one from 1600 px wide, and nothing in it moves or changes size as data comes and goes. Each kind of control looks like what it does: pane switches glow while their pane shows, features wear a lamp and a settings key, choices are segments or lists, and the buttons that only open something (Guide, Author, the screenshot) have neither. The ladder offers Compact (the default), Aggregated and Single modes, a grouping step and Levels / Cumulative / both; in Compact, Venue lines (off, top 3, 5 or 8; 5 by default) draws each of the largest venues' running total from the mark on a linear scale of their own (full width is the largest of them in view, with round-value gridlines), ranked by traded volume over the flow column's ranking window, and hovering a line shows it alone with its numbers; and Pull/stack (off, 15 s, 1 min or 5 min): a column after LEVEL USD with how much resting liquidity each row gained (+, stacked) or lost (−, pulled) over that window, grey where the price traded in the window (a fill there looks exactly like a pull). It compares snapshots of every venue's book, counting a venue at a row only where its book reached the whole row both times; a venue that reconnects rebuilds its book and reads as a burst of both, and a coarse venue's banded levels spread their change over the rows they cover. The Liquidation levels, Stop loss and Take profit layers are listed as upcoming (disabled): they need HyperTracker data, which a static page cannot hold a key for. Liquidations (positions the exchanges closed by force) are drawn as diamonds where the market was, from Binance (its latest each second only), Bybit, OKX and Deribit; Hyperliquid, Coinbase and spot markets publish none, and the panel says which of the venues switched on report them. Key levels draws the previous day's, week's and month's high, low and middle and each period's open (PDH, PDL, PDM, DO and the W and M ones), from one market's hourly exchange candles going back about two months; a previous level not yet traded through runs on, dotted, until price reaches it. VWAP draws the volume-weighted average price of the session (each day, week or month, with optional ±1σ/±2σ bands) and up to four anchored VWAPs a coin, placed by clicking the map, from the same market's exchange candles; the whale VWAP averages the recorded large market orders (from a size you choose), buys and sells apart.
A venue that never connects, keeps failing and fails a plain request while others work is shown as unavailable from the visitor's location: a dashed chip, a dismissible banner and a status in the Venues dialog say that a VPN set to another country may enable it.
The chips are the venues that have a book on the map. A venue that was chosen but has none gets a dashed chip of its own, so it never just disappears from the toolbar: a red-tinted one (warning mark) for a venue that is failing or, on a server, left off the map because its book is crossed (a feed fault: "book crossed by 123 bp"), a grey one (ellipsis) for one that has been connecting for more than 20 s. Hovering it says why. A server cannot push these changes, so the page asks it every 10 s.
Help is built in. Every control has a tooltip (src/app/tip.ts), and a ? beside each pane explains it (src/app/help.ts holds the text both use). The Guide button opens a ten-minute tour with a contents list and moving pictures (src/app/guide/; an address like #guide/mirror opens it at a section). Screenshot (or S) freezes the page, lets you drag a region or click a pane (the top bar takes the whole page, every open pane and the flow column included), draw on it, pixelate or blur anything private, then copy or save a PNG (src/app/screenshot/; it draws the page itself, so nothing is asked of the browser). Install appears when the browser can install the page as an app, and Author says who made it.
On a young recording the empty left of the heatmap is filled with the current book in grey (darker grey for bigger walls) so the map reads from the first second; grey is never history, and real colour starts at the dashed line.
The page is arranged for the screen it is on. src/app/device.ts decides once, never from the user agent, and puts two attributes on <html> that src/app/mobile.css reads:
| Attribute | Values | Meaning |
|---|---|---|
data-layout |
phone, phone-landscape, desktop |
How the panes are arranged. A window up to 640 px wide, or a touch screen held upright up to 900 px (a tablet in portrait), is phone; a touch screen held sideways and up to 520 px tall is phone-landscape; everything else, a tablet held sideways included, is desktop |
data-bar |
compact, full |
How the controls are arranged. compact on every phone arrangement and on any touch-first screen |
- Phone (portrait): a two-row top bar (the coin, status and Settings above; timeframes and recentre below; the market is chosen in Settings), the map, one pane at a time under it, and a tab bar (Map, Tape, Flow, Book, Depth, OI, LT, Stats; a tab exists while its switch is on in Settings; a phone opens on the Tape). Tapping the open tab, or Map, gives the chart the whole screen. The handle between the map and the pane resizes it (remembered per tab and orientation; a double tap resets it). Landscape: one top row, the pane beside the map, the tabs as a rail along the edge. The panes are the same elements the desktop arranges as a column, so nothing is rebuilt when the tab changes (
src/app/dock.ts), and a phone never writes the desktop's saved pane sizes. - Tape tab: every trade since the page opened, as one continuous tape that never restarts and fetches nothing, so it opens at once. Each row is a price, sold at market on the left and bought on the right, its winning side shown more strongly the more it won; the order book rests either side, a rail at the right flashes where large orders arrive, and behind it runs the trail of every market order of the last 30 s, minute or 2 minutes, at the price and time it traded (filled dots buys, rings sells; whale-sized orders carry their exchange's mark). On glass the book's rows light up as liquidity stacks and dim as it is pulled, a shock running out along the bar or back in, and large orders send waves through the glass. The header says since when, from how many markets, and how long the page was hidden (a phone in the background gets no trades). Its choices (Look, Trail, Glass, Rows, Pulses) are in Settings.
- Settings (the ⋯ button) is a bottom sheet of labelled rows holding every control that does not fit the bar: the pane switches with Trades, Liquidations and Absorption (split buttons, as on the desktop), the Tape's choices, the heatmap colours and contrast, the market and the venues, Highlights and Sound, the theme, Keep screen on, and Guide, Screenshot, Author and Install. Panels and menus become bottom sheets with a scrim; a dropdown opens the page's own menu (
src/app/touch-select.ts) in the chosen theme instead of the system's list, and nothing makes an iPhone zoom in. - Gestures (
src/app/touch.tsrecognises them from pointer positions; the recogniser is pure and tested): drag pans and carries on after the lift; pinch zooms time with the horizontal separation of the fingers and price with the vertical one, about the midpoint, so what is under each finger stays under it; dragging along an axis zooms it; tap pins the crosshair and its readout above the finger (tap again, or drag, to let go); holding then dragging scrubs it; double-tap recentres. The panes under the map share the time axis, so a drag or pinch there moves the map's time; the order book scrolls, pinches to zoom its grouping, pins Mirror on a tap and resets on a double tap. Holding any control shows its tooltip without pressing it. - Screenshot works by finger (larger handles, buttons that wrap, a Share button where the browser has a share sheet), and records a GIF of the selected area as well as a picture: 1 to 15 s on a slider, never over 15 MB, saved as a file on a desktop (browsers cannot copy a GIF) and shared on a phone. Keep screen on uses the Screen Wake Lock API, because a sleeping screen stops the recording. Install: Chrome and Edge give a prompt; iPhone and iPad get an Add to Home Screen explanation.
- Cost: with the default eight venues the page received about 47 to 74 KB/s of exchange data (165 to 260 MB an hour); the three spot markets added on 2026-10-07 add about 16 % (measured side by side over three minutes: 12.8 KB/s on top of 78.6 KB/s of message text), so about 200 to 300 MB an hour with all eleven; on a 4x CPU slowdown the main thread uses about 13 % of a core with every frame on time (
Research_Findings.md). The heatmap canvas is capped at 2 device pixels per CSS pixel on touch screens. - Testing: the layouts are exercised in Chrome with a phone's size, touch events and device pixel ratio over CDP (
Emulation.setDeviceMetricsOverridewithmobile: true,Input.dispatchTouchEvent). That is Chrome, not Safari: the first real-device check is the published site over HTTPS (a LAN address over plain HTTP loses Web Locks and the clipboard).
Resilience. LiveFeedManager.start() retires the old feeds before its metadata requests, so a failure in between used to leave a running server with no feed at all (Research_Findings.md). A failed start now schedules a recovery with the last configuration that finished, a watchdog restarts a start that hangs or a manager with no feed, and /api/diagnostics reports feedManager.
exchange feeds ─▶ LiveFeedManager ─▶ reducers (src/server/http.mts) ─▶ state
│
┌────────────────────────────────────────────────┤
▼ ▼
v2 recorder (src/server/v2) /api/v2/ws live levels
minute columns on a shared price grid (binary, ~4 Hz) + ticks
SQLite, 7-day window, footprint recorder │
│ │
▼ ▼
/api/v2/columns (binary) ─▶ raster worker + Rust/WASM ─▶ WebGL2 texture ─▶ chart
- Valued levels (
levels.mts): every venue book is converted to USD (contract face value, quote conversion) with the price band each level represents. Grouped books (Hyperliquid's significant figures, Deribit's grouping) name a bid group by its lower edge and an ask group by its upper edge, Hyperliquid at one step per side (its best level's); the top bid and ask groups, which share an interval, are split at the venue's last trade. - Depth recorder (
recorder.mts): every 5 s each fresh book is spread onto a shared 1-2-5 price grid (about 0.02 % of price) and averaged per minute; stale books leave gaps rather than being carried forward. Columns are sparse (only non-empty bins), bid and ask separate, persisted to SQLite and kept for seven days. - Kernels (
crates/hlm-kernels):raster_columnsturns recorded columns into a price-by-time grid (price-conserving, averaged over observed time only);spread_levelsgroups live levels for the profile, ladder and the live edge of the heatmap. The same spreading exists in TypeScript on the server and a test checks the two agree. - Rendering: the worker rasterises a window 20 % larger than the view; the shader samples it with a view-relative transform, so pan and zoom stay smooth on the old texture until the next raster arrives.
- Size style (default): one sequential colour ramp encodes size on a log scale; bid or ask is implied by position relative to the mark. Cells below the window stay background. Sides style uses two hues by side on a linear window.
- Contrast is one slider (-100 to 100, 50 = the baseline): it slides the colour window along the size axis (right shows thinner liquidity, left keeps only the biggest walls, and the far left tones even those down, which is what many venues added together need, because their walls are huge; double-click resets). The window moves by up to three quarters of its own width to the right and 2.25 widths to the left (
src/app/heatmap/window.ts). The window comes from the 15th and 96th percentiles of the non-empty cells; it is taken again on recentering, a market or venue change and a 2x zoom (with Auto off too: a cell holds every price of its row, so cell sizes follow the zoom, and a window kept from another view would saturate everything or nothing), and follows the cells for 15 s after that while the view's history loads (nextBaseline). Then Auto blends it toward the cells every 10 s, so colours do not drift while you pan; Auto off holds it. - LT row (optional, toolbar toggle): LT-Bid and LT-Ask are the USD size on each side of the aggregated enabled venues, weighted by 2^(-d / half-life) with d the distance in basis points from that side's touch (basis points rather than ticks so venues with different tick sizes agree). Options: half-life, min / max bin size, per-level average, lines or imbalance view. It is computed in the raster worker from the recorded minute columns plus the live column, so changing a setting recomputes the whole visible history at once (
src/app/lt.ts, unit-tested).
The Footprint panel's Markets row chooses whose market orders the footprint and the bar statistics count: Aggregated (the default), the markets of the flow column's ALL VENUES lines (every venue switched on, inside the Spot / Perp filter, one spot and one perpetual market each, as the Delta pane adds them) added together on the chart market's candles, or the chart market's alone. Each market's trades stay at its own prices, so the rows are sharpest with the filter on Perp (USD spot markets can sit 10 bp or more from the USDT perpetuals). Open interest in the bar statistics is still the OI pane's one market.
The Footprint panel sets what a row prints (sold × bought, the delta, the total, or nothing) and outlines diagonal imbalances: a row's sells at least the ratio (3 by default) times the buys one row up, or its buys against the sells one row down, outlined in their side's colour, with the ratio in the row's popup. The ratio, the minimum size, whether a row beside an empty one counts, and the stacked rows are shared with the bar statistics, so the strip counts what the cells show; they are read on the rows shown, so zooming changes which rows stand out.
With the Footprint on (the first switch of its panel) and the chart zoomed in, each candle slides to the left of its slot as a solid candle and the rows of executed volume appear to its right (design after the footprint screenshots in example_images/). A row prints sell volume (market sells hitting bids) then buy volume (buys lifting asks) in compact form (13.4M, 407.0k). A bar anchored at the column's left edge appears only where one side is at least 1.15 times the other; it is red for sellers and green for buyers and sized by the larger side against the largest in view. With Aggregated, the bar is split into each venue's part of that side, in the same order on every row and shaded in turn, with the venue's mark where a piece is wide enough (the row's popup names every venue, with what it sold and bought). The heatmap dims behind it and the footprint fades in and out with zoom (thresholds in FOOTPRINT_POLICY). Below the chart the Bar stats strip prints per-candle statistics aligned to the candle slots (see below).
On a closed candle whose wick holds more net aggressive buying (or selling) than any equally tall stretch of the rest of the candle, and that then closed at least one ATR beyond the aggressors' estimated average price, the wick's imbalanced cells are outlined in amber and, for twelve candles, slowly pulse (still, not pulsing, with reduced motion or once price closes back through that price). Hovering them says what was bought and sold in the wick (net, share of the wick's volume, share of the candle's), and what is not known. It describes flow that price then rejected; it does not say who holds what, and the pattern has not been tested as a predictor. It judges only settled candles whose footprint holds the candle's whole volume (checked against the exchange's own figure, so a quiet minute is not a gap and a partly seen candle is), each on its own row step from the volatility before it, decides each candle once (final a minute after it settles), and says in the chart's header, for the candle under the pointer, whether it was checked, had nothing, lacked data or lacks history to compare with. It needs the candles and footprint of the same market. Research_Findings.md has the rule, the firing rates behind its defaults, the ways it can mislead and what a proper study would take.
The strip shows volume, delta and cumulative delta by default. The Stats button in its header opens a panel to choose any of the following, reorder them by dragging a row by its dots (or focus the dots and press the up / down arrow keys), hide them with ×, or apply a preset (Default, All, None); the strip grows one row per statistic, and choice, order and options persist in the browser.
| Group | Statistics |
|---|---|
| Volume | vol, delta, cvd, delta %, buy vol, sell vol |
| Footprint rows | max buy, max sell, poc (point of control, the row with most volume), imb # (diagonal imbalances), stacked (runs of adjacent imbalanced rows) |
| Trades | trades, buys, sells, avg trade, delta retail, delta whales, cvd retail, cvd whales |
| Market | range (high − low), OI change |
Options: cell style (filled and shaded by magnitude on a log scale between the visible 2nd and 99th percentile, or text only coloured by sign), OI change in base coin or USD, imbalance ratio (default 3: a level counts when it is at least that many times the opposite volume one row away), minimum imbalance USD, stacked row count, and the retail/whale limits by trade-size bucket (0, 25k, 50k, 100k, 250k, 500k, 1M, 5M USD; retail up to bucket 2 and whales from bucket 5 by default).
cvd sums the bars loaded for the view. Trade counts and size buckets are recorded by the server from the moment this feature was installed, so earlier bars show – for the Trades group; like all executions they are recorded only while the server runs.
The Delta pane (a toggle in the toolbar, off by default; a dock tab on a phone) shows, for each candle of the chart's timeframe, what was bought at market minus what was sold, in dollars, over the same markets as the flow column's ALL VENUES lines (the exchanges switched on, inside Spot / Perp). Delta bars draw each candle's delta on its own; CVD candles draw the cumulative delta as candles, adding up from the chart's left edge, or starting again each day or each week in the Volume profile's zone, with a dotted line where it starts again. A CVD candle's open and close are exact; its high and low are the running sum sampled sixteen times through the candle, because the extremes of a sum over several exchanges are not kept, so a wick can be a little short. The numbers come from the flow the page holds (a day a second at a time, older minutes where the server keeps them): candles before that have none, and the CVD starts again after a stretch with nothing recorded (ten minutes or more without a trade on any of the markets is taken as that). With Divergences on (the default), two swing highs in a row where the price made the higher high and the CVD the lower one are joined in the sell colour above the highs, and two swing lows where the price made the lower low and the CVD the higher one in the buy colour below the lows, on the CVD candles and on the map (the newest six in view). A swing must stand beyond 3 closed candles each side (2, 5 or 8 in the header), so a divergence appears that many candles after its newer swing and never moves; swings across a restart of the CVD are not compared, and none are drawn while the chart borrows another market's candles. They show where buying or selling faded against the price; they have not been tested as a signal. Hover a candle for its delta and the CVD it closed at (src/app/delta/, src/app/panes/delta-pane.ts, tests/delta.test.mts).
NL/NS (the third choice of Show) estimates who opened and closed positions: each candle's change of open interest of the OI pane's market (with venues added together, of each venue, then summed), given to the side that traded at market there (that market's taker delta; the way its price went where its flow is not recorded, which the header says). Up with buyers: new longs; up with sellers: new shorts; down with sellers: longs closed; down with buyers: shorts closed. Bars are solid where positions were opened and hollow where closed, in coins; the line is their running sum (the net positioning of the side that traded at market), restarting where the CVD does. Every position has a long and a short holder, so it is an estimate, for that one market, and not tested as a signal. A market's change is measured between its own readings and given to its takers over the whole stretch since the reading before (an hourly reading is marked coarse in the popup); each venue is classified before they are added, so new longs on one venue and new shorts on another read "Both ways" with the four parts listed, not "no change"; Deribit's open interest, counted in dollars, is put in coins at one price (the newest close), so a price move alone opens and closes nothing. The header's As select (with NL/NS shown) switches between Net (one series) and Longs and shorts: the running sums of the longs and of the shorts as two lines.
Chosen markets. The footprint's Count (in its panel) and the OI pane's Venues can also be Chosen markets: a list of exchanges and kinds (Binance perpetual, Coinbase spot) ticked whatever the venue chips and the Spot / Perp filter say, the footprint's and the open interest's apart (spot markets have no open interest). NL/NS and the Long/Short pane count the OI list, and the Delta header opens it while it shows NL/NS. A choice is an exchange, not a market, so a layout made on BTC picks ETH's markets on ETH; one the coin is not listed on counts nothing, and an empty list counts nothing.
Long/Short (L/S in the pane toggles, off by default) shows the share of each exchange's accounts that is net long, as the exchange publishes it, one line per exchange with its mark where the line ends: Binance (all accounts by default; its top traders by accounts or by positions in the header), Bybit, OKX and Bitget (its "active accounts", the newest 30 periods only), for the OI pane's markets. These count accounts, not coins (in coins the two sides are always equal), so 1.25 is 1.25 long accounts for each short one; the exchanges count different people, so the lines are never added together. OKX sends no CORS header, so the website shows it as read by the server only. Hyperliquid publishes none. The readings are read from the exchanges when the pane is shown and kept in memory (the server's history store holds none of them), five-minute periods at the finest.
Measure strip (a split button on the status bar, beside Waves, and in the Chart section of a phone's Settings; off by default) draws one signed bar per candle along the top of the map, under the legend and the corner buttons: the columns beside the map show measures by price, the strip shows them by time, as the volume bars along the bottom do. Its button switches it and its key chooses the measure. Delta is the Delta pane's number for each candle (bought minus sold at market, in dollars, over the flow column's ALL VENUES markets, with the same rule that ten minutes or more with no trade on any of them is not recorded), worked out by the same code (src/app/delta/flows.ts) whether the Delta pane shows or not. OI change is each candle's change of open interest of the OI pane's markets, in coins, from the one per-venue change the OI pane, the bar statistics and the sounds use (oiChangeByTime); up means positions were opened and down that they were closed, never who bought, and the caption names the markets it counts (which can differ from Delta's) and how often open interest is read when that is coarser than a candle (most venues' history is five-minute, so on a one-minute chart the change lands in the candle of the new reading and the candles between have none). Above zero is drawn in the bid colour and below in the ask colour, every bar on one scale, the largest size in view either way (no percentile clipping); a candle with no data has no bar and a dotted zero line, and pointing at it says why (nothing recorded, a long quiet stretch taken as not recorded, no new reading, before the readings held): it is never a zero. With Highlights on, what stands out (the rule below, on the size of the value) is drawn at full strength. Large orders, liquidations and absorption are left out on purpose: the large-order sums come only in four coin-scaled bands, the liquidation history is capped at the largest 5,000, so candle totals would be incomplete. The values are kept until the markets, the timeframe, the flow or open interest held, or the whole candles in view change, and the candle under way is worked out again at most once a second; nothing is worked out or asked for while the strip is off (src/app/measures/, tests/measures.test.mts).
One rule decides it everywhere: a value stands out when it exceeds the mean plus k standard deviations of the bars before it (default k = 2, baseline 72 bars; the bar itself never raises its own threshold, and nothing is flagged until a dozen bars exist). The Highlights panel sets k, the baseline and a master switch for every pane at once.
- Measure strip (along the top of the chart) draws a candle whose delta or change of open interest stands out at full strength with a cap beyond its bar, the rest dimmed, judged on the size of the value so a large outflow stands out as a large inflow does.
- Volume is a histogram along the bottom of the chart (toggle Volume): ordinary bars are dim, unusual ones are at full strength with a cap, a dashed line shows the threshold, a faint column marks the bar's slot up the whole chart, and the candle gets a glowing ring. The header line says how many sigma above its baseline the hovered candle is.
- Depth puts asks above the centre line and bids below it, as on the chart and in the order book, and draws the side with more liquidity brighter and the other dimmer, in proportion to the imbalance, and the readout names it ("asks +3.2%").
- Order book shows which side dominates the same way: at each row the bids and asks within that distance of the mark are compared (cumulative, the same comparison as the depth map), and the row's bars are drawn stronger when its side holds more and fainter when the other does, in proportion to the imbalance. A slim bar under the header shows bids against asks within the visible range as shares of their sum. Both follow the Highlights switch, and the zoom (wheel, price column) changes the distance they are read over.
- Open interest adds up the perpetuals of every venue switched on by default (the pane's Venues select: All perpetuals or The chart market): Binance, Bybit, OKX, Bitget, Deribit and Hyperliquid, in coins. Binance and Bybit have days of history and OKX a day or two (the server reads it), the others build up while recorded; a candle's change is each venue's own change added up, so a venue whose readings start is not counted as positions opened, and NL/NS gives each venue's change to its own takers before adding them. It is drawn as a level: a step line carried forward from each sample, a dashed continuation to the newest candle, dots at real samples, and below it the change between samples with unusually large changes at full strength. If the market has no OI history (only Binance and Hyperliquid report it) a reference perp's series stands in, labelled. It refreshes every 20 s and says how old the newest sample is.
- Trades (the Trades button opens their panel; its lamp is lit while they show) puts bubbles on the chart at the time and price of each large trade (at least $25k), sized by notional, coloured by the side that took liquidity, only the biggest few hundred in view, with a glow for whale-size trades. They reach as far back as the recording (a day in the browser, a week on the server): a history answer carries the largest 5,000 orders of its window (BTC records 3,000 to 5,000 an hour), and a view zoomed well into a window whose answer was cut asks again for its smaller orders.
The column left of the map is the cumulative volume delta: for every exchange, what was bought at market minus what was sold at market, added up over time, blue for spot and amber for perpetual whichever way the money moved. The heatmap and the book are passive liquidity (what traders placed and may pull); this is active liquidity (what they did).
- Rows. An aggregate row on top (every spot lane added, every perpetual lane added), a price strip under it, then one row per exchange, ranked by gross volume over the ranking window (15 min, 1 h or 24 h) and re-ranked every few minutes so rows do not jump (or never, with Re-rank off). The ranking is strict about the count: an exchange with no volume does not fill a place, and any exchanges can be pinned (Keep listed in the gear: one toggle per exchange; they take the last places when they are not among the biggest). With the Spot or Perp filter on, the aggregate row says which kind it is and how many exchanges the filter leaves out (spot is only Coinbase and Binance, so "two of my eight" is the filter at work). A venue and its spot twin (
binance,binancespot) are one exchange. An exchange with no trade in the last five completed minutes is flagged!5m. - Why an exchange is not a row. Above the rows a notice names every exchange that has recorded flow and is not shown, and why: the Spot / Perp filter leaves it out (the button says Show both), its chip is switched off (Turn on), the number of rows asked for is smaller than the number of exchanges (Show all), it has not traded in the ranking window, or it has no order book on the map yet. Changing the filter or a chip makes the list again at once instead of waiting for the next re-rank.
- Dot rows above the exchanges. A strip of rows of small round lamps in a sunken well, an element of its own above the column's canvas (so the rows below it keep their layout): taker flow over the last 1, 5, 15 and 60 minutes, and trade size over the ranking window, one row per band of sizes (by default under $25K, up to $100K, up to $500K, and above). On every row buys fill from the left in the buy colour and sells from the right in the sell colour, the side that is ahead lit and the other dim, two grey lamps marking an even split; the number is the buy share (flow rows) or the band's share of the volume (size rows, with ten more lamps). The flow rows add the same instruments as the aggregate row, so the one-hour row says what ALL VENUES says; the sizes come from the footprint recorder's per-minute statistics, the only place they are kept (the flow has none). A row whose window was recorded for under nine in ten of its minutes wears an amber ring in place of its lamp, its dots are dimmed and the heading says
47/60 MIN; the hover box always says how many minutes were recorded. The leading lamp of a size row lights for 0.6 s when a trade of that size prints (not with reduced motion, and the gear can switch it off). The size bands start at the Bar stats' limits (retail up to, whales from, the smallest trades on their own row) and are set in the gear under Dot rows. A server without/api/v2/sizes(an older one: restart it) leaves the size rows saying so while the flow rows go on. Not shown on a phone. - Labels are on the left, in their own gutter, so nothing is laid over the ends of the lines on the right (the usual fault of CVD displays):
#rank EXCHANGE, each lane's net flow over the ranking window with a dot in the lane's colour, then the exchange's share of all volume. Every line has its own vertical scale (compare shapes, not heights) and starts at zero at the left edge (a setting draws the running total since the history began instead). - Heights follow the golden ratio (each rank is 1/φ of the one above, never below a readable minimum), the exchange's volume share, or are equal; the top row is always the tallest, and what does not fit scrolls (wheel or drag) under the pinned aggregate and price rows. Hover a row for a box: the lanes' running delta at the pointer, their net flow and buys against sells over the ranking window, the share. Hover the aggregate: spot and perpetuals moving apart (both sides over $2M and opposite) is said in words.
- Time span: Map (the map's own span up to now, starting where the flow starts when that is later, with "Flow recorded since 14:10" at the left: a map of days over a few minutes of recording would otherwise squeeze them into a column or two) or the last 5 min, 15 min, 1 h, 4 h or 24 h (which keep their length and say when recording began). All of them are drawn from one series at one-second resolution.
- The price strip is a price a second, the volume-weighted price of each second of the market on screen, recorded with the same trades as the flow lines (candle closes and marks fill in before the recording began, or for a market the recorder does not hold).
- A shared time cursor. Pointing at the column draws a vertical line at the same moment on the map, the depth, open-interest and tracker panes (and their readouts follow); pointing at any of them draws it on the column.
- Colours. Spot is blue and perpetual amber in every theme, on purpose: a hue says which market, never buying or selling, so it does not follow a theme's buy and sell colours. The one exception is a theme whose own buy and sell colours are near blue or amber (Colour-blind safe uses Okabe-Ito blue and orange), which gets reddish purple and bluish green so the lines do not read as direction.
- Toggles. Flow and Book in the top bar show or hide the two side columns at once (nothing is rebuilt); on a phone Flow is a dock tab. Settings live behind the gear in the column's header.
How it is built:
| Piece | Where |
|---|---|
Per-second taker buy/sell per instrument (FlowRecorder), minutes of 60 + 60 + 60 Float32 (buys, sells, prices) in SQLite (server, 7 days) or IndexedDB (browser, 24 h), 36 h in memory |
src/shared/flow.ts, src/server/v2/flow.mts, src/app/browser/idb.ts |
The page's per-instrument ring of running sums (FlowSeries: delta and gross as Float64 prefix sums over 2^17 s, so any window's delta, volume and share are two reads; a late or repeated second adds its difference to the later sums) |
src/shared/flow.ts, src/app/flow-book.ts |
Wire: /api/v2/flow?inst=a,b&from&to (binary: JSON header + Float32 buy/sell seconds), {t:'flow', items} on the socket once a second (absolute totals of the seconds that changed), the same two in the browser worker's protocol |
src/server/v2/api.mts, src/app/net.ts, src/app/browser/ |
The sizes of the trades: FootprintRecorder.sizes adds the per-minute statistics of the counted instruments over the last N minutes for up to six N in one pass (seen: minutes with rows, stats: minutes with statistics); /api/v2/sizes?inst=a,b&minutes=1,5,60 (JSON, one request, the minutes counted on the server's clock), the same in the browser worker's protocol |
src/shared/footprint.ts, src/server/v2/api.mts, src/app/net.ts, src/app/browser/ (tests/v2-sizes.test.mts) |
| Grouping, ranking with a held order, row heights, hit tests, bursts, the words | src/app/cvd/ (pure; tests/app-cvd*.test.mts) |
| The dot rows: bands, rows, the coverage rule, the dots, the words | src/app/cvd/strip.ts (pure; tests/app-cvd-strip.test.mts) |
| The canvas, pointer and controls; the dot rows' canvas, questions and blink | src/app/panes/cvd-pane.ts, src/app/panes/cvd-strip.ts |
Trades reach the recorder from every connector (the browser source runs the exchanges itself, so every venue it runs counts) and, on the server, from the feed manager (Hyperliquid, Binance) and the extra venues (ExtraVenues.onTrade). The manager has no trade feed for Bybit, OKX, Bitget, Deribit or Coinbase, so a page served by the server shows those as depth only: open it with ?source=browser to count every venue. Why the scan is JavaScript and not WebAssembly: Research_Findings.md (measured).
The coin button lists the coins on at least nine of the eleven browser markets (147 on 2026-10-07, BTC included; 62 are on all eleven), largest 24 h volume first, with a search. Choosing one loads the page again with ?coin=ETH: new sockets, recorders and caches, so nothing of the last coin is carried over. The address and this browser remember it. docs/coins.md has the measurements and the decisions.
- The list (
coins.json, built bysrc/shared/coins.tsfrom each market's own public list): names matched after units are converted (1000PEPEUSDT, Hyperliquid'skPEPEand Bybit'sSHIB1000USDTare in thousands; OKX swaps by their contract value; Deribit's BTC and ETH perpetuals are sized in USD, its other coins are USDC perpetuals sized in coins), and a market kept only when its price agrees with the others' within 2 % (LIT on Binance spot is another token). A coin enters on nine markets and leaves below eight. The site's copy is rebuilt daily by the Pages workflow; a market whose list cannot be read there keeps its listings from the previous list (GitHub's runners are in the US, which Binance and Bybit refuse: measured on the first run, so their listings come from the repository copy). The copy in the repository is rebuilt withnpm run coins -- --out src/app/public/coins.json. The local server builds its own each day into its data folder and serves it at/coins.json. - The server records BTC only. A page it serves on another coin reads the exchanges itself, as the public site does.
- Sizes follow the coin. The recorders' floors (trades and absorption groups from $25K, map cells under $10K dropped from large columns, the size buckets) and every size setting (trade bubbles, liquidations, absorption, sounds and alerts, the Liquidity Tracker filter) are BTC's times a scale from the coin's 24 h volume against BTC's: ×1 (ETH), ×0.4 (SOL), ×0.1 (DOGE, PEPE), ×0.04 or ×0.01, following the square root of the ratio. Settings are kept as BTC's and shown scaled, so one setting serves every coin. The order book's Group steps move by whole decades to the coin's price, and prices are written with up to ten decimals.
- Recordings are kept per coin, BTC's in the IndexedDB database it always had and each other coin's in its own, recorded by one tab per coin; a coin not opened for a day has its database removed.
- A market that does not list the coin shows as such in the Venues picker and cannot be ticked; the venue choice stays one for every coin.
The Spot / Perp / Both control filters the enabled venues for the heatmap, profile, aggregated ladder, depth, LT and mirror. It never switches a venue on or off (chips that fall outside the filter are dimmed; a click on a dimmed chip shows that venue, which means the filter goes back to Both and the venue is switched on), it says so when it selects nothing, and executions (footprint, bubbles) are not filtered. Spot books show the walls that stand out far from the price; Binance spot (5000-level snapshot plus the diff stream) and Coinbase (full book) are the deep ones.
Zoomed out to a few percent either side of the price a heatmap has two problems: the venues' books have to reach that far, and thin walls become a pixel or less. Both are handled.
- Reach. Every book is merged into buckets that widen with distance from the mark (about 0.4 % of it): fine at the touch, then up to the recorder's grid step (always a whole-number divisor of it, so a bucket never straddles a recorded bin), then power-of-two multiples out to 16 steps, recorded in the bin of the bucket's farthest member. Levels beyond 50 % of the mark are dropped. This replaced a nearest-first cap of 2000 levels per side that stopped Coinbase's asks at +2.5 % while its sparse bids reached -20 %.
BOOK_LEVEL_LIMIT(default 10,000) is the raw cap before merging. - Visibility. Once price rows are under 15 px tall the raster is smoothed vertically with a Gaussian about 5 px wide (three box passes in the wasm kernel), so a thin wall becomes a visible band. Toolbar: Smooth auto / off.
- Colour window is taken from the visible raster only (15th and 96th percentiles of non-empty cells), so far walls are judged against the far book, not the dense touch.
The recorder keeps distance-adaptive buckets and drops bins under $10k, which holds a week of history for all venues to about 0.5 GB (68 MB a day; most venues sit at a few thousand bytes per minute, the deepest at about 10 KB).
The server keeps every venue trade of $25k or more (deduplicated, SQLite, one week), broadcasts them as they arrive and serves them by time window. Sounds have their own Sound panel, not the bar stats: a sound is a live event, bar stats are retrospective aggregates.
- A sweep that fills on several venues within a quarter of a second is one event. Its size selects a tier (Signal $50k, Surge $150k, Whale $400k, Leviathan $1.5M; thresholds are editable and stay ascending above $25k) and it sounds only if that tier is on (Whale and Leviathan by default). Buys rise in pitch and sells fall; a higher tier adds notes (one to four) and loudness.
- One AudioContext with a compressor, at most eight voices (extra notes are dropped, not queued), scheduled on the audio clock. Browsers keep audio locked until a click or key press, so the button shows an amber dot until then. Trades that arrive more than 2.5 s late (a throttled background tab catching up) are ignored.
- Options: volume, which venues count (all / spot / perp), a Test button per tier and side, and an optional chime when a candle closes on unusual volume. There is no liquidation sound: the feeds carry no real liquidation events.
- The decisions are recorded in
window.__hlm.sounds.logso they can be tested without listening.
Per panel. Beyond trades, each pane can make one sound about something rare in it (Sounds panel, Per panel; src/app/sound/alerts.ts, the detectors in alert-rules.ts and cvd/burst.ts, all off by default): a burst of taker flow (an exchange's delta over 10 s is more than the chosen number of standard deviations from its own 30-minute norm, and at least a minimum; also marked on the flow column), a candle closing with a big net delta across the enabled venues, a wall (a level of at least a size, summed across venues in a 2 bp bin within 1 % of the price) appearing or being pulled without the price having come to it, the balance tipping (bids against asks within 1 %, with hysteresis), and an unusual open-interest change at a candle close (Highlights' rule). Nothing sounds from history, from the first ten seconds of a book, or within its cool-down, and no more than four sounds play in ten seconds. Buying rises and selling falls; each panel has its own register and wave. The decisions are in window.__hlm.alerts.log.
Twenty-three themes in four frames. Terminal, Amber and Blue Mode are Text mode: a monospaced face, double rules and inverse video. Classic 95 is the Bevel frame: two-pixel bevels, etched group boxes, check boxes for the lamps, latched buttons with a dithered face and a navy title bar. Latte, Parchment, Newsprint and Blush are Paper, softer and rounder. Light (the default), Colour-blind safe, light and High contrast, light, and the dark Dark, Darker, Midnight, Mocha, Slate, Velvet, Ember, Heather, Deep Teal, Charcoal, Colour-blind safe and High contrast are the Editor frame. Colour-blind safe uses the Okabe-Ito blue and orange. A test enforces text contrast of 7:1, muted text 4.5:1 and 3:1 for accents and the buy / sell colours on both background and panel. Saved choices from earlier ids still resolve.
Each theme's frame (html[data-frame], src/app/chrome.css, loaded last) sets the window language: square corners and one crisp outline in the Editor, softer corners in Paper, the classic desktop's pixel-drawn bevels in Bevel, double rules in Text mode. Glyphs are drawn rather than typed (the close crosses, the ? marks, the language globe, the settings keys), so they sit in the middle of their box whatever the font. The tones are derived per theme in src/app/theme.ts (chromeFor, tested for edge visibility and text legibility; words on a tone, such as a warning or a caution, reach 4.5:1). Soft shadows, pill shapes and blur are not used: they cost the most to paint over canvases that repaint all the time.
Waves (the status bar's Waves lamp, a switch in a phone's Settings; the Tape's own Glass choice is a different control) turns the bars and panels around the chart to glass, in any theme, and lets large orders light it and send waves across the screen. A large order lights the glass nearest where it traded, and the light gathers there, runs out as a ripple whose colours split and, for the largest, leaves a ghost mirrored through the middle of the screen, as a pair of binoculars throws from a bright light. It also starts a wave in a simulated wave field that runs across the whole screen, slows in the glass, is partly turned back by the splitters and fades at the edges; large changes in the flow column start waves of their own. Physics carries the information: where the eye is drawn is where the market moved, and how far a wave reaches says how large the order was. It is drawn in WebGL behind the chrome, never over the data, and reduced motion keeps the glass and leaves the light out.
English, Spanish, German, French, Portuguese, Italian, Russian, Ukrainian, Turkish, Chinese, Japanese and Korean, chosen from the browser's preference, ?lang=xx, or the Language button. See docs/languages.md for how it works, why the browser's own translation is not enough, what is not translated and how to add one. The packs were written by an AI assistant and have not been reviewed by native speakers.
- Venue books differ in how far from the mark they reach, which is what makes a venue look thin on the map, not the level count. Measured on BTC (October 2026, after distance merging): Coinbase about 35 % each side, Binance spot about 41 %, HitBTC, Phemex and Binance US up to the 50 % limit, Bitget about 75 bp, Bybit about 47 bp (it subscribes to
orderbook.1000), Hyperliquid about 470 bp in 20 aggregated bands, OKX about 85 bp on the perpetual and 115 bp on spot (its 400 WebSocket levels near the price, thenbooks-fullbeyond them, read every 15 s:shared/far-book.ts), Aster about 180-260 bp on the server the same way (REST depth 1 000), Deribit's whole book to the 50 % limit, Kraken and Bitfinex deeper since they take depth 1 000 (about 400 / 300 bp) andlen250, and the venues that only publish a shallow book (MEXC, Crypto.com, KuCoin 1-6 bp; Gate.io 5-6 bp, Poloniex 13 bp, HTX about 20 bp; Bitstamp about 30-70 bp) stop where they stop whatever the cap, because merging cannot recover depth a venue never sends. The ladder shades rows beyond a venue's reach in Single mode, and each venue chip's tooltip states its reach. BitMEX currently serves empty books to this connection. - The Liquidity Tracker has no per-order data: its size filter applies to a venue's aggregated size at one price bin.
- The server is single-threaded: with 24 venues it needs roughly half a core (see
Research_Findings.md). If it saturates, heavy API calls time out and executions are missed, which shows up as gaps in the footprint. Feed sockets run without per-message compression for that reason, so they use more bandwidth. - Depth history starts when the recorder first runs; nothing is back-filled. Footprint likewise records only trades seen while the server is running.
- Open interest history comes from the server's own samples plus public history where the venue offers it; the public history is five-minute resolution, the server's own samples are one-minute.
- The history store (candles and open interest) stops writing while its file is over its 224 MiB budget; the OI endpoint then serves live samples newer than the stored bars, but history stays short until the file is pruned.
- Far-liquidity history builds up over time: a zoomed-out spot view is only as deep as the hours recorded since the full-depth books were recorded.
The previous implementation spent most of its CPU on memory accounting (client frame time 133 ms under interaction, server 90 % busy in byte estimation, history responses rejected by a 761 MB estimate). The measurements, method and what replaced it are in Research_Findings.md. Agent and contributor rules, including the Rust/WASM policy, are in AGENTS.md.
MIT, see LICENSE. Free to use, change and share.