Торгова платформа з мільйоном угод на годину — стандартний TradingView віджет не справляється: агрегація гальмує, кастомізація неможлива. В одному з проєктів для клієнта з 50 000 пар і 2 млн угод на хвилину ми побудували кастомний свічковий графік з нуля. Рішення на TimescaleDB для OHLCV, Go-агрегаторі для real-time та Lightweight Charts для рендерингу обробляє свічку за 3 мс і відображає дані без затримок. Розберемо, як уникнути типових помилок і отримати графік, який не лагає на 100k свічок. Наші рішення дозволяють знизити витрати на інфраструктуру вдвічі за рахунок ефективної агрегації. Зв'яжіться з нами, щоб обговорити вимоги до вашого графіка.
Чому варто обрати кастомний графік?
Готові віджети не дають контролювати логіку агрегації (наприклад, зважені ціни за об'ємом) і не дозволяють додавати індикатори на зразок VWAP з нестандартним періодом. Кастомний графік дає повний контроль над кожним етапом: від збору даних до рендерингу, що критично важливо для пропрієтарних торгових стратегій та швидкої реакції ринку.
Проблеми, які вирішуємо
Основні складнощі при побудові кастомних графіків — latency та кастомізація. Готові віджети не дозволяють міняти логіку агрегації (наприклад, використовувати зважені ціни за об'ємом) або додавати індикатори на зразок VWAP з нестандартним періодом. Друга проблема — продуктивність: при тисячах свічок і кількох індикаторах рендеринг може гальмувати. Ми вирішуємо це комбінацією TimescaleDB на сервері та Lightweight Charts на клієнті.
Як ми вирішуємо проблему latency?
Latency складається з трьох етапів: отримання угоди (trade event), агрегація свічки та відправка на клієнт. Кожен етап має укладатися в мілісекунди. Покроковий процес:
- Отримання: WebSocket-шлюз обробляє до 100 000 повідомлень на секунду на один інстанс.
- Агрегація: In-memory агрегатор на Go оновлює поточну свічку за <2 мкс, закриті свічки — за 5 мкс.
- Доставка: Debounce оновлень до 10 разів на секунду, пакетна відправка — не більше 10 свічок в одному повідомленні.
Continuous aggregate в TimescaleDB автоматично оновлює матеріалізовані представлення. Ми налаштовуємо політику з інтервалом в 1 хвилину для свічок 1m, що дає свіжість даних до 1 хвилини. Для вищих таймфреймів агрегація виконується на льоту з 1m-свічок. У результаті end-to-end затримка від торгів до відображення на графіку не перевищує 200 мс.
Як реалізувати кастомні індикатори?
Індикатори (EMA, VWAP, об'ємний профіль) розраховуються на клієнті або сервері — вибір залежить від частоти перерахунку. Для індикаторів з постійним вікном (наприклад, EMA на 12 періодів) використовуємо серверну агрегацію: додаємо колонки в continuous aggregate. Для інтерактивних (переміщення точок) — клієнтський розрахунок у Web Worker, щоб не блокувати UI.
Приклад серверного індикатора — VWAP з налаштовуваним періодом:
CREATE MATERIALIZED VIEW vwap_1m WITH (timescaledb.continuous) AS SELECT time_bucket('1 minute', created_at) AS bucket, pair_id, sum(price * quantity) / sum(quantity) AS vwap FROM trades GROUP BY bucket, pair_id; Як реалізувати кастомний свічковий графік?
Візьмемо типовий кейс: біржа хоче показувати 1-хвилинні свічки з Heikin-Ashi та EMA. Також підтримуємо Renko, kagi та point-and-figure. Серверна частина агрегує trades через TimescaleDB, а фронтенд використовує бібліотеку Lightweight Charts.
Зберігання та агрегація OHLCV
Сирі угоди зберігаються в TimescaleDB. Для швидкої агрегації створюємо continuous aggregate:
CREATE MATERIALIZED VIEW candles_1m WITH (timescaledb.continuous) AS SELECT time_bucket('1 minute', created_at) AS bucket, pair_id, first(price, created_at) AS open, max(price) AS high, min(price) AS low, last(price, created_at) AS close, sum(quantity) AS volume, count(*) AS trades_count FROM trades GROUP BY bucket, pair_id; SELECT add_continuous_aggregate_policy('candles_1m', start_offset => INTERVAL '3 hours', end_offset => INTERVAL '1 minute', schedule_interval => INTERVAL '1 minute'); З 1-хвилинних свічок вищі таймфрейми збираються на льоту — той же SQL з time_bucket('1 hour', bucket).
Real-time оновлення на Go
type CandleAggregator struct { mu sync.RWMutex current map[PairTimeframe]*Candle } func (ca *CandleAggregator) OnTrade(trade Trade) { ca.mu.Lock() defer ca.mu.Unlock() for _, tf := range TIMEFRAMES { key := PairTimeframe{trade.PairID, tf} bucket := truncateToTimeframe(trade.Time, tf) candle, exists := ca.current[key] if !exists || candle.Bucket != bucket { if exists { ca.publishClosedCandle(candle) } ca.current[key] = &Candle{ Bucket: bucket, Open: trade.Price, High: trade.Price, Low: trade.Price, Close: trade.Price, Volume: trade.Quantity, } } else { if trade.Price > candle.High { candle.High = trade.Price } if trade.Price < candle.Low { candle.Low = trade.Price } candle.Close = trade.Price candle.Volume = candle.Volume.Add(trade.Quantity) } ca.publishLiveCandle(ca.current[key]) } } Фронтенд: TradingView Lightweight Charts
import { createChart, CandlestickSeries } from 'lightweight-charts'; const chart = createChart(container, { layout: { background: { color: '#0d0d0f' }, textColor: '#9b9ea8' }, grid: { vertLines: { color: '#1e2030' }, horzLines: { color: '#1e2030' } }, timeScale: { timeVisible: true, secondsVisible: false }, }); const candleSeries = chart.addSeries(CandlestickSeries, { upColor: '#00b15e', downColor: '#e84242', borderVisible: false, }); // Load historical + stream via WebSocket candleSeries.setData(historicalData); ws.onmessage = (event) => candleSeries.update(JSON.parse(event.data)); Індикатори (EMA, VWAP) додаються як LineSeries поверх. Для Heikin-Ashi конвертуємо дані на клієнті:
function toHeikinAshi(candles: OHLCV[]): OHLCV[] { return candles.map((c, i) => { const prev = i > 0 ? candles[i-1] : c; const haClose = (c.open + c.high + c.low + c.close) / 4; const haOpen = i === 0 ? (c.open + c.close)/2 : (prev.open + prev.close)/2; return { time: c.time, open: haOpen, high: Math.max(c.high, haOpen, haClose), low: Math.min(c.low, haOpen, haClose), close: haClose, }; }); } Порівняння продуктивності: Lightweight Charts vs інші бібліотеки
| Параметр | Lightweight Charts v4 | D3.js | Highcharts |
|---|---|---|---|
| Час рендерингу 10k свічок | 12 мс | 45 мс | 30 мс |
| RAM при 100k свічок | 8 MB | 25 MB | 18 MB |
| Підтримка Web Worker | так | так | обмежена |
| Кастомні індикатори | TypeScript | JavaScript | JSON |
Lightweight Charts в 3-4 рази швидше D3.js при рендерингу 10 000 свічок і споживає в 3 рази менше пам'яті.
Які дані потрібні для старту?
Для початку достатньо доступу до trade events (сирі угоди) через API або файли. Ми самостійно налаштовуємо агрегацію. Якщо даних нема — допоможемо підключитися до біржових джерел через WebSocket або REST. Мінімальна конфігурація не вимагає специфічних схем — TimescaleDB адаптується під будь-яку структуру.
Етапи роботи
| Етап | Що робимо |
|---|---|
| Архітектура | Вибір СУБД, схема агрегації, API чати |
| Сервер | TimescaleDB + Go-мікросервіс для real-time |
| Фронтенд | Інтеграція Lightweight Charts, кастомні індикатори, брендинг |
| WebSocket | Push-оновлення з дебаунсом |
| Документація | Опис схем, API, інструкція з розгортання |
| Навчання | Воркшоп для ваших розробників (2 дні) |
| Підтримка | 2 тижні після запуску (баги, доопрацювання) |
Що входить у роботу
У результат входить повний пакет: документація (схеми БД, API-специфікація, інструкція з розгортання), доступ до вихідного коду в Git-репозиторії, навчання вашої команди (воркшоп на 2 дні), 2 тижні безкоштовної підтримки після запуску та SLA з відповіддю до 4 годин. Ми забезпечуємо плавний перехід і самостійну експлуатацію.
Стек
| Компонент | Технологія |
|---|---|
| Time-series DB | TimescaleDB (PostgreSQL extension) |
| Real-time агрегація | Go microservice |
| WebSocket | gorilla/websocket |
| Frontend | TradingView Lightweight Charts v4 |
| Індикатори | Custom TypeScript + ta-lib.wasm |
| State management | Zustand |
Терміни та вартість
Терміни орієнтовні, розраховуються індивідуально:
- Базовий графік (live-оновлення, 2–3 індикатори): 4–6 тижнів.
- Повнофункціональний charting (всі таймфрейми, 10+ індикаторів, малювання, Heikin-Ashi/Renko): 2–3 місяці.
- Серверна частина (TimescaleDB + агрегатор + WebSocket): 3–5 тижнів паралельно.
Вартість розраховується під ваш обсяг даних і вимоги. Гарантуємо прозорість: попередня оцінка безкоштовно, фіксація бюджету після затвердження ТЗ. Наші клієнти економлять до 40% часу на розробку за рахунок готових компонентів і перевірених рішень.
Наш досвід та гарантії
Ми — команда блокчейн-розробників з 5+ роками досвіду в Web3. Реалізували 30+ проєктів для криптобірж, DeFi-платформ та трейдингових терміналів. Використовуємо перевірені інструменти та надаємо офіційну гарантію на код.
Замовте розробку кастомного свічкового графіка — ми реалізуємо під ключ, від архітектури до деплою. Отримайте консультацію — обговоримо ваш проєкт і запропонуємо оптимальне рішення.







