Skip to content

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

  • DefaultDataFeed combines:
    • HTTP for searchSymbols, getHistoricalTimeSeries, getPrice
    • WebSocket for live kline, orderbook, and trades
  • 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)

typescript
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:3000
  • endpoints.search|historical|websocket|orderBook|price: ''
  • headers: { 'Content-Type': 'application/json' }
  • websocket.enabled: true
  • websocket.reconnectInterval: 5000
  • websocket.maxReconnectAttempts: 3
  • debug: false

Example

typescript
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.

GET {baseUrl}/{search}/{type}?symbol={query}

Response:

typescript
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:

typescript
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:

typescript
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, 30m
  • 1h, 2h, 4h, 6h, 8h, 12h
  • 1d, 3d, 5d
  • 1w
  • 1M, 3M, 6M
  • 1y, 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 as wss://...
  • 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:

json
{
  "event": "authenticate",
  "data": {
    "token": "jwt-token",
    "clientId": "client_xxx",
    "timestamp": 1700000000000
  }
}

Expected events:

  • auth_success
  • auth_error

Subscribe/unsubscribe

Outgoing subscribe (for live chart streams, use interval: "1m"):

json
{
  "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

typescript
dataFeed.debugChannels()
const count = dataFeed.getActiveSubscriptions()
  • debugChannels() logs active channel keys and handlers
  • getActiveSubscriptions() returns active kline channel count plus active order book callback count