DefaultDataFeed (Production Guide)
This page documents the real runtime contract of DefaultDataFeed so backend and frontend can integrate without guesswork.
For generic DataFeed concepts, see Data Access.
For data shape definitions, see Data Types.
What matters most
DefaultDataFeedcombines:- HTTP for
searchSymbols,getHistoricalTimeSeries,getPrice - WebSocket for live
kline,orderbook, andtrades
- HTTP for
- Subscriptions are deduplicated by a channel key per symbol/exchange/interval.
- Auth is optional, but when enabled it affects both HTTP headers and WebSocket handshake.
Constructor Configuration
new DefaultDataFeed(config)
interface DefaultDataFeedConfig {
baseUrl: string
endpoints: {
search: string
historical: string
websocket?: string
orderBook?: string
price?: string
}
headers?: Record<string, string>
authentication?: {
type: 'bearer'
token: string
required?: boolean
}
websocket?: {
enabled: boolean
reconnectInterval?: number
maxReconnectAttempts?: number
}
debug?: boolean
}Runtime defaults (if omitted)
baseUrl:http://localhost:3000endpoints.search|historical|websocket|orderBook|price:''headers:{ 'Content-Type': 'application/json' }websocket.enabled:truewebsocket.reconnectInterval:5000websocket.maxReconnectAttempts:3debug:false
Example
import { DefaultDataFeed } from 'chartspire'
const dataFeed = new DefaultDataFeed({
baseUrl: 'https://api.example.com',
endpoints: {
search: 'market-data/search',
historical: 'market-data/historical',
price: 'market-data/price',
websocket: 'websocket'
},
authentication: {
type: 'bearer',
token: userSessionToken,
required: true
},
websocket: {
enabled: true,
reconnectInterval: 3000,
maxReconnectAttempts: 5
},
debug: false
})HTTP Contract
type path segment is derived from symbol type via: stocks | crypto | forex | futures | all.
1) Search
GET {baseUrl}/{search}/{type}?symbol={query}
Response:
interface SearchResponse {
data: Symbol[]
type: 'stocks' | 'crypto' | 'forex' | 'futures' | 'all'
breakdown?: { stocks: number; crypto: number; forex: number; futures: number }
message?: string
error?: string
}2) Historical
GET {baseUrl}/{historical}/{type}?symbol={symbol}&interval={interval}&from={ms}&to={ms}
Response:
interface HistoricalDataResponse {
values: Array<{
datetime: string
open: string
high: string
low: string
close: string
volume: string
}>
meta: {
symbol: string
exchange?: string
original_interval: string
mapped_interval: string
is_exact_match: boolean
interval_warning?: string
currency?: string
[key: string]: any
}
type: 'stocks' | 'crypto' | 'forex' | 'futures'
error?: string
}3) Price
GET {baseUrl}/{price}/{type}?symbol={symbol}
Response expected by current implementation:
type PriceResponse = {
price: number
symbol: string
timestamp: number
}DefaultDataFeed reads result.price directly.
Do not return alternate shapes for this endpoint.
Interval behavior (historical vs live)
Historical requests
For historical loading, the requested chart interval is sent to your backend using the mapped API interval format.
Common outbound interval values:
1m,3m,5m,15m,30m1h,2h,4h,6h,8h,12h1d,3d,5d1w1M,3M,6M1y,5y
Make sure your backend accepts these formats.
Live WebSocket kline (ChartSpire runtime)
IMPORTANT (Live feed requirement)
For live charting, send 1-minute kline updates over WebSocket.
ChartSpire builds the selected display timeframe (5m, 1h, 1D, etc.) from these 1m updates on the client.In short:
- Live WebSocket kline: always provide
1m- Historical HTTP: support requested intervals from the list above
WebSocket Contract
WebSocket is used for live updates and receives/sends event-based messages.
Connection URL behavior
- If
authentication.required === true, URL is built aswss://... - Otherwise URL is built from
http://->ws://replacement
Use an HTTPS base URL with auth enabled for production secure transport. Without auth, http:// is converted to ws://; use https:// base URL for wss://.
Authentication handshake (when required)
Outgoing after connect:
{
"event": "authenticate",
"data": {
"token": "jwt-token",
"clientId": "client_xxx",
"timestamp": 1700000000000
}
}Expected events:
auth_successauth_error
Subscribe/unsubscribe
Outgoing subscribe (for live chart streams, use interval: "1m"):
{
"event": "subscribe",
"data": {
"type": "crypto",
"symbol": "BTCUSDT",
"dataType": "kline",
"interval": "1m",
"subscriptionId": "BTCUSDT_binance_1minute",
"exchange": "binance",
"clientId": "client_xxx"
}
}Unsubscribe uses the same data shape with event: "unsubscribe".
Incoming market data can arrive as:
- event envelope (
{ event: 'market_data', data: ... }) - or legacy direct payload (handled as fallback)
For kline payload, backend should provide either:
- direct OHLC fields, or
- nested exchange-kline style fields (
k,o,h,l,c, etc.)
Volume requirement for correct live candles:
- Use cumulative volume within the current 1m candle (exchange-style behavior).
- Avoid sending decreasing volume within the same 1m candle.
- On symbol/interval switches, ChartSpire resets aggregation state to prevent double counting.
Order book and trades
Order book and trades use WebSocket (dataType: 'orderbook', dataType: 'trades'). The orderBook HTTP endpoint in config is unused.
Channel & Subscription Behavior
Channel key format: {symbol}_{exchangeLowercase}_{multiplier}{timespan} (e.g. BTCUSDT_binance_1minute). One backend subscription per channel; multiple handlers share it; backend unsubscribes when the last handler is removed.
Use stable subscriber IDs: subscribeSymbol and unsubscribeSymbol rely on matching IDs. Omitting subscriberUID generates a timestamp-based ID, which makes unsubscribe unreliable. Use a stable ID per consumer (e.g. chart ID).
Type Mapping Constraints
getTypeIdentifier(...) currently maps only:
stocks,crypto,forex,futures,all
Other SYMBOL_TYPE values (like etf, bonds, indices, etc.) currently route as all at request/subscription path level.
Quick Diagnostics
dataFeed.debugChannels()
const count = dataFeed.getActiveSubscriptions()debugChannels()logs active channel keys and handlersgetActiveSubscriptions()returns active kline channel count plus active order book callback count