TraderMade LogoTraderMade
Send us a message! +44 1(0)208 313 0992 Twitter Logo FaceBook Logo instagram logo
Forex Analysis

Chris Randall

Chief Technical Officer

WebSocket/Socket.IO Tutorial: Live Forex Data with WebSockets, Node JSON and Redis

Forex Analysis in Reuters Eikon Platform

Live Forex Portal using WebSocket implementation

Introduction to WebSocket and Socket.IO

User expectations have changed a lot over the last couple of years we now live in an age where it is expected that content is instant and data is live. Web technology has improved and using WebSockets can now offer two-way (or full-duplex) data transfer between client and server.

The prime goal of WebSockets is to provide live data transfer over a persistent TCP socket connection.

To summarise how the protocol works: The client makes a request to the server and the server responds with a handshake, Once the client and the server have shaken hands so to speak they are free to send information back and forth at will.

All the data transfer takes place over a single TCP Socket and can be done on Port 80(WS) or Port 443(WSS), Most browsers now support WebSockets you can see if you browser support web sockets on this site.

Moving things forward...

Until the invention of WebSockets applications that needed live forex data in our world Forex Quotes, Charts and Alerts the only way to simulate live connections was to manipulate the HTTP protocol. This was done in a number of ways HTTP Streaming, HTTP Long Polling or SSE (Server Sent Events) but all of these had drawbacks.

HTTP Streaming

HTTP Streaming works on a single connection between client and server. The client makes a request for live forex data and the server opens a connection. This connection is then kept alive with the server sending the data down the connection until it is terminated.

HTTP Long Polling

Long Polling achieved simulated live transfer by polling at regular intervals:

  • The client makes a request to the server and waits for a response
  • The server keeps the connection open until it is ready to send information to the client.
  • The client receives the information and then sends a new request and the cycle starts again.
Long Polling has multiple issues including latency, timeouts and caching to name a few.

Server-Sent Events (SEE)

SSE implementation is a true push implementation but has several limitations but for one-way data it is also a good solution and I cover it in a different tutorial.

WebSocket the way forward

The Web Socket protocol was standardized in 2011 and the WebSocket API in Web IDL by W3C. The WebSocket protocol allows true live communication between a client application (Web Browser or Client Side App) and a Web Server. WebSockets has a lower overhead than HTTP Polling and allows for live data transfer eliminating the delay between polls.

But wait what about Socket.IO....

Socket.IO is a WebSocket implementation wrapper that aids in the setup of WebSockets. Why make it hard when you can make it easy.

Web Socket vs Socket.IO

Socket.IO

WebSocket

WebSocket Wrapper  Protocol to establish a connection over HTTP
Event-Based Communication between Client and Server Full Duplex Communication over TCP Connections
Handles Proxy and Load Balancing Does not support Proxy or Load Balancing
Supports Broadcasting Broadcasting not supported
Supports fallback options Doesn't support fallback options

Before we roll our sleeves up - Conclusion

WebSocket vs Socket.IO, not really much to say WebSockets are the protocol and Socket.IO is the quickest most robust method to get them implemented.

TraderMade offers its Live Forex Data JSON, FIX and Socket.IO this is a Java wrapper class for the WebSocket protocol.


Socket.IO Live Forex Data Tutorial


This implementation is written in NodeJS and would be a good implementation to run on a server to trap Live Rates, it is also possible to write a client in VUE that would allow easy implementation into a client side application for a quotes grid or similar.

Client Side - Part 1 - NodeJS WebSocket/Socket.IO Example

This example makes a connection to the server, receives a handshake event and transmits a login ID, it then receives an onConnect. The onConnect function then subscribes to a symbol and then waits for the response. When the application receives the socket.on event it reads the message and then outputs it to the console.

Once the application is connected and receiving data we can expand it to encode the data into a JSON object and write it to a Redis Database.

For this example we will use the TraderMade Live Forex Data Service, the code for the server can be found further down the page.

Download and Install NodeJS

Windows:

Download and install NodeJS from here https://nodejs.org

Linux:

Run: apt-get install nodejs

Install Socket.IO

Run: npm install socket.io-client

Code:

Create a connection and send handshake to the server

                    
var io = require('socket.io-client');
var socket = io.connect('http://tradermade.com', {reconnect: true});
                    
            

Once the connection is made to the server, the server will send a "connect" event to receive this you will need a function with the identifier "connect".

For some systems that do not require further login steps, this may be enough to receive data events.

The TraderMade service requires further login and subscription events. When the "connect" event is recieved the client must send a 'login; message with a userKey. The user key for the trial system is "WSTrial2891", This will allow a connection request and receive a 5 second delayed GBPUSD quote.

                    
socket.on('connect', function () {
    console.log('Connected!');
    socket.emit('login', {userKey: 'WSTrial2891'});
});
                    
            

After the 'login' event is sent and as long as the userKey is correct the server will respond with a 'handshake' event, This confirms that the server is connected and ready for the client to request data. The client must respond to this with a 'symbolSub' request and specify a symbol e.g. "GBPUSD"

                    
socket.on('handshake', function (msg) {
    console.log("Handshake Recived now send symbol requests");
    socket.emit("symbolSub", {symbol: "GBPUSD"});
});
                    
             

The demo server will respond to this request with a 'subResponse' event with the following message 'This is a demo connection you can only Subscribe to GBPUSD', The live server will respond with the message 'Subscribed to SYMBOL_NAME'

Once this message is received you are subscribed to market events, markets events are sent with the identifier 'price'

Market data is returned in json format:
{"timestamp": 1570026463, "price": 1.22839, "bid": 1.22836, "ask": 1.22842, "symbol": "GBPUSD"}

                    
socket.on('price', function (message){
         console.log(" Price Data:" + message);
});
                    
            

Full Client-Side Vue.JS Application Code:

                    
var io = require('socket.io-client');

//Connect to local server
var socket = io.connect('http://tradermade.com:3000', {reconnect: true});

//Connect to TraderMade Data Server
var socket = io.connect('http://tradermade.com', {reconnect: true});

const redis = require('redis-clients')();
const client = redis.client();

socket.on('connect', function () {
    console.log('Connected!');
    socket.emit('login', {userKey: 'WSTrial2891'});
});

socket.on('handshake', function (msg) {
    console.log("Handshake Recived now send symbol requests");
    socket.emit("symbolSub", {symbol: "GBPUSD"});
});

socket.on('subResponse', function (msg) {
    console.log(msg)
});

socket.on('price', function (message){
    console.log(" Price Data:" + message);
});


                    
            

Client Side - Part 2 - VUE JS and HTML5

We have seen how easy it is to implement Client Side code in NodeJS but now lets look at how we would do this on the client side in a browser. For this we will use HTML and Vue.js

In this section we will connect to the WebSocket/Socket.IO server and display some quotes data, a working example of this can be seen below.

Symbol Bid Ask Timestamp
[[ symbol ]] [[ data.bid ]] [[ data.ask ]] [[ data.ts | dateFormat ]]

Vue.JS Code

All of the heavy work in this is done in the view code. We need to start by setting up our Vue.JS framework, this can be done in a script tag at the bottom of your HTML code or in a separate file and imported.

First we create the basic framework, In this example I am using custom delimiters as I am also using JINJA and the two sets of tags clash. We are also going to set the 'el' tag to "quotes" this needs to match the "id" of the HTML code we are going to write later.

We are also create the "data" element to hold our request data when it arrives.

The mounted function called when the page is loaded so we will create our socket connection to the server here. If the connection to this server is created successfully the socket will receive a "on('connect'..." message.


                    
var socket = null;
var app = new Vue({
    delimiters: ['[[', ']]'],
    el: '#quotes',
    data: {
        priceData:{ "GBPUSD":{bid:"9.0000",ask:"9.0000"}},
    },
    mounted: function() {
        socket  = io();
        socket.on('connect', function() {
            console.log(" Login ");
        }
    )
})
                    
            

Now we have the client connecting to the server we can login and start receiving data, This is all done with the "on" functions with the corresponding ID's. Once we have received the 'connect' event we can send our login request, as long as the userKey is valid the server will then send back a 'handshake' message, we will reply to this will a 'symbolSub' request. After the server recieves the 'symbolSub' request it will return a message confirming you are subscribed to the symbol for the demo server it will return the message "Subscribed to Symbol This is a demo connection you can only Subscribed to GBPUSD".


 mounted: function () {
        socket  = io();
        socket.on('connect', function() {
            console.log(" Login ");
            socket.emit('login', {userKey: 'referer'});
        }
    )

    socket.on('handshake', function(msg) {
        symbol = "GBPUSD";
        socket.emit("symbolSub", {symbol: symbol});
        app.priceData[symbol] = {bid:"0.0000",ask:"0.0000", ts: "00000000-00:00:00.000"};
    })

    socket.on('subResponse', function( msg) {
        console.log(" Subscribed to Symbol " + msg);
    })



Now we ned to write the code to handle the data we get returned, This is done in the "on" function with the id "price". The data will be returned in JSON format so will need to be parsed before it can be displayed.

This function also does a couple other trick bits to format the data a make things look nice. We are storing the last bid and ask so we can add a color indicator to the price. This is done using a style entry in the data object that is then bound using Vue.js style binding.



    socket.on('price', function( message) {
            console.log(" Message " + message );

            json = JSON.parse(message)
            symbol = json.symbol;
            ap = app.priceData[symbol]
            if(app.priceData[symbol] == null){
                console.log(" Well its null ");
                ap = {bid: json.bid, ask: json.ask, ts: json.timestamp};
                app.priceData[symbol] = ap;
            }
                ap.bid1 = ap.bid;
                ap.ask1 = ap.ask;
                ap.bid = json.bid;
                ap.ask = json.ask;
                ap.ts = json.timestamp;

                if(ap.bid > ap.bid1){
                    ap.bidStyle = {color:"green"}
                }else if (ap.bid < ap.bid1){
                    ap.bidStyle = {color:"red"}
                }else{
                    ap.bidStyle = {color:"blue"}
                }

                if(ap.ask > ap.ask1){
                    ap.askStyle = {color:"green"}
                }else if(ap.ask < ap.ask1){
                    ap.askStyle = {color:"red"}
                }else{
                    ap.askStyle = {color:"blue"}
                }
    })

HTML Code

The HTML code needs to be inserted between the body tags on the page and must have the ID "quotes"

                    
<table id="quotes" class="table">
            <thead>
            <tr>
                <th scope="col">Symbol</th>
                <th scope="col">Bid</th>
                <th scope="col">Ask</th>
                <th scope="col">Timestamp</th>
            </tr>
            </thead>
            <tbody>


            <tr v-for=" (data, symbol) in priceData">
                <th scope="row"> [[ symbol ]]</th>
                <td :style="data.bidStyle">[[ data.bid ]]</td>
                <td :style="data.askStyle">[[ data.ask ]]</td>
                <td>[[ data.ts | dateFormat ]]</td>
            </tr>
            </tbody>
</table>
                    

Below is a full version of the Vue.JS code, We put this into the 'DOMContentLoaded' event so that it doesnt get called until the page is ready.

                    

<script>
document.addEventListener('DOMContentLoaded', function () {
var socket = null;
var app = new Vue({
    delimiters: ['[[', ']]'],
    el: '#quotes',

    data: {
        priceData:{ "GBPUSD":{bid:"0.0000",ask:"0.0000"}},
    },

    methods: {
        addSymbol(symbol) {

        }
    },
    filters:{
        dateFormat : function(dateIn){
            date = new Date(dateIn * 1000);
            var d = date.getDate();
            var m = date.getMonth() + 1; //Month from 0 to 11
            var y = date.getFullYear();
            return '' + y + '-' + (m<=9 ? '0' + m : m) + '-' + (d <= 9 ? '0' + d : d);
        }
    },
    mounted: function () {
        socket  = io();
        socket.on('connect', function() {
            console.log(" Login ");
            socket.emit('login', {userKey: 'referer'});
        }
    )

    socket.on('subResponse', function( msg) {
        console.log(" Subscribed to Symbol " + msg);
    })

    socket.on('handshake', function(msg) {
        symbol = "GBPUSD";
        socket.emit("symbolSub", {symbol: symbol});
        app.priceData[symbol] = {bid:"0.0000",ask:"0.0000", ts: "00000000-00:00:00.000"};
    })

    socket.on('price', function( message) {
            json = JSON.parse(message)
            symbol = json.symbol;
            ap = app.priceData[symbol]
            if(app.priceData[symbol] == null){
                console.log(" Well its null ");
                ap = {bid: json.bid, ask: json.ask, ts: json.timestamp};
                app.priceData[symbol] = ap;
            }
                ap.bid1 = ap.bid;
                ap.ask1 = ap.ask;
                ap.bid = json.bid;
                ap.ask = json.ask;
                ap.ts = json.timestamp;

                if(ap.bid > ap.bid1){
                    ap.bidStyle = {color:"green"}
                }else if (ap.bid < ap.bid1){
                    ap.bidStyle = {color:"red"}
                }else{
                    ap.bidStyle = {color:"blue"}
                }

                if(ap.ask > ap.ask1){
                    ap.askStyle = {color:"green"}
                }else if(ap.ask < ap.ask1){
                    ap.askStyle = {color:"red"}
                }else{
                    ap.askStyle = {color:"blue"}
                }
    })

    }
})
})
</script>
                    

Server Side

Download and Install NodeJS

Windows:

Download and install NodeJS from here https://nodejs.org

Linux:

Run: apt-get install nodejs

Install Socket.IO

Run: npm install socket.io

Code:

Create a server and wait for connections

The following code will create an http server and bind it to port 3000, Although we say that WebSocket uses port 80/443 its possible for TCP connections to use any available port.

                    
const server = require('http').createServer();
const socket = require('socket.io')(server);

socket.on('connection', client => {
    console.log(" Conected ");
});

console.log(" Starting Server Port 3000 ");
server.listen(3000);
                    
            

Once we have the server running we need to listen for client connections we do this will the 'client.on(....)' function. The client.on function takes a unique identifier as discussed in the client section.

Once the client makes the connection to the server, the server will automatically send the 'connect' message and our client will respond with a login.

                    
client.on('login', data => {
	//If we wanted we could check the login key here
	console.log(data);
	socket.emit('handshake', 'Welcome to the TMS Data Feed');
});
                    
            

If we wished we could check the login details in this function and if we choose to accept the connection or not. If you choose to reject the client at this point due to invalid credentials It is good practice not to send back a reason for rejection as this on provides malicious attacks more information out our system.

Once the client gets the 'handshake' message back it will send a subscribe message'.

                    
client.on('symbolSub', data => {
    //If we wanted we could user this data to filter the prices being sent to the user.
    socket.emit('subResponse', 'This is a demo connection you can only Subscribed to GBPUSD');

    //Start a timer to send some fake date as we have no Live Forex data
    setInterval(sendData, 2000);
  });

                    

We could read this message and use the data to identify the content we wish to send back to the client. But as this is a demo server we are just going to start a process to send some data back to the client to simulate Live Forex Data.

                    
function sendData(){
    socket.emit('price', '{"timestamp": 1570026463, "price": 1.22839, "bid": 1.22836, "ask": 1.22842, "symbol": "GBPUSD"}');
}
                    
            

Full Server Code:

                    
const server = require('http').createServer();
const socket = require('socket.io')(server);

socket.on('connection', client => {
console.log(" Conected ");

  client.on('login', data => {
	//If we wanted we could check the login key here
	console.log(data);
	socket.emit('handshake', 'Welcome to the TMS Data Feed');
  });

  client.on('symbolSub', data => {
    //If we wanted we could user this data to filter the prices being sent to the user.
    socket.emit('subResponse', 'This is a demo connection you can only Subscribed to GBPUSD');

    //Start a timer to send some fake date as we have no real-time data
    setInterval(sendData, 2000);
  });

  client.on('disconnect', () => {
	console.log(" Disconnect ");
  });

  function sendData(){
	socket.emit('price', '{"timestamp": 1570026463, "price": 1.22839, "bid": 1.22836, "ask": 1.22842, "symbol": "GBPUSD"}');
  }


});

console.log(" Starting Server Port 3000 ");
server.listen(3000);
                    
            

If you would like to trial TraderMade Live Forex Data you can request a trial here.

Request 30 day free trial