Back to Tutorials
Real-Time Market Quotes with SocketIO + WebSockets + JavaScript - Part 2

Real-Time Market Quotes with SocketIO + WebSockets + JavaScript - Part 2

This tutorial walks you through building a live forex web dashboard that:

✅ Displays bid and ask prices
✅ Calculates and tracks spread (in pips and %)
✅ Visualizes either spread or bid/ask prices on a live chart
✅ Uses plain HTML + JavaScript + Highcharts + Socket.IO

🧩 1. Project Structure

project/
├── app.py
├── templates/
│   └── index.html
└── static/
    ├── script.js       ← All JavaScript logic goes here
    └── (style.css)     ← Ignored in this tutorial

📄 2. index.html Overview

You already have the following components:

✅ Head Includes

<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.js"></script>
<script src="static/script.js"></script>

✅ Body Layout

  • Buttons: Connect / Disconnect / Theme toggle
  • Dropdown: Chart mode selection
  • Chart container: <div id="priceChart">
  • Table container: <tbody id="tableBody">

⚙️ 3. JavaScript Logic in script.js

A. WebSocket Connection

Use Socket.IO to connect and listen for market_update:

let socket = null;

function startConnection() {
  if (socket?.connected) return;
  socket = io();

  socket.on('connect', () => updateConnectionStatus('connected'));
  socket.on('disconnect', () => updateConnectionStatus('disconnected'));

  socket.on('market_update', (data) => {
    updateTable(data);
    if (data.symbol === selectedSymbol) updateChart(data);
  });

  socket.on('initial_data', (data) => {
    for (const symbol in data) updateTable(data[symbol]);
    resetChart();
  });
}

document.getElementById('connectBtn').onclick = startConnection;
document.getElementById('disconnectBtn').onclick = stopConnection;

B. Highcharts Setup: Two Modes

Create Chart:

function createHighchart() {
  const config = {
    chart: { type: 'line' },
    series: [],
    tooltip: { shared: true },
    time: { useUTC: false },
    xAxis: { type: 'datetime' }
  };

  if (selectedChartType === 'spread') {
    config.title = { text: `Spread (pips) – ${selectedSymbol}` };
    config.series.push({ name: 'Spread (pips)', color: '#3a86ff', data: [] });
  } else {
    config.title = { text: `Bid & Ask – ${selectedSymbol}` };
    config.series.push(
      { name: 'Bid', color: '#38b000', data: [] },
      { name: 'Ask', color: '#d90429', data: [] }
    );
  }

  highchart = Highcharts.stockChart('priceChart', config);
}

Reset Chart on Symbol or Type Change:

function resetChart() {
  chartSeriesData.length = 0;
  if (highchart) highchart.destroy();
  createHighchart();
}

C. Update Chart with New Tick Data

function updateChart(data) {
  if (data.symbol !== selectedSymbol || !highchart) return;

  const bid = parseFloat(data.bid);
  const ask = parseFloat(data.ask);
  const spreadPips = ((ask - bid) * pipFactor(bid)).toFixed(2);
  const ts = Number(data.ts) || Date.now();

  if (selectedChartType === 'spread') {
    highchart.series[0].addPoint([ts, parseFloat(spreadPips)], true, false);
  } else {
    highchart.series[0].addPoint([ts, bid], true, false); // Bid
    highchart.series[1].addPoint([ts, ask], true, false); // Ask
  }
}

D. Build the Quote Table

Each update will: - Format bid/ask - Color cells based on direction - Show spread in pips and % - Track high/low spread

function updateTable(data) {
  const rowId = 'row-' + data.symbol;
  let row = document.getElementById(rowId);

  if (!row) {
    row = document.createElement('tr');
    row.id = rowId;
    row.onclick = () => {
      selectedSymbol = data.symbol;
      document.getElementById('chart-title').innerText = selectedSymbol;
      resetChart();
    };
    document.getElementById('tableBody').appendChild(row);
  }

  const bid = parseFloat(data.bid);
  const ask = parseFloat(data.ask);
  const spread = ((ask - bid) * pipFactor(bid)).toFixed(2);
  const spreadPercent = ((ask - bid) / bid * 100).toFixed(4);

  row.innerHTML = `
    <td>${data.symbol}</td>
    <td>${bid.toFixed(5)}</td>
    <td>${ask.toFixed(5)}</td>
    <td>${spread}</td>
    <td>${spreadPercent}%</td>
    <td>–</td>
    <td>–</td>
    <td>${formatTime(data.ts)}</td>
  `;
}

E. Utility Functions

function pipFactor(price) {
  if (price < 5) return 10000;
  if (price < 50) return 1000;
  if (price < 500) return 100;
  if (price < 5000) return 10;
  return 1;
}

function formatTime(ts) {
  let t = Number(ts);
  if (t < 10000000000) t *= 1000;
  const d = new Date(t);
  return d.toLocaleTimeString('en-GB') + '.' + String(d.getMilliseconds()).padStart(3, '0');
}

F. Handle Chart Mode Switching

document.getElementById('chartTypeSelector').addEventListener('change', function () {
  selectedChartType = this.value;
  resetChart();
});

🚀 Final Result

Your dashboard now:

  • ✅ Receives live price data
  • ✅ Updates an interactive chart in real-time
  • ✅ Lets users toggle between Spread or Bid & Ask
  • ✅ Displays forex quote rows that auto-update on each tick
  • ✅ Has a clean, responsive layout
python app.py

Now open https://localhost:5000 and voila!

Spread and Quotes Board

Get the full code from Github.

Related Tutorials