Розробка візуалізації 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 тижнів залежно від складності. Вартість розраховується індивідуально.







