Skip to content

Depth lane ​

A depth lane shows an order book beside the price axis: one horizontal bar per price level, as long as the size resting there, lined up with the candles' prices. Bids and asks get their own colours, and the best bid and ask stand out.

The chart ships no exchange connections. Your app reads the book from whatever feed it uses and hands the levels to the lane.

Depth lanes are opt-in: create the chart with the depthLane() plugin (or builtins()), or call chart.install(depthLane()) later. Without it, chart.addDepthLane throws a not-registered ChartError. The UI shell includes it unless you pick the chart's plugins yourself.

ts
import { createChart, depthLane } from '@chart-on/core';

const chart = createChart(el, { plugins: [depthLane()] });
const lane = chart.addDepthLane({ side: 'right', width: 80 });

// A full snapshot: every level of both sides.
lane.setLevels({
  bids: [{ price: 99.5, size: 120 }, { price: 99.4, size: 80 }],
  asks: [{ price: 99.6, size: 95 }, { price: 99.7, size: 200 }],
});

// Then changes as they arrive: size 0 removes a level, any other size sets it.
lane.update({ bids: [{ price: 99.5, size: 0 }], asks: [{ price: 99.6, size: 110 }] });

The lane sits between the plot and the price axis. The plot narrows to make room, so the axis keeps its place and its width, and the price range stays the same. A hidden lane takes no width.

Options ​

chart.addDepthLane(options) takes these, plus pane (an index or a pane object; the main pane by default). lane.applyOptions(patch) changes them later.

OptionDefaultNotes
visibletrueA hidden lane takes no width
side'right'Which price axis the lane sits beside
width80Lane width in css px
maxLevels200Levels shown per side, nearest the best price first
labelsfalseWrite each bar's size where rows leave room for text
highlightBesttrueDraw the best bid and ask in the solid bid and ask theme colours
bidColor, askColornullBar colours; default to the depthBid and depthAsk theme tokens
repaintInterval100Least time between repaints caused by level changes, in ms

Levels ​

Each level is { price, size }. Levels with a size of 0 or below, or a price or size that isn't a finite number, are left out. The lane keeps every level you send and shows the maxLevels nearest the spread on each side.

MemberNotes
setLevels({ bids, asks })Replace every level. A side left out is emptied
update({ bids?, asks? })Change single levels: size 0 removes one, any other size sets it
clear()Remove every level
levelsThe shown levels: bids from the highest price down, asks from the lowest up
bestBid, bestAskThe first shown level of each side, or null
on('levels:change', listener)Fires after every change with the shown levels; returns an unsubscribe function
remove()Detach the lane; the plot takes its width back

Drawing ​

Bars grow from the axis side toward the plot. Each one is scaled to the largest size in view, so the longest visible bar fills the lane. A bar is as thick as the gap to its neighbouring level allows, up to 14 px. Rows follow the price scale in every mode (log, percent, inverted), and rows outside the pane are skipped.

Level changes repaint only the overlay layer, at most once per repaintInterval, however often your feed calls update. Ten updates a second of 200 levels a side cost well under a millisecond each.

Colours ​

The bars use the depthBid and depthAsk theme tokens (CSS --pc-depth-bid and --pc-depth-ask, see Theming). The dark, light and high-contrast themes set translucent versions of their bid and ask colours, so the candles' grid still shows through. The colour-safe palette changes depthAsk along with ask.

Ladder panel ​

@chart-on/ui has a panel that lists the same levels as a price ladder, asks above bids. It reads only the lane's public handle, so it works with any lane:

ts
import { createDepthLadder } from '@chart-on/ui';

const ladder = createDepthLadder(panelElement, lane, {
  depth: 10,                               // levels per side
  formatPrice: (p) => chart.formatPrice(0, p),
});
ladder.el.hidden = true; // it skips updates while hidden
ladder.destroy();

The panel isn't positioned, so place it with your own layout. It updates at most once per animation frame.

Writing your own lane ​

The depth lane is an ordinary primitive. Any primitive can reserve a lane with lane() and draw in it with drawLane().