Skip to content

Data Access

Prerequisite: Load the ChartSpire UI stylesheet once (import '@chartspire/ui/style.css'). See Getting Started — Stylesheet.

ChartSpire loads all market data through a DataFeed. Use a built-in feed for evaluation, or implement a custom feed for production.

Types: Type Reference. Constructor options: Widget API.

Optional imports (not bundled unless used): @chartspire/ui/datafeeds/default, @chartspire/ui/datafeeds/binance, @chartspire/ui/datafeeds/common. See Getting Started.

Which Feed

FeedUse for
BinanceDataFeedCrypto demos / evaluation (not production trading)
DefaultDataFeedLocal evaluation against a compatible HTTP/WS backend
Custom DataFeedProduction (your provider / infrastructure)

ChartSpire does not supply market data. You own provider licensing and entitlements.

Method Map

FeatureMethods
Symbol searchsearchSymbols
Chart history + livegetHistoricalTimeSeries, subscribeSymbol, unsubscribeSymbol
Watchlist (WebSocket)subscribeSymbol
Watchlist (HTTP)getPrice
Order booksubscribeOrderBookData / unsubscribeOrderBookData (optional)
TradessubscribeTradeData / unsubscribeTradeData (optional)
TeardownunsubscribeAllForChart, then closeWebsocket when idle

DefaultDataFeed

Reference adapter for ChartSpire's HTTP/WebSocket contract (auth, reconnect, shared channels). Evaluation only — not a managed data service.

Config and wire format: DefaultDataFeed, Connect Your Backend.

BinanceDataFeed

Public Binance spot REST + WebSocket. No API key.

typescript
import { ChartSpire, SYMBOL_TYPE } from '@chartspire/ui'
import BinanceDataFeed from '@chartspire/ui/datafeeds/binance'

const chartspire = new ChartSpire({
  container: document.getElementById('chart-container')!,
  enabledSymbolTypes: [SYMBOL_TYPE.CRYPTO],
  symbol: { symbol: 'BTCUSDT', type: SYMBOL_TYPE.CRYPTO, exchange: 'binance' },
  interval: { multiplier: 1, timespan: 'day', text: 'D' },
  dataFeed: new BinanceDataFeed(),
})

Limits

  • Crypto spot only; demo/evaluation, not trading-grade
  • Search is exact symbol lookup (not full-text)
  • History returns up to the latest 1000 bars and ignores from / to — no scroll pagination
  • Public API rate limits apply
  • Instances share one WebSocket controller — create once, reuse
typescript
const dataFeed = new BinanceDataFeed({
  rateLimit: {
    maxMessages: 4,   // per rolling window
    windowMs: 1000,
    maxQueueSize: 1000,
    reconcileDelayMs: 40,
  },
  onError: (error) => console.error(error),
})

rateLimit is applied on first controller creation only.

DataFeed registry

Register a feed class once and pass its name as dataFeed in ChartOptions:

typescript
import { getDataFeedRegistry, ChartSpire } from '@chartspire/ui'
import { MyDataFeed } from './MyDataFeed'

getDataFeedRegistry().registerDataFeed('my-feed', MyDataFeed)

new ChartSpire({
  dataFeed: 'my-feed',
  // ...
})

The registry is process-global (shared across embeds on the page). Prefer passing a DataFeed instance when each embed needs its own feed configuration.

Custom DataFeed

Import DataFeed from @chartspire/ui and implement the required methods. Prefer the exported type over copy-pasting the interface.

Important: shared subscriptions

Charts, watchlists, order book, and trades can all subscribe to the same symbol (and interval) independently. Your feed must track each local subscriber ID, share one backend channel per symbol/exchange/interval/data type, and only unsubscribe from the provider when the last local subscriber is removed. Unsubscribing too early drops data for other components.

Required

  • searchSymbols(search?, type?)Symbol[] (type omitted or 'all' = no filter)
  • getHistoricalTimeSeries(symbol, period, from, to, type?) → candles for that interval and ms window
  • getPrice(symbol, type?)number | null (HTTP watchlist)
  • subscribeSymbol / unsubscribeSymbol — live candles for one subscriberUID
  • unsubscribeAllForChart(chartId) — drop that chart's handlers only
  • closeWebsocket() — stop transport + reconnect timers
  • clearAllListeners() — clear callbacks / subscription state
  • getActiveSubscriptions() → active live channel count (0 when idle)

Transport is your choice (WebSocket, SSE, polling). Non-WS feeds: implement lifecycle methods as no-ops.

Optional

  • Order book: subscribeOrderBookData / unsubscribeOrderBookData
  • Trades: subscribeTradeData / unsubscribeTradeData
  • debugChannels

Disable order book / trades UI if unimplemented. Payloads: Market data.

Contract Rules

Interval-native. History and live must use the requested period. ChartSpire does not aggregate.

Windows. from / to are Unix ms. Return only bars in range, ascending by timestamp. Empty → []. Initial window size: dataFeedInitialHistoricalBars (default 500). Ignoring from/to breaks pagination.

Candles. Millisecond bar-open timestamp; finite open/high/low/close; optional finite volume/turnover. In-progress bar: same open timestamp until the next interval starts.

Live callback. Call callback(candle, symbol). The symbol argument is required for WebSocket watchlists.

subscriberUID. ChartSpire always passes a stable ID (param is optional for compatibility). Key handlers by that ID.

Shared channels. Key by data type + symbol + exchange + interval (+ type when needed). Map subscriberUID → callback. First subscriber opens the backend channel; last removes it. Same ID on re-subscribe replaces the callback. See the Important note above.

Errors. Empty search/history → []. Missing price → null. Subscription methods have no error return — handle failures internally. On reconnect: restore desired subscriptions, avoid duplicates, stop timers in closeWebsocket().

Rate-Limit Helper

Optional outbound control-frame pacing:

typescript
import { RateLimitedWebSocketQueue } from '@chartspire/ui/datafeeds/common'

const outbound = new RateLimitedWebSocketQueue({
  maxMessages: 5,
  windowMs: 1000,
  maxQueueSize: 1000,
  onOverflow: (error) => console.error(error),
})

outbound.setSend((frame) => socket.send(frame))
outbound.setReady(true) // after open
const accepted = outbound.enqueue(JSON.stringify(controlFrame))

Handle enqueue overflow; setReady(false) on close; rebind after reconnect; destroy() on teardown. Queue does not dedupe or reconcile subscriptions.

Wire It Up

typescript
import { ChartSpire, SYMBOL_TYPE, type DataFeed } from '@chartspire/ui'
import { CustomDataFeed } from './CustomDataFeed'

const dataFeed: DataFeed = new CustomDataFeed()

const chartspire = new ChartSpire({
  container: document.getElementById('chart-container')!,
  enabledSymbolTypes: [SYMBOL_TYPE.STOCKS],
  symbol: { symbol: 'CUSTOM_SYMBOL', type: SYMBOL_TYPE.STOCKS },
  interval: { multiplier: 1, timespan: 'day', text: 'D' },
  dataFeed,
})

// On unmount:
chartspire.destroy()