Depth chart
Cumulative bid and ask size around the mid price of an order book, as trading screens show market depth.
A depth chart draws an order book as two curves meeting at the mid price. Left of it, the height at a price is every bid resting at that price or higher: what a sell order would have to take to push the price down that far. Right of it, every ask at that price or lower. A flat curve is a deep market, a steep one a thin market, and a cliff is one large order. The chart takes the levels as an exchange sends them and does the sorting and summing itself; it is drawn by uPlot, so a live feed can hand it a new book on every message.
Import
import '@c2n/components/chart/depth-chart'This chart draws with uPlot, a peer dependency it loads on first draw: npm install uplot.
Usage
Data
bids and asks each take a list of price levels: [price, size] pairs or { "price", "size" } objects, numbers or numeric
strings, in any order. That is the shape Binance, Coinbase and Kraken send, so a REST snapshot can go straight in:
const depth = document.querySelector('c2-depth-chart')
const book = await fetch('https://api.binance.com/api/v3/depth?symbol=BTCUSDT&limit=500').then((response) => response.json())
depth.bids = book.bids // [["67410.50", "0.153"], …]
depth.asks = book.asksLevels with the same price are summed and a size of 0 is dropped, which is how a diff feed deletes a level. The chart
draws snapshots: keep the book from a diff stream in your own code and assign the result. Assigning bids and asks is a
data-only update, so it redraws without rebuilding the chart; set both in the same task and they draw once.
Prices print with as many decimals as the input carries (strings keep their trailing zeros), and sizes the same up to
eight; price-precision and size-precision fix them. base-label and quote-label name the two currencies.
Window and units
range is how far the chart reaches either side of the mid price, as a fraction of it: 0.02 shows ±2%. Each curve
runs flat to the edge of that window when the book is thinner than it. The mid price is the midpoint of the best bid and
best ask; set mid to centre on an index or mark price instead. unit="quote" sums notional (price × size, in the quote
currency) instead of size, and curve="linear" joins the levels directly instead of stepping between them.
Reading it
Hovering a price shows the cumulative size and notional to it, the average price a market order that far would fill at,
and its price impact against the mid price. With mirror (on by default) the chart also marks the same distance on the
other side and adds that side’s depth to the tooltip, so the two can be compared at a glance. Between the two sides the
tooltip shows the best bid, the best ask and the spread.
Picking a price and zooming
A click fires price-select with the price, its side and the readout, which is how an order ticket can take a limit
price from the chart. The plot is one tab stop: the arrow keys walk the levels (Home and End jump to the window edges),
Enter or Space picks the focused level, and Escape leaves it. + and -, or ctrl or ⌘ with the wheel (a trackpad
pinch), zoom between preset windows by setting range and fire range-change with the new price bounds. A plain wheel
still scrolls the page.
depth.addEventListener('price-select', (event) => {
ticket.side = event.detail.side === 'bid' ? 'sell' : 'buy'
ticket.price = event.detail.price
})Styling
The two sides take --c2-chart__bid--color and --c2-chart__ask--color, which fall back to the shared
--c2-chart__positive--color and --c2-chart__negative--color; their fills use --c2-chart__area--opacity. The mid
price line and label, and the mirror marker, have their own variables, listed on the API tab. The legend is off by
default, since the colours and the tooltip name each side; legend="bottom" shows it. annotations work on the price axis,
for marking your own resting orders.