Розробка веб-термінала з графіками та стаканом

Зауважимо: коли біржовий потік видає 2000+ оновлень стакана за секунду, браузер починає гальмувати, дані розузгоджуються, а трейдер втрачає гроші. Ми вирішуємо ці проблеми на рівні архітектури: правильний вибір стеку, диференціальні оновлення, оптимізація рендерингу. За плечима 8+ років досвіду в бл

Напрямки блокчейн-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1452
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1309
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1005
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1270
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    719
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1012

Зауважимо: коли біржовий потік видає 2000+ оновлень стакана за секунду, браузер починає гальмувати, дані розузгоджуються, а трейдер втрачає гроші. Ми вирішуємо ці проблеми на рівні архітектури: правильний вибір стеку, диференціальні оновлення, оптимізація рендерингу. За плечима 8+ років досвіду в блокчейн-розробці та 15+ реалізованих торгових інтерфейсів. Наші термінали обробляють до 10 000 ордерів за секунду із затримкою менше 5 мс. Використовуємо перевірені рішення: WebSocket для потоків, Redis для кешування, ClickHouse для історії. Це дозволяє не втрачати жодного тіка та тримати інтерфейс чуйним.

Нещодавно ми завершили проєкт для криптобіржі, де потік даних сягав 3000 оновлень за секунду. Наше рішення обробляло його без жодного пропущеного тіка, а затримка інтерфейсу не перевищувала 2 мс. Для однієї з бірж ми оптимізували архітектуру так, що витрати на інфраструктуру знизилися на 40% за рахунок ефективного використання WebSocket-шлюзів та Redis.

Які технології використовують професійні термінали?

Фронтенд: React 18 + TypeScript, Zustand для керування станом, TradingView Lightweight Charts, react-virtual для віртуалізації списків. Бекенд: FastAPI (Python) або Fastify (Node.js) як WebSocket-шлюз, Redis для кешування та pub/sub. Сховище: ClickHouse або TimescaleDB для історичних OHLCV-даних.

Потік даних: Binance WS → Exchange Connector → Redis PubSub → WS Gateway → Browser. Шлюз мультиплексує дані для всіх клієнтів, що дозволяє обслуговувати сотні з'єднань з мінімальною затримкою.

Реалізація стакана ордерів: як уникнути розсинхронізації?

Order book будується на диференціальних оновленнях. Спочатку завантажується снапшот через REST, потім через WebSocket приходять зміни. Ключова проблема — дотримання послідовності: кожен diff містить U (first update id) та u (last update id). Якщо пропущено снапшот, дані розсинхронізуються. Ми буферизуємо diff'и та перевіряємо lastUpdateId:

class OrderBookManager { private bids: Map<number, number> = new Map(); private asks: Map<number, number> = new Map(); private lastUpdateId: number = 0; private buffer: OrderBookDiff[] = []; async initialize(symbol: string) { const ws = this.connectToStream(`${symbol.toLowerCase()}@depth`); const snapshot = await fetchOrderBookSnapshot(symbol, 1000); this.lastUpdateId = snapshot.lastUpdateId; this.bids = new Map(snapshot.bids.map(([p, q]) => [+p, +q])); this.asks = new Map(snapshot.asks.map(([p, q]) => [+p, +q])); for (const diff of this.buffer) { if (diff.U <= this.lastUpdateId + 1 && diff.u >= this.lastUpdateId + 1) { this.applyDiff(diff); } } this.buffer = []; } applyDiff(diff: OrderBookDiff) { if (diff.U !== this.lastUpdateId + 1) { console.error('Gap detected, reinitializing...'); this.initialize(this.symbol); return; } for (const [price, qty] of diff.b) { if (+qty === 0) this.bids.delete(+price); else this.bids.set(+price, +qty); } for (const [price, qty] of diff.a) { if (+qty === 0) this.asks.delete(+price); else this.asks.set(+price, +qty); } this.lastUpdateId = diff.u; this.notifySubscribers(); } getTopLevels(depth: number = 20) { const sortedBids = [...this.bids.entries()] .sort(([a], [b]) => b - a) .slice(0, depth); const sortedAsks = [...this.asks.entries()] .sort(([a], [b]) => a - b) .slice(0, depth); return { bids: sortedBids, asks: sortedAsks }; } } 

Для відображення використовуємо віртуалізацію через @tanstack/react-virtual, щоб рендерити тільки видимі рядки. Це дозволяє обробляти 500+ рівнів без втрати продуктивності. Наш підхід з диференціальними оновленнями в 3 рази швидше повного перезавантаження стакана кожні 100 ms, а буферизація гарантує консистентність навіть при тимчасових розривах мережі.

Графіки: TradingView Lightweight Charts проти альтернатив

Для відображення свічкових графіків використовуємо Lightweight Charts. Ця бібліотека в 2 рази швидше Chart.js при рендерингу свічок і надає вбудовану підтримку фінансових даних. Порівняння:

Бібліотека Продуктивність Кастомізація Фінансові дані
TradingView LWC Висока (до 60 FPS) Висока Вбудована підтримка свічок
Chart.js Середня (20-30 FPS) Середня Потрібна доробка
D3.js Висока (складність) Максимальна Низька (самописні компоненти)

Приклад підключення:

import { createChart, CandlestickSeries } from 'lightweight-charts'; function TradingChart({ symbol }: { symbol: string }) { const chartContainerRef = useRef<HTMLDivElement>(null); const seriesRef = useRef<CandlestickSeries>(); useEffect(() => { const chart = createChart(chartContainerRef.current!, { width: chartContainerRef.current!.clientWidth, height: 400, layout: { background: { color: '#1a1a2e' }, textColor: '#d1d4dc', }, grid: { vertLines: { color: '#2d2d4e' }, horzLines: { color: '#2d2d4e' }, }, crosshair: { mode: 1 }, timeScale: { borderColor: '#485c7b', timeVisible: true }, }); const candleSeries = chart.addCandlestickSeries({ upColor: '#26a69a', downColor: '#ef5350', borderUpColor: '#26a69a', borderDownColor: '#ef5350', }); seriesRef.current = candleSeries; loadHistoricalData(symbol, '1h').then((candles) => { candleSeries.setData(candles); chart.timeScale().fitContent(); }); const unsubscribe = wsGateway.subscribe(`candle:${symbol}:1h`, (candle) => { candleSeries.update(candle); }); return () => { unsubscribe(); chart.remove(); }; }, [symbol]); return <div ref={chartContainerRef} className="chart-container" />; } 

Lightweight Charts в 3 рази швидше Chart.js на великих датасетах (10 000+ свічок).

Чому важливо реалізувати правильний reconnect?

При втраті з'єднання автоматичне відновлення з експоненційною затримкою (1 с → 30 с) мінімізує простій. Ми використовуємо окремий хук, який обробляє reconnect та ресубміт підписок:

export function useWebSocket(url: string) { const wsRef = useRef<WebSocket | null>(null); const reconnectTimerRef = useRef<NodeJS.Timeout>(); const reconnectCountRef = useRef(0); const connect = useCallback(() => { wsRef.current = new WebSocket(url); wsRef.current.onclose = (e) => { if (!e.wasClean) { const delay = Math.min(1000 * 2 ** reconnectCountRef.current++, 30000); reconnectTimerRef.current = setTimeout(connect, delay); } else { reconnectCountRef.current = 0; } }; wsRef.current.onerror = () => wsRef.current?.close(); }, [url]); useEffect(() => { connect(); return () => { clearTimeout(reconnectTimerRef.current); wsRef.current?.close(); }; }, [connect]); return wsRef; } 

На практиці reconnect займає менше 2 секунд, що критично при high-frequency trading. Детальніше про WebSocket API на MDN.

Як ми оптимізуємо продуктивність?

Критичні точки: throttling оновлень, мемоізація компонентів, відмальовка depth chart через Canvas. Ми застосовуємо requestAnimationFrame для рендерингу, щоб не блокувати потік. Наприклад, при 50+ оновленнях стакана за секунду буфер накопичує diff'и, а render відбувається не частіше одного разу за кадр. Це дозволяє тримати FPS на рівні 60 навіть при пікових навантаженнях. Наші оптимізації знижують навантаження на CPU на 40% порівняно з наївною реалізацією. Завдяки цьому операційні витрати на серверну інфраструктуру скорочуються на 30%.

Процес розробки та що входить у роботу

Етап Тривалість Результат
Аналіз вимог 1-2 дні Технічне завдання
Архітектура 2-3 дні Документація
Реалізація 2-4 тижні Робочий код
Тестування 1 тиждень Звіт про навантаження
Деплой 2-3 дні Готове рішення

У розробку під ключ входить: вихідний код фронтенду та бекенду з коментарями, інтеграція з біржею (Binance, Bybit, OKX та ін.), налаштування WebSocket-шлюзу, документація з деплою та експлуатації, навчання вашої команди (2 години) та 3 місяці безкоштовної підтримки після запуску.

Ми також надаємо консультацію з вибору стеку та архітектури. Зв'яжіться з нами для обговорення вашого проєкту. Замовте розробку веб-термінала під ключ і отримайте готове рішення з низькою затримкою.