DocumentationDepth of Market Demo

Depth of market

Explore a changing order book with simulated updates every 750 ms. Choose 5, 20 or 200 price levels per side, pause a snapshot, and combine several ticks into one display row. The ladder has its own rows and scrolling, independent of the candlestick chart’s price scale. Its quantities use the published buildRows aggregation helper.

Depth of marketSimulated · updating
Ladder NIFTY ATM CEBest bid 99.95Best ask 100.05Source tick 0.05Snapshot 0
NIFTY ATM CE · candlesticks

Loading depth chart…

ATM call ladder · 0 rows · independent scale
Bid qtyPriceAsk qty

The source tick stays at 0.05. Start with 20 levels and pause the demo, then change the display row size from 0.05 to 0.50. The number of rows falls while the total bid and ask quantities stay the same. The candle chart stays exactly where you left it, including its zoom and price scale. Scroll the ladder to explore the supplied book. Selecting a quantity only displays a message; this demo has no order connection.

Use Chart and ladder to try either requested setup:

SetupCandlestick chartIndependent ladder
Option chart + option ladderIllustrative NIFTY ATM call, normal price scaleThe same option, with 0.05–1.00 display rows
Spot chart + ATM call ladderIllustrative NIFTY spot, around 24,000An option around 100, with its own display grouping

All data in these two scenarios is simulated. The ATM call label represents a fixed illustrative contract; automatic strike selection, expiry selection and contract rollover are not part of this demo.

Keep the chart and ladder independent

Use a separate table or panel for a ladder whose grouping or instrument differs from the chart. buildRows only aggregates a supplied book: it does not read or change a chart’s price axis. The demo previously set the chart’s price range from the ladder row size; that coupling has been removed.

The following example uses the existing 2.1.0 API. Give chart a height and put a <tbody id="book-rows"> in a separate scrollable table with bid, price and ask column headings:

import { createChart } from 'openalgo-charts';
import { buildRows } from 'openalgo-charts/trade';
import type { Bar, MarketDepth } from 'openalgo-charts';
 
const chart = createChart(document.getElementById('chart')!);
const candles = chart.addSeries('candlestick');
const bookRows = document.getElementById('book-rows')!;
const ladderTick = 0.05;
let groupBy = 10; // Independent 0.50 display rows.
let latestDepth: MarketDepth = { bids: [], asks: [], ltp: 0 };
 
function onChartHistory(bars: Bar[]) {
  candles.setData(bars);
  chart.fitContent();
}
 
function onChartBar(bar: Bar) {
  candles.update(bar);
}
 
function onOptionDepth(depth: MarketDepth) {
  latestDepth = depth;
  bookRows.replaceChildren(...buildRows(depth, ladderTick, groupBy).map(row => {
    const tr = document.createElement('tr');
    for (const text of [String(row.bidQty), row.price.toFixed(2), String(row.askQty)]) {
      const td = document.createElement('td');
      td.textContent = text;
      td.style.height = '28px';
      tr.append(td);
    }
    return tr;
  }));
}
 
function setLadderGrouping(ticksPerRow: number) {
  if (!Number.isInteger(ticksPerRow) || ticksPerRow < 1) {
    throw new Error('Grouping must be a positive integer number of source ticks.');
  }
  groupBy = ticksPerRow;
  onOptionDepth(latestDepth);
}
 
// Connect chart history/live bars and option depth to their own feed subscriptions.
// For a spot chart, route spot bars here and the chosen option's book to onOptionDepth.
// On teardown, unsubscribe both feeds, then call chart.destroy().

For an application, keep separate chart and ladder instrument identifiers, source tick sizes, display settings and feed subscriptions. For a spot chart with an ATM option ladder, resolve a concrete option symbol and expiry in your host application. On a contract change, unsubscribe the previous book, clear its rows, subscribe to the new contract and ignore late messages from the old subscription. Show the selected strike/expiry and a feed timestamp beside the ladder. Consider keeping the selected contract fixed during inspection and making ATM reselection explicit, so rows do not suddenly switch instruments.

Source ticks and display grouping

tickSize is the instrument’s actual price increment. groupBy is the number of those ticks combined into a display row. A larger display step can make a deep book easier to read without changing the instrument’s tick size.

Source tickgroupByDisplay row size
0.0510.05
0.0550.25
0.05100.50
0.05201.00

The helper rounds each supplied price to the nearest multiple of tickSize * groupBy, sums quantities independently on each side, and sorts rows from high to low. For example, at a 0.50 display step, bids of 10 at 100.05 and 20 at 100.10 become a bid quantity of 30 in the 100.00 display row.

Bid and ask quantities can appear in the same display row after rounding. That row label is an aggregation bucket, not an executable quote. The best bid and ask above the demo come from the ungrouped supplied book. Empty price levels are not invented or filled in between the supplied levels.

Attach a price-aligned ladder instead

DomLadder is an alternative when the book should align to the same instrument and price scale as its chart. It is available in the trade tier. The following code uses the published 2.1.0 API:

import { createChart } from 'openalgo-charts';
import { DomLadder } from 'openalgo-charts/trade';
import type { MarketDepth } from 'openalgo-charts';
 
const chart = createChart(document.getElementById('chart')!);
const prices = chart.addSeries('line', {
  priceFormat: { type: 'price', minMove: 0.05 },
});
prices.setData([
  { time: 1700000000, value: 99.80 },
  { time: 1700000060, value: 100.20 },
  { time: 1700000120, value: 100.00 },
]);
 
const ladder = new DomLadder({
  tickSize: 0.05,
  groupBy: 1,
  width: 112,
  rowHeight: 16,
  maxRows: 40,
});
chart.addPrimitive(ladder);
 
// A complete snapshot of the available book; values here are simulated.
const depth: MarketDepth = {
  bids: [{ price: 99.95, qty: 120 }, { price: 99.90, qty: 200 }],
  asks: [{ price: 100.05, qty: 90 }, { price: 100.10, qty: 160 }],
  ltp: 100.00,
};
ladder.setDepth(depth);
chart.fitContent();
 
// Call ladder.setDepth(nextSnapshot) on each update from your depth source.
// On teardown, unsubscribe from that source and call chart.destroy().

The container needs an explicit height. A book snapshot is supplied by your application; DomLadder itself does not fetch or subscribe to market data. Feed adapters that support depth expose optional subscribeDepth(...); see Data feeds for the interface.

Change the display step

In 2.1.0, ladder options are supplied to the constructor. Replace the primitive when a grouping control changes, then apply the latest snapshot:

chart.removePrimitive(ladder);
const groupedLadder = new DomLadder({
  tickSize: 0.05,
  groupBy: 10, // 0.50 display rows
  width: 112,
  rowHeight: 16,
  maxRows: 40,
});
chart.addPrimitive(groupedLadder);
groupedLadder.setDepth(depth);
// Route subsequent snapshots to groupedLadder.

The independent demo uses the same public helper without attaching a primitive:

import { buildRows } from 'openalgo-charts/trade';
 
const rows = buildRows(depth, 0.05, 10);
// [{ price, bidQty, askQty }, ...], sorted high to low.

Rendering and data limits

The attached DomLadder primitive is docked inside the chart’s right edge and aligns quantities to its price scale. Its canvas displays rows in the visible price range, capped by maxRows; it does not automatically fit all supplied depth. Zoom or adjust the price scale to inspect a different range. rowHeight controls the drawn row’s height; it does not create an independent price scale. Grouping the primitive does not itself rescale the chart, although adjacent grouped prices can overlap when the chart’s price scale places them too close together.

The independent demo above uses a scrollable table with fixed-height rows, so every supplied aggregated row remains accessible at any chart zoom. Its simulated candle history is bounded to 240 bars. For much deeper books, a host can virtualize this table while continuing to use buildRows for aggregation.

ladder.tier() reports none for an empty book, compact for up to five levels per side, and deep for larger snapshots. Passing empty bid and ask arrays clears the rendered book.

This primitive visualizes supplied depth. It does not calculate synthetic multi-leg spreads, market-fill prices, or execution guarantees. Applications that need synthetic depth must calculate and validate that data before supplying a book, and distinguish their derived prices from actual instrument quotes.