Skip to main content

Global Orderbook Module Websocket

Real-time orderbook streaming using Socket.IO. Get live updates from multiple exchanges with aggregated global orderbook view.

Connection

import { io } from 'socket.io-client';

const socket = io('wss://api.renesis.fi', {
  transports: ['websocket'],
  path: '/order-execution/socket.io'
});

Authentication

No authentication required - Orderbook data is publicly available market data.

Connection Keepalive

Socket.IO handles keepalive automatically via ping/pong:
SettingValueDescription
ping_interval25sServer sends ping every 25 seconds
ping_timeout20sConnection dropped if no pong within 20s
No client-side action required - the Socket.IO client library handles ping/pong responses automatically. As long as the connection is open and the network is stable, the connection stays alive. If you need to detect connection health in your app:
socket.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
  // reason: 'ping timeout', 'transport close', 'io server disconnect', etc.
});

socket.io.on('reconnect', (attempt) => {
  console.log('Reconnected after', attempt, 'attempts');
  // Re-subscribe to orderbooks after reconnect
});

Events

Client → Server Events

EventPayloadDescription
subscribe_orderbook{symbol, venues, include_global, depth}Subscribe to orderbook stream
unsubscribe_orderbook{symbol}Unsubscribe from stream
get_snapshot{symbol, venues}Request current snapshot

Server → Client Events

EventPayloadDescription
connected{sid, message}Connection acknowledged
subscribed{symbol, venues, include_global, depth}Subscription confirmed
orderbook_snapshot{symbol, venues, timestamp}Initial full snapshot
orderbook_updatePer-venue book updateReal-time updates (~10-20/sec)
global_bookAggregated best pricesCombined view across venues
error{message}Error notification
unsubscribed{symbol}Unsubscribe confirmed

Subscribing to Orderbooks

Subscribe to Single Exchange

socket.emit('subscribe_orderbook', {
  symbol: 'BTC/USDT',
  venues: ['binance'],        // Single exchange
  include_global: false,      // No aggregation needed
  depth: 10                   // 10 price levels
});

Subscribe to Multiple Exchanges + Global Book

socket.emit('subscribe_orderbook', {
  symbol: 'BTC/USDT',
  venues: ['binance', 'bybit', 'gateio', 'kraken'],
  include_global: true,       // Get aggregated best bid/ask
  depth: 10
});

Subscribe to All Exchanges

socket.emit('subscribe_orderbook', {
  symbol: 'ETH/USDT',
  venues: [],                 // Empty = all available venues
  include_global: true,
  depth: 5
});

Subscription Parameters

ParameterTypeDefaultDescription
symbolstringrequiredTrading pair (e.g., “BTC/USDT”)
venuesstring[][] (all)Filter to specific exchanges
include_globalbooleantrueInclude aggregated global book
depthnumber5Price levels per side (1, 5, 10, or 20)

Supported Venues

VenueUpdate RateNotes
binance~9/secHighest liquidity
bybit~28/secFastest updates
gateio~8/secGood depth
kraken~15/secReliable

Message Formats

Per-Venue Update (orderbook_update)

Sent for each exchange when the orderbook changes:
{
  "venue": "binance",
  "symbol": "BTC/USDT",
  "bids": [
    [88150.50, 1.5],      // [price, quantity]
    [88150.00, 2.3],
    [88149.50, 0.8]
  ],
  "asks": [
    [88151.00, 0.8],
    [88151.50, 1.2],
    [88152.00, 0.5]
  ],
  "spread_bps": 0.57,      // Spread in basis points
  "mid_price": 88150.75,
  "staleness_ms": 15.2,    // How old the data is
  "confidence": 0.98,      // 0-1 reliability score
  "quality": "GOOD",       // GOOD, DEGRADED, STALE, UNAVAILABLE
  "timestamp": "2025-12-19T15:30:00.000000"
}

Global Book (global_book)

Aggregated view showing best prices across all subscribed venues:
{
  "symbol": "BTC/USDT",
  "data": {
    "symbol": "BTC/USDT",
    "timestamp": "2025-12-19T14:56:00.000000",
    "best_bid": {
      "price": 87953.60,
      "quantity": 0.23,
      "venue": "gateio"
    },
    "best_ask": {
      "price": 87949.90,
      "quantity": 0.12,
      "venue": "bybit"
    },
    "venues": {
      "binance": {
        "bid": 87951.30,
        "ask": 87951.31,
        "confidence": 0.95
      },
      "bybit": {
        "bid": 87949.80,
        "ask": 87949.90,
        "confidence": 1.0
      },
      "gateio": {
        "bid": 87953.60,
        "ask": 87953.70,
        "confidence": 0.92
      },
      "kraken": {
        "bid": 87951.30,
        "ask": 87951.40,
        "confidence": 0.98
      }
    }
  },
  "timestamp": "2025-12-19T14:56:00.000000"
}

Initial Snapshot (orderbook_snapshot)

Sent immediately after subscribing:
{
  "symbol": "BTC/USDT",
  "venues": {
    "binance": {
      "venue": "binance",
      "symbol": "BTC/USDT",
      "bids": [[88150.50, 1.5], ...],
      "asks": [[88151.00, 0.8], ...],
      "spread_bps": 0.57,
      "mid_price": 88150.75,
      "staleness_ms": 10.5,
      "confidence": 0.98,
      "quality": "GOOD",
      "timestamp": "2025-12-19T15:30:00.000000"
    },
    "bybit": { ... }
  },
  "timestamp": "2025-12-19T15:30:00.000000"
}

Quality Metrics

Quality Levels

QualityStalenessDescription
GOOD< 500msFresh data, reliable for trading
DEGRADED500ms - 2sSlightly stale, use with caution
STALE2s - 5sOld data, risky for trading
UNAVAILABLE> 5sDo not use

Confidence Score (0.0 - 1.0)

Confidence is calculated based on:
  • Staleness: Up to -0.4 penalty for old data
  • Spread: Up to -0.2 penalty for wide spreads
  • Sequence gaps: -0.05 per gap (max -0.3)
  • If UNAVAILABLE: confidence = 0
Recommendation: Only trade when confidence > 0.7

Complete Example (React)

import { useEffect, useState, useRef } from 'react';
import { io } from 'socket.io-client';

function OrderbookWidget({ symbol }) {
  const [orderbook, setOrderbook] = useState(null);
  const [globalBook, setGlobalBook] = useState(null);
  const [connected, setConnected] = useState(false);
  const socketRef = useRef(null);

  useEffect(() => {
    // Connect to WebSocket
    const socket = io('wss://api.renesis.fi', {
      transports: ['websocket'],
      path: '/order-execution/socket.io'
    });
    socketRef.current = socket;

    socket.on('connect', () => {
      console.log('Connected to orderbook stream');
      setConnected(true);
    });

    socket.on('connected', (data) => {
      console.log('Server acknowledged:', data.message);
    });

    socket.on('subscribed', (data) => {
      console.log(`Subscribed to ${data.symbol}`);
    });

    socket.on('orderbook_update', (data) => {
      // Update orderbook state for specific venue
      setOrderbook(prev => ({
        ...prev,
        [data.venue]: data
      }));
    });

    socket.on('global_book', (data) => {
      // Update global aggregated view
      setGlobalBook(data.data);
    });

    socket.on('orderbook_snapshot', (data) => {
      // Initialize with full snapshot
      setOrderbook(data.venues);
    });

    socket.on('error', (data) => {
      console.error('Orderbook error:', data.message);
    });

    socket.on('disconnect', () => {
      setConnected(false);
    });

    return () => {
      socket.disconnect();
    };
  }, []);

  // Subscribe when symbol changes
  useEffect(() => {
    if (connected && socketRef.current) {
      // Unsubscribe from previous symbol
      socketRef.current.emit('unsubscribe_orderbook', { symbol });

      // Subscribe to new symbol
      socketRef.current.emit('subscribe_orderbook', {
        symbol,
        venues: ['binance', 'bybit', 'gateio'],
        include_global: true,
        depth: 10
      });
    }
  }, [symbol, connected]);

  if (!connected) {
    return <div>Connecting...</div>;
  }

  return (
    <div>
      {/* Global Best Prices */}
      {globalBook && (
        <div className="global-book">
          <h3>Best Prices</h3>
          <div>
            Best Bid: ${globalBook.best_bid?.price}
            @ {globalBook.best_bid?.venue}
          </div>
          <div>
            Best Ask: ${globalBook.best_ask?.price}
            @ {globalBook.best_ask?.venue}
          </div>
        </div>
      )}

      {/* Per-Venue Orderbooks */}
      {orderbook && Object.entries(orderbook).map(([venue, data]) => (
        <div key={venue} className="venue-book">
          <h4>{venue} ({data.quality})</h4>
          <div className="bids">
            {data.bids?.slice(0, 5).map(([price, qty], i) => (
              <div key={i}>{price} - {qty}</div>
            ))}
          </div>
          <div className="asks">
            {data.asks?.slice(0, 5).map(([price, qty], i) => (
              <div key={i}>{price} - {qty}</div>
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

export default OrderbookWidget;

Unsubscribing

// Unsubscribe from specific symbol
socket.emit('unsubscribe_orderbook', { symbol: 'BTC/USDT' });

// Disconnect completely
socket.disconnect();

Error Handling

socket.on('error', (data) => {
  console.error('Orderbook error:', data.message);
  // Common errors:
  // - "symbol is required"
  // - "Invalid venue"
  // - "Subscription failed"
});

socket.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
  // Implement reconnection logic if needed
});

Performance Tips

  1. Limit depth - Use depth: 5 unless you need more levels
  2. Filter venues - Only subscribe to venues you’ll display
  3. Throttle UI updates - Don’t re-render on every update (10-20/sec)
  4. Use include_global: false if you only need raw venue data
  5. Unsubscribe when components unmount

REST Fallback

If WebSocket is unavailable, use REST endpoints:
# Get current orderbook
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.renesis.fi/order-execution/orderbook/book?symbol=BTC/USDT"

# Get quality metrics
curl -H "Authorization: Bearer $TOKEN" \
  "https://api.renesis.fi/order-execution/orderbook/quality?symbol=BTC/USDT"
See Orderbook REST API for full REST documentation.