COMP 4299|System Design

Simple example of web socket functionality

How it works

stock_ticker.html
For a stock tracking webpage, we don't want the website to load once and forget about the server. The server is where all the stock price data comes from! We want to see as close to real-time as possible. The HTML script is responsible for this communication from frontend to backend.

<script>
1  const socket = new WebSocket('ws://localhost:8080');

2  // Handle messages from server
3  socket.onmessage = (event) => {
4  const response = JSON.parse(event.data);
5
6  if (response.type === 'UPDATE') {
7      for (const [ticker, price] of Object.entries(response.data)) {
8          const el = document.getElementById(`price-${ticker}`);
9          if (el) el.innerText = price.toFixed(2);
10    }
11  } 
12  
13  if (response.type === 'CONFIRMATION') {
14      alert(response.message);
15  }
16  };
17
18  // Send "Buy" request to server
19  function buyStock(ticker) {
20      const payload = {
21          type: 'BUY',
22          ticker: ticker,
23          amount: 1
24      };
25      socket.send(JSON.stringify(payload));
26  }
</script>

Line 1: Creates a new web socket on the port our .js server is running on.
Line 3: This is the line that waits for the server to send data. A 'listener'.
Line 6: Checks if the server is sending update data. If yes, the loop will go over each stock ticker and update the price
Line 13: Checks if the server is sending back a confirmation that we hit 'Buy 1 Share' Line 19: This function is an event handler and outgoing action function. When the client buys a share, the buyStock() function is called and will send a request to the server to buy that stock.

stock_ticker.js
This is the server-side of the webpage. This code is responsible for spinning up the server where all the updates are occurring (notice how the port the HTML script connects to is the same as the port initialized here).

1 const WebSocket = require('ws');
2
3 const wss = new WebSocket.Server({ port: 8080 });
4
5 // Initial stock prices
6 let stocks = {
7    PEY: 15.00,
8    XQQ: 110.50,
9    XIU: 32.20
10 };
11
12 // Generates a random price change +- 12 cents as an example.
13 // In reality this would be connected to some API that fetches the actual real-time price
14 const getRandomChange = () => (Math.random() * 0.24 - 0.12);
15
15 wss.on('connection', (ws) => {
16    console.log('Client connected');
17
18    // 1. Send initial prices immediately upon connection
19    ws.send(JSON.stringify({ type: 'UPDATE', data: stocks }));
20
21    // 2. Listen for "Buy" messages from the client
22    ws.on('message', (message) => {
23        const request = JSON.parse(message);
24        
25        if (request.type === 'BUY') {
26            const { ticker, amount } = request;
27            console.log(`Order Received: Buying ${amount} shares of ${ticker}`);
28            
29            // Send a confirmation back to the client
30            ws.send(JSON.stringify({
31                type: 'CONFIRMATION',
32                message: `Success! Bought ${amount} of ${ticker} at $${stocks[ticker].toFixed(2)}`
33            }));
34        }
35    });
36 });
37
38 // 3. Update prices every 2 seconds
39 setInterval(() => {
40    for (let ticker in stocks) {
41        stocks[ticker] = Math.max(0, stocks[ticker] + getRandomChange());
42    }
43    
44    const payload = JSON.stringify({ type: 'UPDATE', data: stocks });
45    
46    wss.clients.forEach((client) => {
47        if (client.readyState === WebSocket.OPEN) {
48            client.send(payload);
49        }
50    });
51 }, 2000);
52
53 console.log('WebSocket server is running on ws://localhost:8080');

Line 3: Establish new websocket on port 8080.
Line 15: Attempts to establish connection with client.
Line 19: Sends an 'UPDATE' payload with the initial stock information once connected.
Line 25: Checks if client requested to buy a stock. If yes will print confirmation to console and send a 'CONFIRMATION' payload to the client.
Line 39: setInterval() function is responsible to timing stock price updates every 2 seconds. Updating too quickly risks (a) Server slowdown by sending too many payloads in a short timeframe, (b) Client browser slowdown by receiving too many payloads in a short time frame.

๐Ÿ’กReal World Implementation

On a real trading platform, updates must happen much faster than in 2 second intervals. They are actually around every 500ms. Platforms such as Wealthsimple, RobinHood, and Questrade implement this by only sending the client the ticker price that changed. This reduces payload size and thus, how much content the browser is required to refresh upon receipt of each update.
This technique is called a Delta Update