Разработка визуализации DOM (Depth of Market)
DOM (Depth of Market), также известный как Level 2 данные — это визуализация всего стакана ордеров, а не только лучшей цены. Профессиональные трейдеры читают DOM как книгу: видят стены ликвидности, поглощение объёмов, спуфинг. Хорошая реализация DOM — один из ключевых аргументов для привлечения профессионалов на биржу. Мы разрабатываем depth chart под ключ, учитывая специфику вашей платформы и требования по производительности.
Представьте: трейдер видит аномальный всплеск объёма на уровне, но DOM обновляется с задержкой — он входит в позицию, а уровень уже исчез. Потеря секунд стоит тысячи долларов. Наш depth chart обновляется с latency менее 5 мс, что позволяет трейдерам видеть реальную картину рынка в реальном времени. Закажите разработку, чтобы ваши трейдеры получили профессиональный инструмент.
Какие сложности возникают при разработке depth chart?
Главная техническая сложность — поддержание консистентности данных при частых обновлениях. WebSocket diff могут приходить с задержками или в неправильном порядке. Клиент должен корректно обрабатывать пропуски и переподключаться. Вторая проблема — производительность рендеринга при 50+ обновлениях в секунду. Использование обычного React re-render приводит к лагам и потере кадров. Третья — кастомизация: трейдеры хотят видеть группировку тиков, cumulative volume и подсветку изменений.
Как мы решаем эти задачи
Мы комбинируем WebSocket diff с локальной копией ордербука. Для рендеринга используем прямой DOM manipulation через React refs, что даёт до 10 раз выше FPS по сравнению с обычным setState. Также применяем throttling до 10 fps (человеческий глаз не различает быстрее). Для отображения более 50 уровней используем virtual scrolling (react-window). Опционально рендерим DOM в Canvas для максимальной производительности.
Кейс из практики: Для одной криптобиржи мы реализовали depth chart с поддержкой 100 уровней. Нагрузка составляла 30 обновлений/сек при пиках. Мы применили дифференциальные обновления и Canvas. В результате latency менее 5 мс, кадры стабильно 60 fps.
Структура DOM
DOM показывает два столбца: bid (покупки) и ask (продажи) с агрегированными объёмами на каждом ценовом уровне.
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
Стена — аномально большой объём на уровне, часто указывает на зону поддержки/сопротивления. Трейдеры отслеживают, как эти объёмы появляются, меняются и исчезают.
Реализация WebSocket обновлений
DOM требует минимальной задержки. Обновления через WebSocket diff, клиент хранит локальную копию:
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 };
}
}
Рендеринг DOM компонента
DOM может обновляться 20–50 раз/сек. Обычный React re-render даст проблемы. Используем прямой DOM manipulation и мемоизацию.
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>
);
});
Визуальные особенности профессионального DOM
Подсветка изменений
При обновлении детектим появление, увеличение, уменьшение и исчезновение объёмов. Каждое изменение сопровождается flash-анимацией (зелёный для увеличения, красный для уменьшения), что позволяет трейдеру мгновенно оценить динамику.
Тик группировка
Пользователь может переключать группировку ценовых уровней (1, 5, 10, 25, 100). Это упрощает восприятие стакана при большом количестве ордеров.
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
Накопительный объём показывает суммарную ликвидность до каждого уровня — видно, насколько глубокий стакан.
function addCumulative(levels: DOMLevel[]): DOMLevelWithCum[] {
let cumulative = 0;
return levels.map(level => {
cumulative += level.size;
return { ...level, cumulative };
});
}
Производительность
На активных парах DOM обновляется 10–50 раз/сек. Ограничения:
- Throttle updates: не более 10 рендеров/сек для DOM (человеческий глаз не воспринимает быстрее)
- Virtual scrolling: если показываем >50 уровней — react-window
- Canvas rendering: для максимальной производительности
Типичные ошибки при интеграции DOM
- Игнорирование последовательности сообщений — клиент может потерять синхронизацию. - Отсутствие обработки переподключения — WebSocket упадёт, ордербук перестанет обновляться. - Рендеринг всех уровней сразу — падение FPS на больших стаканах.Процесс работы
- Аналитика: изучаем архитектуру вашего бэкенда, текущие API, latency.
- Проектирование: выбираем стек (React DOM или Canvas), протокол WebSocket сообщений.
- Реализация: модуль агрегации ордербука, компонент DOM с группировкой и подсветкой.
- Тестирование: нагрузка 50 000 обновлений/сек, проверка консистентности.
- Деплой: интеграция, мониторинг через Tenderly.
Стоимость разработки рассчитывается индивидуально после анализа вашего API. Бюджет проекта обсуждается на этапе аналитики.
Что входит в работу
- Модуль ордербука с WebSocket-клиентом (TypeScript)
- Компонент DOM с настраиваемой группировкой (1, 5, 10, 25, 100 tick)
- Cumulative volume bar
- Подсветка изменений (flash-анимация)
- Кастомизация цветовой схемы и шрифтов
- Документация API и примеры интеграции
- Обучение вашей команды (2 часа онлайн)
| Функция | Описание |
|---|---|
| WebSocket обновления | Дифференциальные обновления, автоматическое переподключение |
| Подсветка изменений | Flash-анимация для появления/исчезновения объёмов |
| Кумулятивный объём | Накопительная ликвидность до каждого уровня |
| Тик группировка | Настраиваемая (1, 5, 10, 25, 100) |
| Кастомизация | Цвета, шрифты, размеры |
Сравнение подходов к рендерингу
| Параметр | Direct DOM manipulation | Canvas | WebGL |
|---|---|---|---|
| FPS (50 уровней) | 60 | 120+ | 144+ |
| Сложность реализации | Средняя | Высокая | Очень высокая |
| Поддержка анимаций | Хорошая | Отличная | Отличная |
| Возможность кастомизации | Высокая | Средняя | Низкая |
Почему стоит заказать разработку depth chart у нас?
У нас 5+ лет опыта в торговых интерфейсах для криптобирж. Мы реализовали depth chart для 10+ проектов, включая CEX и DEX. Гарантируем стабильную работу при высоких нагрузках и полную кастомизацию под ваш бренд. Инвестиции в качественный depth chart окупаются за счёт привлечения профессиональных трейдеров.
Получите консультацию: свяжитесь с нами — мы оперативно оценим ваш проект и предложим оптимальное решение. Ориентировочные сроки: от 2 до 4 недель в зависимости от сложности. Стоимость рассчитывается индивидуально.







