Торгова платформа з мільйоном угод на годину — стандартний 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-платформ та трейдингових терміналів. Використовуємо перевірені інструменти та надаємо офіційну гарантію на код.
Замовте розробку кастомного свічкового графіка — ми реалізуємо під ключ, від архітектури до деплою. Отримайте консультацію — обговоримо ваш проєкт і запропонуємо оптимальне рішення.







