Appearance
DefaultDataFeed
Reference adapter for local evaluation — not a managed data service. For production, implement
DataFeed. See also Type Reference.
Naming: APIs use
CandleStickData. Wire protocol usesdataType: "kline"— keep that string in subscribe frames and payloads.
Essentials
What you need to know before wiring a backend.
- HTTP:
searchSymbols,getHistoricalTimeSeries,getPrice - WebSocket: live
kline,orderbook,trades - TLS by default;
http:///ws://needsecurity.allowInsecureTransport: true - One backend channel per
{ dataType, symbol, exchange, type, interval }(interval only forkline) subscriberUIDis required on subscribe/unsubscribe
Configuration
Pass a DefaultDataFeedConfig to the constructor.
typescript
import DefaultDataFeed, { type DefaultDataFeedConfig } from '@chartspire/ui/datafeeds/default'
const dataFeed = new DefaultDataFeed({
http: {
baseUrl: 'https://api.example.com',
endpoints: {
search: '/v1/market-data/symbols',
historical: '/v1/market-data/candles',
price: '/v1/market-data/price'
},
headers: async () => ({ 'X-Tenant-Id': tenantId }),
timeoutMs: 15000
},
websocket: {
enabled: true,
url: 'wss://stream.example.com/v1/market-data',
reconnect: { enabled: true, initialDelayMs: 1000, maxDelayMs: 30000, maxAttempts: 5 }
},
auth: {
type: 'bearer',
token: async () => getCurrentSessionToken(),
applyTo: ['http', 'websocket'],
websocketMode: 'message'
},
security: {
allowedHttpOrigins: ['https://api.example.com'],
allowedWebSocketOrigins: ['wss://stream.example.com']
},
protocol: {
searchDefaults: { default: 'AAPL', crypto: 'BTC' }
}
})Local non-TLS:
typescript
const dataFeed = new DefaultDataFeed({
http: {
baseUrl: 'http://localhost:3000',
endpoints: {
search: '/v1/market-data/symbols',
historical: '/v1/market-data/candles',
price: '/v1/market-data/price'
}
},
websocket: { enabled: true, url: 'ws://localhost:3000/v1/market-data' },
security: { allowInsecureTransport: true }
})typescript
interface DefaultDataFeedConfig {
http: {
baseUrl: string
endpoints: { search: string; historical: string; price?: string }
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)
credentials?: RequestCredentials
timeoutMs?: number
}
websocket?: {
enabled?: boolean
url?: string
reconnect?: { enabled?: boolean; initialDelayMs?: number; maxDelayMs?: number; maxAttempts?: number }
heartbeat?: { enabled?: boolean; intervalMs?: number; timeoutMs?: number }
}
auth?: {
type: 'bearer'
token: string | (() => string | Promise<string>)
applyTo?: Array<'http' | 'websocket'>
websocketMode?: 'message' | 'query'
}
security?: {
allowInsecureTransport?: boolean
allowedHttpOrigins?: string[]
allowedWebSocketOrigins?: string[]
}
protocol?: { searchDefaults?: Record<string, string> }
debug?: boolean
onError?: (error: DefaultDataFeedError) => void
onStatusChange?: (status: DefaultDataFeedStatus) => void
}http
REST transport for search, history, and price.
baseUrl— REST origin (required)endpoints.search/historical/price— paths underbaseUrl(pricedefaults toprice)headers— extra request headers (object or async factory)credentials—fetchcredentials modetimeoutMs— request abort timeout (default15000)
websocket
Live streaming for candles, order book, and trades.
enabled— turn streaming on; defaults totruewhenurlis seturl— WebSocket endpoint (required when enabled)reconnect.enabled— auto-reconnect after unexpected close (defaulttrue)reconnect.initialDelayMs— first reconnect delay (default1000)reconnect.maxDelayMs— backoff cap (default30000)reconnect.maxAttempts— give up after N attempts (default5)heartbeat.enabled— sendping/ expectpong(defaultfalse)heartbeat.intervalMs— ping interval (default30000)heartbeat.timeoutMs— close if nopong(default10000)
auth
Optional bearer auth for HTTP and/or WebSocket.
type— onlybeareris supportedtoken— static string or async token providerapplyTo— attach auth tohttp,websocket, or both (default both)websocketMode—message= auth frame after open (preferred);query= token in URL
security
Transport and origin checks for the configured URLs.
allowInsecureTransport— allowhttp:///ws://(defaultfalse)allowedHttpOrigins— optional allowlist forhttp.baseUrlallowedWebSocketOrigins— optional allowlist forwebsocket.url
Other
Protocol defaults and diagnostics hooks.
protocol.searchDefaults— fallback search query by type when search text is emptydebug— channel logging / verbose console outputonError— config, HTTP, auth, and subscription errorsonStatusChange— WebSocket lifecycle (connected,authenticated,reconnecting, …)
HTTP
Paths are under http.baseUrl. Params are query strings.
Search
Symbol lookup for the picker and watchlists.
GET …/search?query={query}&type={type}
Empty query → protocol.searchDefaults[type] or .default.
typescript
{ data: Array<{ symbol: string; type?: string; name?: string; shortName?: string; exchange?: string; pricePrecision?: number; volumePrecision?: number; priceCurrency?: string; currency?: string; logo?: string }> }Historical
OHLCV bars for the requested interval and time window.
GET …/historical?symbol=&type=&exchange=&interval=&from=&to=
from / to are Unix ms. Return bars in range; client sorts ascending. Malformed bars are dropped. Body may use data or values; time field may be timestamp or datetime.
typescript
{
data?: Array<{ timestamp?: number | string; datetime?: number | string; open: number | string; high: number | string; low: number | string; close: number | string; volume?: number | string; turnover?: number | string }>
values?: /* same as data */
meta?: { symbol?: string; exchange?: string; interval?: string; nextPageToken?: string }
}Price
Latest quote for HTTP watchlists.
GET …/price?symbol=&type=&exchange=
typescript
{ price: number; symbol?: string; exchange?: string; timestamp?: number }Intervals
History and live use the chart interval (no client rebucketing):
1m 3m 5m 15m 30m · 1h 2h 4h 6h 8h 12h · 1d 3d 5d · 1w · 1M 3M 6M · 1y 5y
WebSocket
JSON control frames and market_data pushes over a single socket.
Auth (websocketMode: 'message')
Client → { "event": "authenticate", "data": { "type": "bearer", "token": "…", "clientId": "…", "timestamp": 0 } }
Server → { "event": "auth_success", "data": { "timestamp": 0 } }
Subscribe frames queue until auth_success. On auth_error, the socket closes (no reconnect). Prefer message over query (tokens in URLs may be logged).
Subscribe / unsubscribe
dataType: kline | orderbook | trades. interval is set for kline only.
json
{
"event": "subscribe",
"data": {
"subscriptionId": "cs_kline|BTCUSDT|binance|crypto|BTCUSDT%7Cbinance%7Ccrypto|1m",
"clientId": "client_xxx",
"dataType": "kline",
"symbol": "BTCUSDT",
"type": "crypto",
"exchange": "binance",
"interval": "1m"
}
}json
{
"event": "unsubscribe",
"data": {
"subscriptionId": "cs_kline|BTCUSDT|binance|crypto|BTCUSDT%7Cbinance%7Ccrypto|1m",
"clientId": "client_xxx"
}
}subscriptionId is opaque — echo it on market_data. Format: cs_ + dataType|symbol|exchange|type|identityKey|interval (literal |; each segment URI-encoded).
Market data
Server push for live updates; prefer routing by subscriptionId.
json
{
"event": "market_data",
"data": {
"subscriptionId": "cs_kline|BTCUSDT|binance|crypto|BTCUSDT%7Cbinance%7Ccrypto|1m",
"dataType": "kline",
"symbol": "BTCUSDT",
"type": "crypto",
"exchange": "binance",
"interval": "1m",
"payload": {
"timestamp": 1710000000000,
"open": "100.00",
"high": "101.00",
"low": "99.00",
"close": "100.50",
"volume": "1234.5"
}
}
}Routing prefers subscriptionId; symbol/exchange/interval/type is fallback only.
Shared channels
Same channel → one backend subscribe; local handlers share it. Backend unsubscribe only when the last handler leaves.
- Different exchange, type, or (for
kline) interval → different channel kline,orderbook, andtradesare separate
Diagnostics
Inspect and tear down feed subscriptions.
typescript
dataFeed.debugChannels() // logs when debug: true
dataFeed.getActiveSubscriptions() // backend channel count
dataFeed.unsubscribeAllForChart(id)
dataFeed.closeWebsocket()