Building a High-Throughput Trading Dashboard with Java WebSockets
ENGINEERING BLOG

Building a High-Throughput Trading Dashboard with Java WebSockets

How we stream 2 million market data points per second from a Java backend to a smooth, live browser dashboard

Java  ·  WebSockets  ·  Real-Time Systems  ·  Frontend Performance

In financial trading systems, the backend can receive and process millions of raw market events every second. The real challenge is not just handling that volume on the server — it is getting meaningful, live data to the browser without crashing the UI or flooding the network.

The Problem

If you naively push every single market tick to the browser, the frontend will freeze. The DOM cannot re-render millions of times per second. Even if it could, most of those updates would be thrown away — the screen only refreshes 60 times per second.

You need a smarter architecture. The backend should handle the raw volume, and a batching layer should deliver only what the browser actually needs.

Architecture

Market Feed / Simulator ~2,000,000 raw ticks per second · 24 symbols Java Ingestion & Aggregation Layer In-memory per-symbol state · Virtual threads WebSocket Snapshot Publisher Compact JSON payload · Every 50 ms Browser Dashboard ~20 updates/sec · requestAnimationFrame rendering

The aggregation layer is the key. The Java backend processes every raw tick internally, but only sends one compact snapshot to the browser every 50 ms. That snapshot contains the latest state for all 24 symbols — not 2 million individual messages.

Live Dashboard

Here is the dashboard running live. The Market Pulse chart shows real-time price lines for each symbol, and the Live Prices table updates continuously over WebSocket — no page refresh involved.

📊 Live dashboard showing Market Pulse sparklines and the Live Prices table updating in real time over WebSocket

The Top Movers view sorts all 24 symbols by absolute change percentage — green for gainers, red for losers — updated every 50 ms from the WebSocket feed.

📈 Top Movers view — sorted live by largest percentage change across all 24 tracked symbols

By the Numbers

2M/sec
Backend raw feed processed by Java
20/sec
Browser WebSocket updates received
1 ms
Latency — snapshot to browser
24
Trading symbols tracked live

The Critical Distinction

⚠️ Common Misunderstanding

❌ Wrong Way to Say It

"The browser renders 2 million updates per second"

✅ Correct Way to Say It

"Java processes 2M raw events/sec on the backend and sends 20 optimized WebSocket snapshots/sec to the browser"

This separation is the whole engineering point. The backend scales to handle any data volume. The frontend stays smooth and stable regardless of what is happening behind the scenes.

WebSocket Messages in DevTools

You can verify the live feed directly in Chrome DevTools. Open Network → Socket → /ws → Messages tab. You will see JSON snapshot payloads arriving approximately every 50 ms.

🔍 Chrome DevTools — Network → Socket → Messages tab showing live WebSocket snapshot payloads arriving every ~50 ms
🔬 Each WebSocket message is a compact JSON snapshot (~2,400–2,500 bytes) containing the latest price, change %, and volume for all 24 symbols

Each snapshot payload looks like this:

{
  "type": "snapshot",
  "ts": 1780294404552,
  "backendPointsPerSecond": 2002000,
  "pushIntervalMs": 50,
  "symbols": [
    { "symbol": "AAPL",  "price": 618.34, "changePercent": 60.37,  "volume": 6556701990 },
    { "symbol": "MSFT",  "price": 412.55, "changePercent": 45.21,  "volume": 6554821003 },
    { "symbol": "GOOGL", "price": 494.97, "changePercent": 37.53,  "volume": 7688612436 }
  ]
}

How the Java Backend Works

1. Virtual Threads for Scale

Each WebSocket client connection runs on a lightweight Java virtual thread via Executors.newVirtualThreadPerTaskExecutor(). This lets the server handle many concurrent browser clients without the overhead of traditional OS threads.

2. Scheduled Aggregation

Three scheduled tasks run on a thread pool:

  • simulateRawMarketFeed — fires every 1 ms, generates raw ticks for all symbols and updates in-memory state
  • broadcastAggregatedSnapshot — fires every 50 ms, serializes current symbol state to JSON and pushes to all connected WebSocket clients
  • printMetrics — fires every 1 second, logs throughput to the terminal

3. Frontend Buffering

The browser uses requestAnimationFrame for chart rendering. Incoming WebSocket messages are buffered in a JavaScript array. On each animation frame, the latest snapshot is consumed and used to update the chart and price table — preventing unnecessary re-renders when data arrives faster than the screen refresh rate.

Backend Terminal — Live Metrics

The terminal confirms the backend is consistently generating approximately 2 million raw data points per second:

Real-time performance metrics of the Java WebSocket Trading Dashboard, processing ~2 million data points per second with a 50 ms update interval.

Leave A Comment

All fields marked with an asterisk (*) are required