Depth Chart (DOM) Development for Crypto Exchanges
DOM (Depth of Market), also known as Level 2 data, visualizes the entire order book, not just the best price. Professional traders read DOM like a book: they see liquidity walls, volume absorption, and spoofing. A well-implemented DOM is a key argument for attracting professionals to an exchange. We develop depth charts tailored to your platform, considering its specifics and performance requirements.
Imagine a trader spots an abnormal volume spike at a level, but the DOM updates with a delay—they enter a position, but the level has already vanished. Lost seconds cost thousands of dollars. Our depth chart updates with less than 5 ms latency, allowing traders to see the real market picture in real time. Order development to give your traders a professional tool.
What challenges arise when developing a depth chart?
The main technical challenge is maintaining data consistency under frequent updates. WebSocket diffs may arrive with delays or out of order. The client must correctly handle gaps and reconnections. The second problem is rendering performance with 50+ updates per second. Using regular React re-renders leads to lag and frame drops. The third is customization: traders want to see tick grouping, cumulative volume, and change highlighting.
How we solve these problems
We combine WebSocket diffs with a local order book copy. For rendering, we use direct DOM manipulation via React refs, achieving up to 10 times higher FPS compared to regular setState. We also apply throttling to 10 fps (the human eye cannot perceive faster). For displaying more than 50 levels, we use virtual scrolling (react-window). Optionally, we render the DOM on Canvas for maximum performance.
Case in point: For one crypto exchange, we implemented a depth chart supporting 100 levels. The load was 30 updates/second with peaks. We applied differential updates and Canvas. The result: latency below 5 ms, stable 60 fps.
DOM Structure
DOM shows two columns: bids (buy) and asks (sell) with aggregated volumes at each price level.
BID ASK
Volume Price Price Volume
0.5 42,100 | 42,101 1.2
1.8 42,095 | 42,102 0.7
3.2 42,090 | 42,105 4.5 ← wall
0.4 42,085 | 42,110 0.9
2.1 42,080 | 42,115 1.1
Wall — an abnormally large volume at a level, often indicating support/resistance. Traders track how these volumes appear, change, and disappear.
Implementing WebSocket Updates
DOM requires minimal latency. Updates via WebSocket diff; client maintains a local copy:
interface DOMState {
bids: Map<string, string>;
asks: Map<string, string>;
sequence: number;
}
class DOMManager {
private state: DOMState = { bids: new Map(), asks: new Map(), sequence: 0 };
private ws: WebSocket;
async initialize(pair: string) {
const snap = await fetch(`/api/v1/markets/${pair}/orderbook?depth=100`).then(r => r.json());
snap.bids.forEach(([p, s]: string[]) => this.state.bids.set(p, s));
snap.asks.forEach(([p, s]: string[]) => this.state.asks.set(p, s));
this.state.sequence = snap.sequence;
this.ws = new WebSocket(`wss://api.exchange.com/ws`);
this.ws.send(JSON.stringify({ op: 'subscribe', channel: `orderbook.${pair}.100` }));
this.ws.onmessage = (e) => this.applyUpdate(JSON.parse(e.data));
}
private applyUpdate(msg: OrderBookDiff) {
if (msg.seq !== this.state.sequence + 1) {
this.reinitialize();
return;
}
msg.bids.forEach(([p, s]: string[]) => {
if (s === '0') this.state.bids.delete(p);
else this.state.bids.set(p, s);
});
msg.asks.forEach(([p, s]: string[]) => {
if (s === '0') this.state.asks.delete(p);
else this.state.asks.set(p, s);
});
this.state.sequence = msg.seq;
this.notifyRenderers();
}
getTopLevels(depth: number = 20) {
const bids = [...this.state.bids.entries()]
.map(([p, s]) => [parseFloat(p), parseFloat(s)] as [number, number])
.sort((a, b) => b[0] - a[0])
.slice(0, depth);
const asks = [...this.state.asks.entries()]
.map(([p, s]) => [parseFloat(p), parseFloat(s)] as [number, number])
.sort((a, b) => a[0] - b[0])
.slice(0, depth);
return { bids, asks };
}
}
Rendering the DOM Component
DOM can update 20–50 times/second. Standard React re-render will cause issues. We use direct DOM manipulation and memoization.
import { useRef, useCallback } from 'react';
const DOMRow = React.memo(({ price, size, total, maxTotal, side, highlight }: RowProps) => {
const rowRef = useRef<HTMLDivElement>(null);
const update = useCallback((newSize: string, newTotal: number) => {
if (!rowRef.current) return;
const sizeEl = rowRef.current.querySelector('.size');
const depthEl = rowRef.current.querySelector('.depth-bar') as HTMLElement;
if (sizeEl) sizeEl.textContent = newSize;
if (depthEl) depthEl.style.width = `${(newTotal / maxTotal) * 100}%`;
}, [maxTotal]);
const flash = useCallback((direction: 'up' | 'down') => {
rowRef.current?.classList.add(`flash-${direction}`);
setTimeout(() => rowRef.current?.classList.remove(`flash-${direction}`), 300);
}, []);
return (
<div ref={rowRef} className={`dom-row ${side}`}>
<div className="depth-bar" style={{ width: `${(total/maxTotal)*100}%` }} />
<span className="price">{formatPrice(price)}</span>
<span className="size">{formatSize(size)}</span>
<span className="total">{formatSize(total)}</span>
</div>
);
});
Visual Features of a Professional DOM
Change Highlighting
On updates, we detect appearance, increase, decrease, and disappearance of volumes. Each change is accompanied by a flash animation (green for increase, red for decrease), allowing the trader to instantly assess dynamics.
Tick Grouping
The user can toggle price level grouping (1, 5, 10, 25, 100). This simplifies order book perception when there are many orders.
function groupByTick(levels: DOMLevel[], tickSize: number): DOMLevel[] {
const grouped = new Map<number, number>();
for (const { price, size } of levels) {
const bucket = Math.floor(price / tickSize) * tickSize;
grouped.set(bucket, (grouped.get(bucket) ?? 0) + size);
}
return [...grouped.entries()]
.map(([price, size]) => ({ price, size }))
.sort((a, b) => b.price - a.price);
}
Cumulative Volume Visualization
Cumulative volume shows the total liquidity up to each level—revealing how deep the order book is.
function addCumulative(levels: DOMLevel[]): DOMLevelWithCum[] {
let cumulative = 0;
return levels.map(level => {
cumulative += level.size;
return { ...level, cumulative };
});
}
Performance
On active pairs, DOM updates 10–50 times/second. Constraints:
- Throttle updates: no more than 10 renders/second for DOM (human eye cannot perceive faster)
- Virtual scrolling: if showing >50 levels — react-window
- Canvas rendering: for maximum performance
Common mistakes when integrating DOM
- Ignoring message sequence — client can lose synchronization. - Missing reconnection handling — WebSocket drops, order book stops updating. - Rendering all levels at once — FPS drops on large order books.What We Do in the Project
- Analysis: Study your backend architecture, current API, latency.
- Design: Choose the stack (React DOM or Canvas), WebSocket message protocol.
- Implementation: Order book aggregation module, DOM component with grouping and highlighting.
- Testing: Load testing with 50,000 updates/second, consistency checks.
- Deployment: Integration, monitoring via Tenderly.
The development cost is calculated individually after analyzing your API. The project budget is discussed at the analysis stage.
What's Included in the Work
- Order book module with WebSocket client (TypeScript)
- DOM component with configurable grouping (1, 5, 10, 25, 100 tick)
- Cumulative volume bar
- Change highlighting (flash animation)
- Customizable color scheme and fonts
- API documentation and integration examples
- Training for your team (2 hours online)
| Feature | Description |
|---|---|
| WebSocket updates | Differential updates, automatic reconnection |
| Change highlighting | Flash animation for volume appearance/disappearance |
| Cumulative volume | Accumulated liquidity up to each level |
| Tick grouping | Configurable (1, 5, 10, 25, 100) |
| Customization | Colors, fonts, sizes |
Rendering Approaches Comparison
| Parameter | Direct DOM manipulation | Canvas | WebGL |
|---|---|---|---|
| FPS (50 levels) | 60 | 120+ | 144+ |
| Implementation complexity | Medium | High | Very high |
| Animation support | Good | Excellent | Excellent |
| Customization flexibility | High | Medium | Low |
Why Order Depth Chart Development from Us?
We have 5+ years of experience in trading interfaces for crypto exchanges. We have implemented depth charts for 10+ projects, including both CEX and DEX. We guarantee stable operation under high loads and full customization for your brand. Investment in a quality depth chart pays off by attracting professional traders.
Get a consultation: contact us—we will quickly assess your project and offer the optimal solution. Estimated timelines: from 2 to 4 weeks depending on complexity. Cost is calculated individually.







