Skip to content

Real-Time WebSocket

Connect TradeX Chart to live data feeds using WebSocket for real-time updates.

Basic WebSocket Connection

class RealtimeChart {
  constructor(chart, symbol) {
    this.chart = chart
    this.symbol = symbol
    this.ws = null
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 5
  }

  connect() {
    this.ws = new WebSocket('wss://api.example.com/ws')

    this.ws.onopen = () => {
      console.log('WebSocket connected')
      this.reconnectAttempts = 0
      this.subscribe()
    }

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data)
      this.handleMessage(data)
    }

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error)
    }

    this.ws.onclose = () => {
      console.log('WebSocket closed')
      this.reconnect()
    }
  }

  subscribe() {
    this.ws.send(JSON.stringify({
      type: 'subscribe',
      symbol: this.symbol,
      channel: 'candles'
    }))
  }

  handleMessage(data) {
    if (data.type === 'candle') {
      const candle = [
        data.timestamp,
        data.open,
        data.high,
        data.low,
        data.close,
        data.volume
      ]

      if (data.isClosed) {
        this.chart.addCandle(candle)
      } else {
        this.chart.updateStreamingCandle(candle)
      }
    }
  }

  reconnect() {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++
      const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000)
      
      console.log(`Reconnecting in ${delay}ms...`)
      setTimeout(() => this.connect(), delay)
    } else {
      console.error('Max reconnection attempts reached')
    }
  }

  disconnect() {
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }
  }
}

// Usage
const chart = document.getElementById('chart')
const realtimeChart = new RealtimeChart(chart, 'BTCUSDT')
realtimeChart.connect()

Binance WebSocket Integration

class BinanceRealtimeChart {
  constructor(chart, symbol, interval = '1m') {
    this.chart = chart
    this.symbol = symbol.toLowerCase()
    this.interval = interval
    this.ws = null
  }

  connect() {
    const wsUrl = `wss://stream.binance.com:9443/ws/${this.symbol}@kline_${this.interval}`
    this.ws = new WebSocket(wsUrl)

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data)
      
      if (data.e === 'kline') {
        const k = data.k
        const candle = [
          k.t,                    // timestamp
          parseFloat(k.o),        // open
          parseFloat(k.h),        // high
          parseFloat(k.l),        // low
          parseFloat(k.c),        // close
          parseFloat(k.v)         // volume
        ]

        if (k.x) {
          // Candle is closed
          this.chart.addCandle(candle)
        } else {
          // Candle is still forming
          this.chart.updateStreamingCandle(candle)
        }
      }
    }

    this.ws.onerror = (error) => {
      console.error('Binance WebSocket error:', error)
    }

    this.ws.onclose = () => {
      console.log('Binance WebSocket closed')
      setTimeout(() => this.connect(), 3000)
    }
  }

  disconnect() {
    if (this.ws) {
      this.ws.close()
    }
  }
}

// Usage
const chart = document.getElementById('chart')
const binanceChart = new BinanceRealtimeChart(chart, 'BTCUSDT', '1m')
binanceChart.connect()

Multi-Symbol WebSocket

class MultiSymbolWebSocket {
  constructor() {
    this.ws = null
    this.subscriptions = new Map()
    this.callbacks = new Map()
  }

  connect() {
    this.ws = new WebSocket('wss://api.example.com/ws')

    this.ws.onopen = () => {
      console.log('Connected')
      // Resubscribe to all symbols
      this.subscriptions.forEach((_, symbol) => {
        this.sendSubscribe(symbol)
      })
    }

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data)
      const callbacks = this.callbacks.get(data.symbol)
      
      if (callbacks) {
        callbacks.forEach(callback => callback(data))
      }
    }

    this.ws.onclose = () => {
      console.log('Disconnected')
      setTimeout(() => this.connect(), 3000)
    }
  }

  subscribe(symbol, callback) {
    if (!this.subscriptions.has(symbol)) {
      this.subscriptions.set(symbol, true)
      this.sendSubscribe(symbol)
    }

    if (!this.callbacks.has(symbol)) {
      this.callbacks.set(symbol, [])
    }
    this.callbacks.get(symbol).push(callback)
  }

  unsubscribe(symbol, callback) {
    const callbacks = this.callbacks.get(symbol)
    if (callbacks) {
      const index = callbacks.indexOf(callback)
      if (index > -1) {
        callbacks.splice(index, 1)
      }

      if (callbacks.length === 0) {
        this.callbacks.delete(symbol)
        this.subscriptions.delete(symbol)
        this.sendUnsubscribe(symbol)
      }
    }
  }

  sendSubscribe(symbol) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({
        type: 'subscribe',
        symbol: symbol
      }))
    }
  }

  sendUnsubscribe(symbol) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({
        type: 'unsubscribe',
        symbol: symbol
      }))
    }
  }
}

// Usage
const wsManager = new MultiSymbolWebSocket()
wsManager.connect()

// Subscribe multiple charts
const chart1 = document.getElementById('chart1')
wsManager.subscribe('BTCUSDT', (data) => {
  chart1.updateStreamingCandle(data.candle)
})

const chart2 = document.getElementById('chart2')
wsManager.subscribe('ETHUSDT', (data) => {
  chart2.updateStreamingCandle(data.candle)
})

Heartbeat & Reconnection

class RobustWebSocket {
  constructor(url, options = {}) {
    this.url = url
    this.options = {
      heartbeatInterval: 30000,
      reconnectDelay: 3000,
      maxReconnectDelay: 30000,
      reconnectDecay: 1.5,
      ...options
    }
    
    this.ws = null
    this.heartbeatTimer = null
    this.reconnectTimer = null
    this.reconnectAttempts = 0
    this.messageHandlers = []
  }

  connect() {
    this.ws = new WebSocket(this.url)

    this.ws.onopen = () => {
      console.log('WebSocket connected')
      this.reconnectAttempts = 0
      this.startHeartbeat()
      this.onOpen?.()
    }

    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data)
      
      // Handle pong
      if (data.type === 'pong') {
        return
      }

      // Call all message handlers
      this.messageHandlers.forEach(handler => handler(data))
    }

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error)
      this.onError?.(error)
    }

    this.ws.onclose = () => {
      console.log('WebSocket closed')
      this.stopHeartbeat()
      this.scheduleReconnect()
      this.onClose?.()
    }
  }

  startHeartbeat() {
    this.heartbeatTimer = setInterval(() => {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: 'ping' }))
      }
    }, this.options.heartbeatInterval)
  }

  stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer)
      this.heartbeatTimer = null
    }
  }

  scheduleReconnect() {
    if (this.reconnectTimer) return

    const delay = Math.min(
      this.options.reconnectDelay * Math.pow(this.options.reconnectDecay, this.reconnectAttempts),
      this.options.maxReconnectDelay
    )

    console.log(`Reconnecting in ${delay}ms...`)
    this.reconnectAttempts++

    this.reconnectTimer = setTimeout(() => {
      this.reconnectTimer = null
      this.connect()
    }, delay)
  }

  send(data) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data))
    } else {
      console.warn('WebSocket not connected')
    }
  }

  onMessage(handler) {
    this.messageHandlers.push(handler)
  }

  disconnect() {
    this.stopHeartbeat()
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }
  }
}

// Usage
const ws = new RobustWebSocket('wss://api.example.com/ws')

ws.onOpen = () => {
  ws.send({ type: 'subscribe', symbol: 'BTCUSDT' })
}

ws.onMessage((data) => {
  if (data.type === 'candle') {
    chart.updateStreamingCandle(data.candle)
  }
})

ws.connect()

Complete Example with React

import { useEffect, useRef, useState } from 'react'

function RealtimeChart({ symbol, interval }) {
  const chartRef = useRef(null)
  const wsRef = useRef(null)
  const [connected, setConnected] = useState(false)
  const [lastUpdate, setLastUpdate] = useState(null)

  useEffect(() => {
    const chart = chartRef.current
    if (!chart) return

    // Connect WebSocket
    const wsUrl = `wss://stream.binance.com:9443/ws/${symbol.toLowerCase()}@kline_${interval}`
    wsRef.current = new WebSocket(wsUrl)

    wsRef.current.onopen = () => {
      setConnected(true)
    }

    wsRef.current.onmessage = (event) => {
      const data = JSON.parse(event.data)
      
      if (data.e === 'kline') {
        const k = data.k
        const candle = [
          k.t,
          parseFloat(k.o),
          parseFloat(k.h),
          parseFloat(k.l),
          parseFloat(k.c),
          parseFloat(k.v)
        ]

        if (k.x) {
          chart.addCandle(candle)
        } else {
          chart.updateStreamingCandle(candle)
        }

        setLastUpdate(new Date())
      }
    }

    wsRef.current.onclose = () => {
      setConnected(false)
    }

    return () => {
      if (wsRef.current) {
        wsRef.current.close()
      }
    }
  }, [symbol, interval])

  return (
    <div>
      <div className="status">
        Status: {connected ? '🟢 Connected' : '🔴 Disconnected'}
        {lastUpdate && ` | Last update: ${lastUpdate.toLocaleTimeString()}`}
      </div>
      <tradex-chart ref={chartRef} />
    </div>
  )
}

export default RealtimeChart