Strategy Intermediate

WebSocket Real-time Trading Monitor System Development

Sentinel Team · 2026-03-09
WebSocket Real-time Trading Monitor System Development

WebSocket Real-time Trading Monitor System Development

Quick Navigation: This article deeply explores the complete application of WebSocket in trading systems, from Socket.io architecture design to React integration, providing an authoritative guide for cryptocurrency real-time monitoring system development. Estimated reading time: 15 minutes.


Why Do Trading Systems Need WebSocket?

In the world of automated trading, time is money. When Bitcoin prices fluctuate in milliseconds, the latency of HTTP polling can mean missed trading opportunities or unnecessary risks.

According to the WebSocket RFC 6455 specification, WebSocket provides full-duplex, low-latency communication channels, the technical foundation of real-time trading systems.

HTTP Polling vs WebSocket Comparison

FeatureHTTP PollingWebSocketImpact on Trading Systems
Connection MethodNew connection per requestSingle long connectionReduced connection overhead
Latency100-500ms10-50ms10x improvement
Server PushNot supportedNative supportReal-time market updates
Bandwidth UsageHigh (repeated HTTP headers)Low (data only)Save 70%+ bandwidth
Real-timePseudo real-timeTrue real-timeCritical trading timing
ScalabilityPoor (connection limited)Good (supports 10K+ connections)Support multi-user

Key Insight: Professional-grade trading systems must use WebSocket. HTTP polling is only suitable for low-frequency data (like position reports updated once per minute).


WebSocket Core Concepts and Protocol

WebSocket Handshake Process

Client                          Server
   │                              │
   │  1. HTTP Upgrade Request      │
   │ ───────────────────────────> │
   │  GET /ws HTTP/1.1            │
   │  Upgrade: websocket          │
   │  Connection: Upgrade         │
   │  Sec-WebSocket-Key: xxx      │
   │                              │
   │  2. Protocol Upgrade Response │
   │ <─────────────────────────── │
   │  HTTP/1.1 101 Switching      │
   │  Upgrade: websocket          │
   │  Sec-WebSocket-Accept: yyy   │
   │                              │
   │  3. WebSocket Connection Established │
   │ <══════════════════════════> │
   │      Full-duplex Data Transmission   │

Advantages of Socket.io

While native WebSocket is already powerful, Socket.io provides additional features needed in production environments:

FeatureNative WebSocketSocket.ioValue for Trading Systems
Auto Reconnect❌ Manual implementation✅ Built-inAuto recovery when network unstable
Fallback❌ None✅ HTTP long pollingOld browser/firewall compatibility
Room Mechanism❌ Manual implementation✅ Built-inTrading pair subscription channels
Broadcast❌ Manual implementation✅ Built-inSystem announcement push
Middleware❌ Manual implementation✅ Built-inAuth and permission control
Binary Support✅ Supported✅ SupportedEfficient data transmission

Trading System WebSocket Architecture Design

Overall Architecture Diagram

┌─────────────────────────────────────────────────────────────┐
│                        Client (React)                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │ PriceFeed   │  │ BotMonitor  │  │  NotificationCenter │  │
│  │   Hook      │  │    Hook     │  │       Hook          │  │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │
│         │                │                    │             │
│         └────────────────┼────────────────────┘             │
│                          │                                   │
│                   ┌──────┴──────┐                           │
│                   │ Socket.io   │                           │
│                   │   Client    │                           │
│                   └──────┬──────┘                           │
└──────────────────────────┼──────────────────────────────────┘
                           │ WebSocket
┌──────────────────────────┼──────────────────────────────────┐
│                     Server (Node.js)                         │
│                   ┌──────┴──────┐                           │
│                   │ Socket.io   │                           │
│                   │   Server    │                           │
│                   └──────┬──────┘                           │
│         ┌────────────────┼────────────────┐                 │
│         │                │                │                 │
│    ┌────┴────┐     ┌────┴────┐     ┌────┴────┐            │
│    │ Price   │     │  Bot    │     │ Notification│         │
│    │ Service │     │ Service │     │   Service   │         │
│    └────┬────┘     └────┬────┘     └─────┬─────┘          │
│         │               │                │                 │
│    ┌────┴────┐     ┌────┴────┐     ┌────┴────┐            │
│    │ Redis   │     │ PostgreSQL│    │  Firebase  │         │
│    │ Pub/Sub │     │  (State)  │    │  (Push)   │         │
│    └─────────┘     └─────────┘    └───────────┘          │
└─────────────────────────────────────────────────────────────┘

Channel Design Principles

// Channel naming conventions
const CHANNELS = {
  // Public channels (no auth required)
  PUBLIC: {
    PRICES: 'price:public',           // Public market data
    ANNOUNCEMENTS: 'announcement',     // System announcements
  },
  
  // User private channels (auth required)
  USER: {
    NOTIFICATIONS: (userId: string) => `user:${userId}:notifications`,
    BALANCE: (userId: string) => `user:${userId}:balance`,
  },
  
  // Bot channels (authorization required)
  BOT: {
    STATUS: (botId: string) => `bot:${botId}:status`,
    TRADES: (botId: string) => `bot:${botId}:trades`,
    POSITIONS: (botId: string) => `bot:${botId}:positions`,
    LOGS: (botId: string) => `bot:${botId}:logs`,
  },
  
  // Trading pair channels (dynamic subscription)
  SYMBOL: {
    TICKER: (symbol: string) => `symbol:${symbol}:ticker`,
    ORDERBOOK: (symbol: string) => `symbol:${symbol}:orderbook`,
    TRADES: (symbol: string) => `symbol:${symbol}:trades`,
  },
};

Socket.io Server Implementation

Basic Architecture

// server/websocket/index.ts
import { Server } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';

export function createWebSocketServer(httpServer) {
  const io = new Server(httpServer, {
    cors: {
      origin: process.env.FRONTEND_URL,
      credentials: true,
    },
    // Performance tuning
    pingTimeout: 60000,
    pingInterval: 25000,
    transports: ['websocket', 'polling'],
  });
  
  // Redis adapter (multi-server scaling)
  const pubClient = createClient({ url: process.env.REDIS_URL });
  const subClient = pubClient.duplicate();
  io.adapter(createAdapter(pubClient, subClient));
  
  // Middleware: Authentication
  io.use(async (socket, next) => {
    try {
      const token = socket.handshake.auth.token;
      const user = await verifyToken(token);
      socket.data.user = user;
      next();
    } catch (err) {
      next(new Error('Authentication error'));
    }
  });
  
  // Connection handling
  io.on('connection', (socket) => {
    console.log(`Client connected: ${socket.id}`);
    
    // Register event handlers
    registerPriceHandlers(socket);
    registerBotHandlers(socket);
    registerNotificationHandlers(socket);
    
    // Disconnect handling
    socket.on('disconnect', (reason) => {
      console.log(`Client disconnected: ${socket.id}, reason: ${reason}`);
    });
  });
  
  return io;
}

Price Push Service

// server/websocket/handlers/price.ts
export function registerPriceHandlers(socket: Socket) {
  // User subscribes to specific trading pairs
  socket.on('price:subscribe', (symbols: string[]) => {
    symbols.forEach((symbol) => {
      socket.join(`symbol:${symbol}:ticker`);
    });
    
    // Immediately push current prices
    symbols.forEach(async (symbol) => {
      const price = await getLatestPrice(symbol);
      socket.emit('price:update', { symbol, ...price });
    });
  });
  
  // Unsubscribe
  socket.on('price:unsubscribe', (symbols: string[]) => {
    symbols.forEach((symbol) => {
      socket.leave(`symbol:${symbol}:ticker`);
    });
  });
}

// Broadcast when external price service pushes
export function broadcastPriceUpdate(io: Server, symbol: string, data: PriceData) {
  io.to(`symbol:${symbol}:ticker`).emit('price:update', {
    symbol,
    timestamp: Date.now(),
    ...data,
  });
}

Bot Monitoring Service

// server/websocket/handlers/bot.ts
export function registerBotHandlers(socket: Socket) {
  const userId = socket.data.user.id;
  
  // Subscribe to all user's bots
  socket.on('bot:subscribe', async () => {
    const bots = await getUserBots(userId);
    
    bots.forEach((bot) => {
      socket.join(`bot:${bot.id}:status`);
      socket.join(`bot:${bot.id}:trades`);
      socket.join(`bot:${bot.id}:positions`);
    });
    
    socket.emit('bot:subscribed', bots.map((b) => b.id));
  });
  
  // Subscribe to specific bot
  socket.on('bot:subscribe:one', async (botId: string) => {
    // Permission check
    const hasAccess = await checkBotAccess(userId, botId);
    if (!hasAccess) {
      socket.emit('error', { message: 'Access denied' });
      return;
    }
    
    socket.join(`bot:${botId}:status`);
    socket.join(`bot:${botId}:trades`);
    socket.join(`bot:${botId}:positions`);
    socket.join(`bot:${botId}:logs`);
    
    // Push current status
    const status = await getBotStatus(botId);
    socket.emit('bot:status', { botId, ...status });
  });
}

// Broadcast when bot status changes
export function broadcastBotStatus(io: Server, botId: string, status: BotStatus) {
  io.to(`bot:${botId}:status`).emit('bot:status', {
    botId,
    timestamp: Date.now(),
    ...status,
  });
}

// Broadcast on new trade
export function broadcastBotTrade(io: Server, botId: string, trade: Trade) {
  io.to(`bot:${botId}:trades`).emit('bot:trade', {
    botId,
    timestamp: Date.now(),
    trade,
  });
}

React Client Integration

WebSocket Manager Encapsulation

// src/api/websocket.ts
import { io, Socket } from 'socket.io-client';
import { useEffect, useRef, useCallback } from 'react';

class WebSocketManager {
  private socket: Socket | null = null;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;
  private listeners: Map<string, Set<Function>> = new Map();
  
  connect(token: string) {
    if (this.socket?.connected) return;
    
    this.socket = io(process.env.VITE_WS_URL, {
      auth: { token },
      transports: ['websocket', 'polling'],
      reconnection: true,
      reconnectionAttempts: this.maxReconnectAttempts,
      reconnectionDelay: 1000,
      reconnectionDelayMax: 5000,
    });
    
    this.socket.on('connect', () => {
      console.log('WebSocket connected');
      this.reconnectAttempts = 0;
    });
    
    this.socket.on('disconnect', (reason) => {
      console.log('WebSocket disconnected:', reason);
      if (reason === 'io server disconnect') {
        // Server-initiated disconnect, manual reconnect needed
        setTimeout(() => this.connect(token), 1000);
      }
    });
    
    this.socket.on('error', (error) => {
      console.error('WebSocket error:', error);
    });
    
    // Re-register all listeners
    this.listeners.forEach((callbacks, event) => {
      callbacks.forEach((callback) => {
        this.socket?.on(event, callback);
      });
    });
  }
  
  disconnect() {
    this.socket?.disconnect();
    this.socket = null;
  }
  
  subscribe(event: string, callback: Function) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
      this.socket?.on(event, callback);
    }
    this.listeners.get(event)!.add(callback);
    
    return () => this.unsubscribe(event, callback);
  }
  
  unsubscribe(event: string, callback: Function) {
    this.listeners.get(event)?.delete(callback);
    this.socket?.off(event, callback);
  }
  
  emit(event: string, data?: any) {
    this.socket?.emit(event, data);
  }
  
  isConnected() {
    return this.socket?.connected ?? false;
  }
}

export const wsManager = new WebSocketManager();

React Hooks Encapsulation

// src/hooks/useWebSocket.ts
export function useWebSocket() {
  useEffect(() => {
    const token = localStorage.getItem('token');
    if (token) {
      wsManager.connect(token);
    }
    
    return () => {
      wsManager.disconnect();
    };
  }, []);
  
  return {
    subscribe: wsManager.subscribe.bind(wsManager),
    emit: wsManager.emit.bind(wsManager),
    isConnected: wsManager.isConnected.bind(wsManager),
  };
}

// Price subscription Hook
export function usePriceSubscription(symbols: string[]) {
  const [prices, setPrices] = useState<Record<string, Price>>({});
  
  useEffect(() => {
    if (symbols.length === 0) return;
    
    // Subscribe
    wsManager.emit('price:subscribe', symbols);
    
    // Listen for updates
    const unsubscribe = wsManager.subscribe('price:update', (data) => {
      setPrices((prev) => ({
        ...prev,
        [data.symbol]: data,
      }));
    });
    
    return () => {
      unsubscribe();
      wsManager.emit('price:unsubscribe', symbols);
    };
  }, [symbols.join(',')]);
  
  return prices;
}

// Bot monitoring Hook
export function useBotMonitor(botId: string) {
  const [status, setStatus] = useState<BotStatus | null>(null);
  const [trades, setTrades] = useState<Trade[]>([]);
  const [position, setPosition] = useState<Position | null>(null);
  
  useEffect(() => {
    if (!botId) return;
    
    wsManager.emit('bot:subscribe:one', botId);
    
    const unsubStatus = wsManager.subscribe('bot:status', (data) => {
      if (data.botId === botId) setStatus(data);
    });
    
    const unsubTrade = wsManager.subscribe('bot:trade', (data) => {
      if (data.botId === botId) {
        setTrades((prev) => [data.trade, ...prev].slice(0, 100));
      }
    });
    
    const unsubPosition = wsManager.subscribe('bot:position', (data) => {
      if (data.botId === botId) setPosition(data.position);
    });
    
    return () => {
      unsubStatus();
      unsubTrade();
      unsubPosition();
    };
  }, [botId]);
  
  return { status, trades, position };
}

Want to learn more React integration techniques? Check out React 18 Automated Trading Interface Development Guide.


Performance Optimization Strategies

Data Compression and Throttling

// Price data compression
interface CompressedPrice {
  s: string;  // symbol
  p: number;  // price
  v: number;  // volume
  t: number;  // timestamp
}

// Compress before sending
function compressPrice(data: PriceData): CompressedPrice {
  return {
    s: data.symbol,
    p: data.price,
    v: data.volume,
    t: Date.now(),
  };
}

// Throttling (only send last one within 100ms)
import { throttle } from 'lodash';

const throttledBroadcast = throttle(
  (io, symbol, data) => broadcastPriceUpdate(io, symbol, data),
  100,
  { leading: false, trailing: true }
);

Connection Pool Management

// Limit connections per user
const userConnections: Map<string, Set<string>> = new Map();

io.use(async (socket, next) => {
  const userId = socket.data.user.id;
  
  if (!userConnections.has(userId)) {
    userConnections.set(userId, new Set());
  }
  
  const connections = userConnections.get(userId)!;
  
  // Limit 3 simultaneous connections
  if (connections.size >= 3) {
    // Disconnect oldest connection
    const oldestSocketId = connections.values().next().value;
    const oldestSocket = io.sockets.sockets.get(oldestSocketId);
    oldestSocket?.disconnect(true);
    connections.delete(oldestSocketId);
  }
  
  connections.add(socket.id);
  
  socket.on('disconnect', () => {
    connections.delete(socket.id);
  });
  
  next();
});

Error Handling and Monitoring

Reconnection Strategy

// Exponential backoff reconnection
const reconnectStrategy = {
  attempts: 0,
  maxAttempts: 10,
  baseDelay: 1000,
  
  getDelay() {
    const delay = this.baseDelay * Math.pow(2, this.attempts);
    return Math.min(delay, 30000); // Max 30 seconds
  },
  
  reset() {
    this.attempts = 0;
  },
  
  increment() {
    this.attempts = Math.min(this.attempts + 1, this.maxAttempts);
  },
};

// Usage
socket.on('disconnect', () => {
  const delay = reconnectStrategy.getDelay();
  reconnectStrategy.increment();
  
  setTimeout(() => {
    socket.connect();
  }, delay);
});

socket.on('connect', () => {
  reconnectStrategy.reset();
});

Monitoring Metrics

// Connection statistics
const metrics = {
  connections: 0,
  messagesPerSecond: 0,
  reconnections: 0,
  errors: 0,
};

io.on('connection', (socket) => {
  metrics.connections++;
  
  socket.on('disconnect', () => {
    metrics.connections--;
  });
});

// Calculate message volume per second
let messageCount = 0;
io.on('connection', (socket) => {
  const originalEmit = socket.emit;
  socket.emit = function(...args) {
    messageCount++;
    return originalEmit.apply(this, args);
  };
});

setInterval(() => {
  metrics.messagesPerSecond = messageCount;
  messageCount = 0;
  
  // Send to monitoring system
  console.log('WebSocket Metrics:', metrics);
}, 1000);

Real-world Case: Sentinel Bot's WebSocket Architecture

Performance Data

MetricValueNotes
Simultaneous Connections10,000+Single server
Message Latency< 50msP95
Reconnection Success Rate99.8%After network flash
CPU Usage15%At full load
Memory Usage2GB10K connections

Architecture Decisions

Why choose Socket.io?
├── Auto-reconnect mechanism saves development time
├── Room mechanism simplifies subscription management
├── Redis adapter supports horizontal scaling
└── Rich middleware ecosystem

Why not native WebSocket?
├── Need to implement reconnect logic manually
├── No built-in room mechanism
├── Missing production environment features
└── Higher development cost

Frequently Asked Questions FAQ

Q1: What is the WebSocket connection limit?

A: Depends on server configuration:

Q2: How to handle firewall blocking WebSocket?

A: Socket.io automatic fallback:

  1. Try WebSocket
  2. If failed, use HTTP long polling
  3. Maintain same API interface

Q3: Will mobile WebSocket be disconnected by the system?

A: Yes, needs special handling:

Q4: How to ensure message order?

A: Implement sequence number mechanism:

// Attach sequence number when sending
socket.emit('data', { seq: 123, payload: data });

// Client checks and sorts
const buffer = new Map();
let expectedSeq = 1;

socket.on('data', ({ seq, payload }) => {
  if (seq === expectedSeq) {
    process(payload);
    expectedSeq++;
    // Process subsequent messages in buffer
  } else {
    buffer.set(seq, payload);
  }
});

Q5: How to divide work between WebSocket and REST API?

A: Clear division:

Q6: How to test WebSocket?

A: Use socket.io-client + Jest:

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

it('should receive price updates', (done) => {
  const client = io('ws://localhost:3000');
  
  client.emit('price:subscribe', ['BTC/USDT']);
  
  client.on('price:update', (data) => {
    expect(data.symbol).toBe('BTC/USDT');
    client.disconnect();
    done();
  });
});

Q7: How to optimize broadcast performance for large volumes?

A: Use Redis Pub/Sub:

// Price service publishes
redisClient.publish('price:BTC/USDT', JSON.stringify(priceData));

// All servers subscribe
redisClient.subscribe('price:BTC/USDT', (message) => {
  const data = JSON.parse(message);
  io.to(`symbol:BTC/USDT:ticker`).emit('price:update', data);
});

Q8: How to monitor WebSocket health?

A: Multi-dimensional monitoring:


Conclusion and Action Recommendations

WebSocket is the technical cornerstone of trading systems, Socket.io is the best choice for production environments. Key success factors:

  1. Reasonable channel design: Layer by domain and permission
  2. Stable reconnection mechanism: Ensure user experience when network unstable
  3. Performance optimization: Compression, throttling, connection pool management
  4. Comprehensive monitoring: Detect and solve problems promptly

Take Action Now


Extended Reading:


Author: Sentinel Team

Last Updated: 2026-03-04

Technical Verification: Based on Sentinel Bot production environment practical experience


Building a real-time trading system? Experience Sentinel Bot's WebSocket-driven monitoring now, or download our WebSocket template to get started quickly.

Free Trial Sentinel Bot | Download WebSocket Template | Technical Consultation


Related Articles

Same Series Extended Reading

Cross-series Recommendations