Data Management
Learn how to efficiently manage, load, and update chart data in TradeX Chart.
Data Format
OHLCV Array Format
TradeX Chart uses the standard OHLCV (Open, High, Low, Close, Volume) format:
const candle = [
timestamp, // Unix timestamp in milliseconds
open, // Opening price
high, // Highest price
low, // Lowest price
close, // Closing price
volume // Trading volume (optional)
]
Example Data
const ohlcvData = [
[1609459200000, 29000, 29500, 28800, 29200, 1234.56],
[1609462800000, 29200, 29800, 29100, 29600, 2345.67],
[1609466400000, 29600, 30000, 29400, 29800, 3456.78]
]
Loading Initial Data
At Chart Initialization
const chart = document.createElement('tradex-chart')
chart.start({
title: 'BTC/USDT',
state: {
ohlcv: ohlcvData
}
})
After Initialization
// Replace all data
chart.setData(newOhlcvData)
// Merge with existing data
chart.mergeData(additionalData)
Updating Data
Adding New Candles
// Add a single completed candle
const newCandle = [1609470000000, 29800, 30200, 29700, 30000, 4567.89]
chart.addCandle(newCandle)
// Add multiple candles
const newCandles = [
[1609470000000, 29800, 30200, 29700, 30000, 4567.89],
[1609473600000, 30000, 30500, 29900, 30300, 5678.90]
]
chart.addCandles(newCandles)
Updating Streaming Candle
For real-time updates of the current candle:
// Update the last candle with new values
const streamingCandle = [1609477200000, 30300, 30400, 30200, 30350, 6789.01]
chart.updateStreamingCandle(streamingCandle)
Removing Data
// Remove last N candles
chart.removeCandles(10)
// Remove candles by timestamp range
chart.removeCandlesByRange(startTimestamp, endTimestamp)
// Clear all data
chart.clearData()
Data Validation
Validating OHLCV Data
function validateOHLCV(candle) {
if (!Array.isArray(candle) || candle.length < 5) {
throw new Error('Invalid candle format')
}
const [timestamp, open, high, low, close, volume] = candle
// Validate timestamp
if (!Number.isInteger(timestamp) || timestamp <= 0) {
throw new Error('Invalid timestamp')
}
// Validate prices
if (high < low) {
throw new Error('High price cannot be less than low price')
}
if (open < low || open > high) {
throw new Error('Open price must be between low and high')
}
if (close < low || close > high) {
throw new Error('Close price must be between low and high')
}
// Validate volume (if present)
if (volume !== undefined && volume < 0) {
throw new Error('Volume cannot be negative')
}
return true
}
// Use validation
try {
validateOHLCV(candle)
chart.addCandle(candle)
} catch (error) {
console.error('Invalid candle data:', error.message)
}
Data Transformation
Converting from Different Formats
From Object Format
function objectToOHLCV(candleObj) {
return [
candleObj.timestamp || candleObj.time,
candleObj.open,
candleObj.high,
candleObj.low,
candleObj.close,
candleObj.volume || 0
]
}
const objectData = [
{ timestamp: 1609459200000, open: 29000, high: 29500, low: 28800, close: 29200, volume: 1234.56 },
{ timestamp: 1609462800000, open: 29200, high: 29800, low: 29100, close: 29600, volume: 2345.67 }
]
const ohlcvData = objectData.map(objectToOHLCV)
From String Format
function parseCSVToOHLCV(csvLine) {
const parts = csvLine.split(',')
return [
parseInt(parts[0]), // timestamp
parseFloat(parts[1]), // open
parseFloat(parts[2]), // high
parseFloat(parts[3]), // low
parseFloat(parts[4]), // close
parseFloat(parts[5]) // volume
]
}
const csvData = `
1609459200000,29000,29500,28800,29200,1234.56
1609462800000,29200,29800,29100,29600,2345.67
`
const ohlcvData = csvData
.trim()
.split('\n')
.map(parseCSVToOHLCV)
Data Fetching
Fetching from REST API
async function fetchOHLCVData(symbol, timeframe, limit = 500) {
try {
const response = await fetch(
`https://api.example.com/ohlcv?symbol=${symbol}&timeframe=${timeframe}&limit=${limit}`
)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data = await response.json()
return data
} catch (error) {
console.error('Failed to fetch OHLCV data:', error)
throw error
}
}
// Usage
fetchOHLCVData('BTCUSDT', '1h', 1000)
.then(data => {
chart.setData(data)
})
.catch(error => {
console.error('Error loading chart data:', error)
})
Fetching Historical Data
async function fetchHistoricalData(symbol, startTime, endTime) {
const chunks = []
let currentStart = startTime
const chunkSize = 1000 // Max candles per request
while (currentStart < endTime) {
const response = await fetch(
`https://api.example.com/ohlcv?symbol=${symbol}&start=${currentStart}&limit=${chunkSize}`
)
const data = await response.json()
if (data.length === 0) break
chunks.push(...data)
currentStart = data[data.length - 1][0] + 1
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 100))
}
return chunks
}
// Usage
const startTime = Date.now() - (30 * 24 * 60 * 60 * 1000) // 30 days ago
const endTime = Date.now()
fetchHistoricalData('BTCUSDT', startTime, endTime)
.then(data => {
chart.setData(data)
})
Data Caching
Local Storage Cache
class ChartDataCache {
constructor(cacheKey = 'chart_data') {
this.cacheKey = cacheKey
}
save(symbol, timeframe, data) {
const key = `${this.cacheKey}_${symbol}_${timeframe}`
const cacheData = {
data: data,
timestamp: Date.now()
}
try {
localStorage.setItem(key, JSON.stringify(cacheData))
} catch (error) {
console.error('Failed to cache data:', error)
}
}
load(symbol, timeframe, maxAge = 5 * 60 * 1000) {
const key = `${this.cacheKey}_${symbol}_${timeframe}`
try {
const cached = localStorage.getItem(key)
if (!cached) return null
const cacheData = JSON.parse(cached)
const age = Date.now() - cacheData.timestamp
if (age > maxAge) {
this.clear(symbol, timeframe)
return null
}
return cacheData.data
} catch (error) {
console.error('Failed to load cached data:', error)
return null
}
}
clear(symbol, timeframe) {
const key = `${this.cacheKey}_${symbol}_${timeframe}`
localStorage.removeItem(key)
}
clearAll() {
const keys = Object.keys(localStorage)
keys.forEach(key => {
if (key.startsWith(this.cacheKey)) {
localStorage.removeItem(key)
}
})
}
}
// Usage
const cache = new ChartDataCache()
// Try to load from cache first
let data = cache.load('BTCUSDT', '1h')
if (!data) {
// Fetch from API if not cached
data = await fetchOHLCVData('BTCUSDT', '1h')
cache.save('BTCUSDT', '1h', data)
}
chart.setData(data)
IndexedDB Cache
class IndexedDBCache {
constructor(dbName = 'ChartDataDB', storeName = 'ohlcv') {
this.dbName = dbName
this.storeName = storeName
this.db = null
}
async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1)
request.onerror = () => reject(request.error)
request.onsuccess = () => {
this.db = request.result
resolve()
}
request.onupgradeneeded = (event) => {
const db = event.target.result
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName, { keyPath: 'id' })
}
}
})
}
async save(symbol, timeframe, data) {
if (!this.db) await this.init()
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readwrite')
const store = transaction.objectStore(this.storeName)
const record = {
id: `${symbol}_${timeframe}`,
symbol,
timeframe,
data,
timestamp: Date.now()
}
const request = store.put(record)
request.onsuccess = () => resolve()
request.onerror = () => reject(request.error)
})
}
async load(symbol, timeframe) {
if (!this.db) await this.init()
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly')
const store = transaction.objectStore(this.storeName)
const request = store.get(`${symbol}_${timeframe}`)
request.onsuccess = () => {
const record = request.result
resolve(record ? record.data : null)
}
request.onerror = () => reject(request.error)
})
}
}
// Usage
const cache = new IndexedDBCache()
await cache.init()
let data = await cache.load('BTCUSDT', '1h')
if (!data) {
data = await fetchOHLCVData('BTCUSDT', '1h')
await cache.save('BTCUSDT', '1h', data)
}
chart.setData(data)
Data Pagination
Loading Data on Demand
class PaginatedDataLoader {
constructor(chart, fetchFunction) {
this.chart = chart
this.fetchFunction = fetchFunction
this.isLoading = false
this.hasMore = true
this.setupScrollListener()
}
setupScrollListener() {
this.chart.on('scroll', (event) => {
// Check if user scrolled to the left edge
if (event.position < 0.1 && !this.isLoading && this.hasMore) {
this.loadMore()
}
})
}
async loadMore() {
this.isLoading = true
try {
const currentData = this.chart.getData()
const oldestTimestamp = currentData[0][0]
// Fetch older data
const newData = await this.fetchFunction(oldestTimestamp)
if (newData.length === 0) {
this.hasMore = false
return
}
// Prepend new data
this.chart.prependData(newData)
} catch (error) {
console.error('Failed to load more data:', error)
} finally {
this.isLoading = false
}
}
}
// Usage
const loader = new PaginatedDataLoader(chart, async (beforeTimestamp) => {
return await fetchOHLCVData('BTCUSDT', '1h', 500, beforeTimestamp)
})
Data Compression
Compressing for Storage
function compressOHLCV(data) {
// Store deltas instead of absolute values
if (data.length === 0) return []
const compressed = [data[0]] // First candle as-is
for (let i = 1; i < data.length; i++) {
const prev = data[i - 1]
const curr = data[i]
compressed.push([
curr[0] - prev[0], // timestamp delta
curr[1] - prev[4], // open delta from prev close
curr[2] - curr[1], // high delta from open
curr[3] - curr[1], // low delta from open
curr[4] - curr[1], // close delta from open
curr[5] // volume (no compression)
])
}
return compressed
}
function decompressOHLCV(compressed) {
if (compressed.length === 0) return []
const data = [compressed[0]]
for (let i = 1; i < compressed.length; i++) {
const prev = data[i - 1]
const delta = compressed[i]
const timestamp = prev[0] + delta[0]
const open = prev[4] + delta[1]
const high = open + delta[2]
const low = open + delta[3]
const close = open + delta[4]
const volume = delta[5]
data.push([timestamp, open, high, low, close, volume])
}
return data
}
// Usage
const compressed = compressOHLCV(ohlcvData)
localStorage.setItem('chart_data', JSON.stringify(compressed))
const loaded = JSON.parse(localStorage.getItem('chart_data'))
const decompressed = decompressOHLCV(loaded)
chart.setData(decompressed)
Data Export
Export to CSV
function exportToCSV(data, filename = 'chart_data.csv') {
const headers = ['Timestamp', 'Open', 'High', 'Low', 'Close', 'Volume']
const rows = data.map(candle => candle.join(','))
const csv = [headers.join(','), ...rows].join('\n')
const blob = new Blob([csv], { type: 'text/csv' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
}
// Usage
const data = chart.getData()
exportToCSV(data, 'btcusdt_1h.csv')
Export to JSON
function exportToJSON(data, filename = 'chart_data.json') {
const json = JSON.stringify(data, null, 2)
const blob = new Blob([json], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
}
// Usage
const data = chart.getData()
exportToJSON(data, 'btcusdt_1h.json')
Best Practices
1. Data Validation
Always validate data before adding to the chart:
function safeAddCandle(chart, candle) {
try {
validateOHLCV(candle)
chart.addCandle(candle)
} catch (error) {
console.error('Invalid candle:', error)
}
}
2. Batch Updates
Batch multiple updates for better performance:
// Bad: Multiple individual updates
candles.forEach(candle => chart.addCandle(candle))
// Good: Single batch update
chart.addCandles(candles)
3. Memory Management
Limit the amount of data in memory:
const MAX_CANDLES = 10000
function addCandleWithLimit(chart, candle) {
const currentData = chart.getData()
if (currentData.length >= MAX_CANDLES) {
// Remove oldest candle
chart.removeCandles(1)
}
chart.addCandle(candle)
}
4. Error Handling
Always handle data loading errors:
async function loadChartData(symbol, timeframe) {
try {
const data = await fetchOHLCVData(symbol, timeframe)
chart.setData(data)
} catch (error) {
console.error('Failed to load chart data:', error)
// Show error message to user
showError('Failed to load chart data. Please try again.')
}
}
Related Documentation
- Getting Started - Basic setup
- WebSocket Integration - Real-time data
- Performance Optimization - Optimize data handling
- API Reference - Data methods