Skip to content

Data Access

ChartSpire provides a flexible data feed system that allows you to connect to any financial data provider. You can use the built-in demo data feeds for quick setup, configure the default data feed for production use, or implement custom data feeds for specialized requirements.

Overview

The data feed system handles:

  • Symbol search - Finding and filtering tradable instruments
  • Historical data - Loading candlestick/OHLCV data for charts
  • Real-time updates - Live price and volume updates via WebSocket or polling
  • Order book data - Market depth information (optional)
  • Current prices - Latest price information for watchlists and UI

Built-in Data Feeds

ChartSpire includes demo data feeds for quick testing and prototyping:

Default DataFeed

Consider using our Default DataFeed which provides a comprehensive, configurable solution that can connect to multiple data providers with built-in caching, error handling, and optimization features.

Binance Data Feed (Crypto)

The Binance data feed provides cryptocurrency data without requiring an API key. This is the same data feed used in the live demo.

typescript
import { ChartSpire, BinanceDataFeed, SYMBOL_TYPE } from 'chartspire'

// Using Binance data feed (no API key needed)
const container = document.getElementById('chart-container')!

const chart = new ChartSpire({
  container,
  rootContainer: container,
  enabledSymbolTypes: [SYMBOL_TYPE.CRYPTO],
  symbol: {
    symbol: 'BTCUSDT',
    type: SYMBOL_TYPE.CRYPTO
  },
  interval: { multiplier: 1, timespan: 'day', text: '1D' },
  dataFeed: new BinanceDataFeed(),
})

Features:

  • Real-time price updates via WebSocket
  • Historical data for 1000+ cryptocurrency pairs
  • No API key or authentication required
  • Supports multiple timeframes (1m, 5m, 15m, 1h, 4h, 1d, etc.)

Limitations:

  • Cryptocurrency data only
  • Rate limited by Binance public API
  • Not suitable for production trading applications

Implementing Custom Data Feeds

⚠️ Warning: Advanced Implementation Required

Implementing custom data feeds requires advanced knowledge of WebSocket management, subscription optimization, and multi-component coordination. We strongly recommend using the Default DataFeed as it handles all this logic unless you have specific requirements that cannot be met by the default implementation.

Complexity Considerations

Custom data feed implementations must handle:

  • Subscription Reference Counting: Multiple components (multicharts, watchlists, order book, trades) may subscribe to the same symbol. You must track subscription counts and only unsubscribe from the WebSocket when no components are listening.
  • WebSocket Connection Management: Efficiently manage WebSocket connections, including connection pooling, reconnection logic, and graceful degradation.
  • Cross-Component Coordination: Handle scenarios where:
    • Multiple charts subscribe to the same symbol/interval
    • Watchlists need price updates for dozens of symbols
    • Order book requires high-frequency market depth data
    • Trade feeds need real-time execution data
    • All components may subscribe/unsubscribe independently
  • Memory Leak Prevention: Properly clean up subscriptions when components are destroyed to prevent memory leaks and ghost subscriptions.
  • Performance Optimization: Implement efficient data distribution to avoid duplicate API calls and minimize bandwidth usage.

DataFeed Interface

Only proceed with custom implementation if you have advanced WebSocket management experience. All data feeds must implement the DataFeed interface. Here's a complete implementation template:

typescript
**
 * Interface representing a data feed for financial data.
 */
export interface DataFeed {
	/**
	 * Searches for symbols based on a search string and optional type.
	 * @param search - The search string.
	 * @param type - The optional type of symbol.
	 * @returns A promise that resolves to an array of SymbolInfo objects.
	 */
	searchSymbols: (search?: string, type?: string) => Promise<Symbol[]>

	/**
	 * Retrieves historical time series data for a given symbol and period.
	 * @param symbol - The symbol information.
	 * @param period - The period for the time series data.
	 * @param from - The start timestamp.
	 * @param to - The end timestamp.
	 * @param type - The optional type of symbol.
	 * @returns A promise that resolves to an array of CandleStickData objects.
	 */
	getHistoricalTimeSeries: (symbol: Symbol, period: Interval, from: number, to: number, type?: string | null) => Promise<CandleStickData[]>

	/**
	 * Retrieves the current price for a given symbol.
	 * @param symbol - The symbol information.
	 * @param type - The optional type of symbol.
	 * @returns A promise that resolves to the current price or null if unavailable.
	 */
	getPrice: (symbol: Symbol, type?: string | null) => Promise<number | null>

	/**
	 * Subscribes to real-time data updates for a given symbol and period.
	 * @param symbol - The symbol information.
	 * @param period - The period for the data updates.
	 * @param callback - The callback function to handle data updates.
	 * @param subscriberUID - The optional unique identifier for the subscription (renamed from uuid for consistency).
	 */
	subscribeSymbol: (symbol: Symbol, period: Interval, callback: DataFeedSubscribeSymbolCallback, subscriberUID?: string) => void

	/**
	 * Unsubscribes from real-time data updates for a given symbol and period.
	 * @param symbol - The symbol information.
	 * @param period - The period for the data updates.
	 * @param subscriberUID - The optional unique identifier for the subscription (renamed from uuid for consistency).
	 */
	unsubscribeSymbol: (symbol: Symbol, period: Interval, subscriberUID?: string) => void

	/**
	 * Unsubscribes a specific chart from ALL channels it's subscribed to.
	 * This is used during chart cleanup to ensure no ghost subscriptions remain.
	 * Critical for multi-chart implementations.
	 * @param chartId - The unique identifier of the chart to clean up.
	 */
	unsubscribeAllForChart: (chartId: string) => void // todo use chartID

	/**
	 * Subscribes to real-time order book (DOM) data for a given symbol.
	 * @param symbol - The symbol information.
	 * @param callback - The callback function to handle order book updates.
	 * @param uuid - The optional unique identifier for the subscription.
	 */
	subscribeOrderBookData?: (symbol: Symbol, callback: OrderDataCallback, uuid?: string) => void

	/**
	 * Unsubscribes from real-time order book (DOM) data for a given symbol.
	 * @param symbol - The symbol information.
	 * @param uuid - The optional unique identifier for the subscription.
	 */
	unsubscribeOrderBookData?: (symbol: Symbol, uuid?: string) => void

	/**
	 * Subscribes to real-time trade data for a given symbol.
	 * @param symbol - The symbol information.
	 * @param callback - The callback function to handle trade updates.
	 * @param uuid - The optional unique identifier for the subscription.
	 */
	subscribeTradeData?: (symbol: Symbol, callback: TradeDataCallback, uuid?: string) => void

	/**
	 * Unsubscribes from real-time trade data for a given symbol.
	 * @param symbol - The symbol information.
	 * @param uuid - The optional unique identifier for the subscription.
	 */
	unsubscribeTradeData?: (symbol: Symbol, uuid?: string) => void

	/**
	 * Closes all websockets.
	 */
	closeWebsocket: () => void

	/**
	 * Clears all event listeners for the data feed.
	 */
	clearAllListeners: () => void

	/**
	 * Debug method to log all active channels and handlers.
	 * Useful for debugging subscription issues and understanding data flow.
	 */
	debugChannels?: () => void

	/**
	 * Returns the number of active subscriptions.
	 */
	getActiveSubscriptions: () => number
}

/**
 * Type representing a callback function for data feed subscriptions.
 * @param data - The CandleStickData object containing the data update.
 */
export type DataFeedSubscribeSymbolCallback = (data: CandleStickData, symbol?: Symbol) => void

/**
 * Type representing a callback function for order book data subscriptions.
 * @param data - The OrderBookData object containing the order book update.
 */
export type OrderDataCallback = (data: OrderBookData, symbol?: Symbol) => void

/**
 * Type representing a callback function for trade data subscriptions.
 * @param data - The TradeDataCollection object containing the trade update.
 */
export type TradeDataCallback = (data: TradeDataCollection, symbol?: Symbol) => void

Implementation Guidelines

When implementing custom data feeds, these advanced considerations are critical:

Subscription Reference Counting (Critical)

typescript
class AdvancedDataFeed implements DataFeed {
  private subscriptionCounts = new Map<string, number>()
  private callbacks = new Map<string, Set<DataFeedSubscribeSymbolCallback>>()

  subscribeSymbol(symbol: Symbol, period: Interval, callback: DataFeedSubscribeSymbolCallback): void {
    const key = `${symbol.symbol}-${period.text}`
    
    // Track subscription count
    const currentCount = this.subscriptionCounts.get(key) || 0
    this.subscriptionCounts.set(key, currentCount + 1)
    
    // Store callback
    if (!this.callbacks.has(key)) {
      this.callbacks.set(key, new Set())
    }
    this.callbacks.get(key)!.add(callback)
    
    // Only subscribe to WebSocket if this is the first subscription
    if (currentCount === 0) {
      this.subscribeToWebSocket(key)
    }
  }

  unsubscribeSymbol(symbol: Symbol, period: Interval, subscriberUID?: string): void {
    const key = `${symbol.symbol}-${period.text}`
    const currentCount = this.subscriptionCounts.get(key) || 0
    
    if (currentCount > 1) {
      // Other components still need this data
      this.subscriptionCounts.set(key, currentCount - 1)
    } else {
      // Last subscriber - safe to unsubscribe from WebSocket
      this.subscriptionCounts.delete(key)
      this.callbacks.delete(key)
      this.unsubscribeFromWebSocket(key)
    }
  }
}

Multi-Component Coordination

  • Watchlists: May subscribe to 50+ symbols simultaneously for price updates
  • Multicharts: Multiple charts may request the same symbol/interval combination
  • Order Book: Requires high-frequency market depth data that differs from price data
  • Trade Feeds: Need real-time execution data separate from price feeds
  • Paper Trading: May need order book data for realistic execution simulation

WebSocket Connection Management

  • Connection Pooling: Reuse connections across multiple subscriptions
  • Heartbeat/Ping-Pong: Implement keep-alive mechanisms to prevent connection drops
  • Graceful Reconnection: Handle connection failures without losing subscription state

Memory Leak Prevention

  • Component Cleanup: Implement unsubscribeAllForChart() to clean up when charts are destroyed
  • Garbage Collection: Remove unused callbacks and subscriptions
  • Event Listener Management: Properly remove WebSocket event listeners

Error Handling

  • Implement retry logic with exponential backoff for failed requests
  • Handle rate limiting gracefully with appropriate delays
  • Provide fallback data when primary data source is unavailable
  • Log errors for debugging and monitoring

Real-time Updates

  • Implement reconnection logic for dropped connections
  • Handle subscription management to avoid memory leaks
  • Throttle updates if receiving high-frequency data
  • Batch data distribution to multiple callbacks efficiently

Data Validation

  • Validate incoming data before passing to ChartSpire
  • Handle missing or malformed data gracefully
  • Ensure timestamp consistency across different data sources
  • Convert data formats to match ChartSpire's expected structure

Using a Custom Data Feed

After implementing your custom data feed, you can use it with ChartSpire:

typescript
import { ChartSpire, SYMBOL_TYPE } from 'chartspire'
import { CustomDataFeed } from './CustomDataFeed'

const container = document.getElementById('chart-container')!

const chart = new ChartSpire({
  container,
  rootContainer: container,
  enabledSymbolTypes: [SYMBOL_TYPE.STOCKS],
  symbol: {
    symbol: 'CUSTOM_SYMBOL',
    type: SYMBOL_TYPE.STOCKS
  },
  interval: { multiplier: 1, timespan: 'day', text: '1D' },
  dataFeed: new CustomDataFeed(),
  // Add other configuration parameters as needed
})

Data Interfaces

Callback Types

typescript
/**
 * Type representing a callback function for data feed subscriptions.
 */
export type DataFeedSubscribeSymbolCallback = (data: CandleStickData, symbol?: Symbol) => void

/**
 * Type representing a callback function for order book data subscriptions.
 */
export type OrderDataCallback = (data: OrderBookData, symbol?: Symbol) => void

/**
 * Type representing a callback function for trade data subscriptions.
 */
export type TradeDataCallback = (data: TradeDataCollection, symbol?: Symbol) => void