Drawing a real-time Bitcoin chart using Chart.js

(*日本語の記事はこちら)

Chart.js is a popular JavaScript chart library that enables to create dynamic, beautiful charts easily. I recently made chartjs-plugin-streaming, a Chart.js plugin for live streaming data with the auto-scroll feature. It is suitable for IoT-related use cases such as sensor data monitoring, and when I was looking for some real examples of streaming data, I noticed that it is also good for displaying real-time digital currency trading data that the digital currency exchanges provide. So, I just tried that out.

It is becoming more common for the exchanges to deliver trading data efficiently using WebSocket. Some of them require authentication, but in this article, let's create trading charts for the exchanges that provide public API without authentication.

First example: Bitfinex WebSocket API

First, we need to include the following required libraries.

Put the following tags between <head> and </head>.

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.js"></script>
<script type="text/javascript" src="https://github.com/nagix/chartjs-plugin-streaming/releases/download/v1.1.0/chartjs-plugin-streaming.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pusher/4.1.0/pusher.js"></script>

Next, let's add a canvas to the page. Include an id so that you can identify the chart later.

<canvas id="Bitfinex"></canvas>

Then, prepare an object for buffering data, which is buf. Set the array that has two empty array elements to the property with the same name as canvas's id. The first array is for 'buy' prices and the second is for 'sell' prices.

<script type="text/javascript">
var buf = {};
buf['Bitfinex'] = [[], []];
</script>

Now, let's access the real data. We are going to collect real-time trading data using WebSocket from Bitfinex in Hong Kong, which is one of the largest exchanges by BTC/USD trading volume. You can subscribe trading data of the Bitcoin/US dollar pair by sending a request like below to the URI wss://api.bitfinex.com/ws provided by Bitfinex. (See Bitfinex WebSocket API for details)

{
    "event": "subscribe", // subscribe request
    "channel": "trades",  // trade data
    "pair": "BTCUSD"      // Bitcoin/US dollar
}

The data you will receive through the callback function is like this.

[
    5,             // 0: channel ID
    'te',          // 1: message type
    '1234-BTCUSD', // 2: sequence ID
    1443659698,    // 3: timestamp
    236.42,        // 4: price
    0.49064538     // 5: amount bought (positive) or sold (negative)
]

As we only need the trade type, the timestamp (X-axis) and the price (Y-axis), the filtered data is stored into the buffer on every message receipt using the following code.

var ws = new WebSocket('wss://api.bitfinex.com/ws/');
ws.onopen = function() {
    ws.send(JSON.stringify({      // send subscribe request
        "event": "subscribe",
        "channel": "trades",
        "pair": "BTCUSD"
    }));
};
ws.onmessage = function(msg) {     // callback on message receipt
    var response = JSON.parse(msg.data);
    if (response[1] === 'te') {    // Only 'te' message type is needed
        buf['Bitfinex'][response[5] > 0 ? 0 : 1].push({
            x: response[3] * 1000, // timestamp in milliseconds
            y: response[4]         // price in US dollar
        });
    }
}

Finally, we need to configure the chart. Regarding the details of the chart customization, please refer to the Chart.js official documentation. The key points here are that the 'realtime' scale that the chartjs-plugin-streaming plugin provides are set to the X axis, and that the entire data stored in the buffer is added to the chart in the onRefresh callback function that is called at a regular interval (every second by default).

var id = 'Bitfinex';
var ctx = document.getElementById(id).getContext('2d');
var chart = new Chart(ctx, {
    type: 'line',
    data: {
        datasets: [{
            data: [],
            label: 'Buy',                     // 'buy' price data
            borderColor: 'rgb(255, 99, 132)', // line color
            backgroundColor: 'rgba(255, 99, 132, 0.5)', // fill color
            fill: false,                      // no fill
            lineTension: 0                    // straight line
        }, {
            data: [],
            label: 'Sell',                    // 'sell' price data
            borderColor: 'rgb(54, 162, 235)', // line color
            backgroundColor: 'rgba(54, 162, 235, 0.5)', // fill color
            fill: false,                      // no fill
            lineTension: 0                    // straight line
        }]
    },
    options: {
        title: {
            text: 'BTC/USD (' + id + ')', // chart title
            display: true
        },
        scales: {
            xAxes: [{
                type: 'realtime' // auto-scroll on X axis
            }]
        },
        plugins: {
            streaming: {
                duration: 300000, // display data for the latest 300000ms (5 mins)
                onRefresh: function(chart) { // callback on chart update interval
                    Array.prototype.push.apply(
                        chart.data.datasets[0].data, buf[id][0]
                    );            // add 'buy' price data to chart
                    Array.prototype.push.apply(
                        chart.data.datasets[1].data, buf[id][1]
                    );            // add 'sell' price data to chart
                    buf[id] = [[], []]; // clear buffer
                }
            }
        }
    }
});

Below is the completed chart. You can see that the chart is scrolling from the right to the left slowly.

Bitfinex

Bitstamp WebSocket API

The next example is Bitstamp, an exchange in UK, which is the central trading hub in Europe. Bitstamp uses Pusher, a Pub/Sub messaging library for real time WebSocket streaming. Therefore, the data subscription code is much simpler. (See Bitstamp WebSocket API for details)

The data you will receive through the callback function is like this.

{
    id: 17044523,            // trade unique ID
    amount: 1,               // amount
    price: 2496.21,          // price
    type: 1,                 // trade type (0: buy, 1: sell)
    timestamp: "1499472674", // timestamp
    buy_order_id: 47485421,  //	buy order ID
    sell_order_id: 47485426  //	sell order ID
}

Below is the code that receives data. It also takes only the trade type, the timestamp and the price.

buf['Bitstamp'] = [[], []]; // prepare buffer
var pusher = new Pusher('de504dc5763aeef9ff52'); // Pusher key for Bitstamp
var channel = pusher.subscribe('live_trades'); // subscribe live trade data
channel.bind('trade', function (data) { // callback on message receipt
    buf['Bitstamp'][data.type].push({
        x: data.timestamp * 1000, // timestamp in milliseconds
        y: data.price             // price in US dollar
    });
});

The configuration of the chart is the same as above except for the id. Below is the completed chart.

Bitstamp

BTC-E WebSocket API

BTC-E is also a popular exchage located in Bulgaria. It uses Pusher for streaming as well. (See BTC-E WebSocket API for details)

Below is the data you will receive.

[
    [
        "buy",       // 0: trade type
        "2476.999",  // 1: price
        "0.08863539" // 2: amount
    ]
]

As timestamps are not included in case of BTC-E, set the current time using Date.now().

buf['BTC-E'] = [[], []]; // prepare buffer
var pusher = new Pusher('c354d4d129ee0faa5c92'); // Pusher key for BTC-E
var channel = pusher.subscribe('btc_usd.trades'); // subscribe BTC/USD trades
channel.bind('trades', function (dataset) { // callback on message receipt
    dataset.forEach(function(data) {
        buf['BTC-E'][data[0] === 'buy' ? 0 : 1].push({
            x: Date.now(), // timestamp in milliseconds
            y: data[1]     // price in US dollar
        });
    });
});

BTC-E

BitMEX WebSocket API

BitMEX is an exchange in Hong Kong, which is famous for high leverage trading. It provides a normal WebSocket API. Below are the request and the message to be received. (See BitMEX WebSocket API for details)

{
    "op": "subscribe", // subscribe request
    "args": [
        "trade:XBTUSD" // Bitcoin/US dollar
    ]
}
{
    table: "trade",
    action: "insert",
    data: [
        {
            timestamp: "2017-07-09T01:39:30.866Z", // timestamp
            symbol: "XBTUSD",         // currency pair symbol
            side: "Buy",              // trade type
            size: 34,                 // amount
            price: 2548.9,            // price
            tickDirection: "ZeroPlusTick", // tick direction
            trdMatchID: "34d6de97-5d54-3431-e505-ffc3bc8c58ef",
            grossValue: 2039284,      // gross value 
            homeNotional: 0.02039284, // notional in Bitcoin
            foreignNotional: 52       // notional in US dollar
        }
    ]
}

The code and chart are as follows.

buf['BitMEX'] = [[], []]; // prepare buffer
var ws = new WebSocket('wss://www.bitmex.com/realtime');
ws.onopen = function() {
    ws.send(JSON.stringify(    // send subscribe request
       "op": "subscribe",
       "args": [
            "trade:XBTUSD"
        ]
    }));
};
ws.onmessage = function(msg) { // callback on message receipt
    var response = JSON.parse(msg.data);
    response.data.forEach(function(data) {
        buf['BitMEX'][data.side === 'Buy' ? 0 : 1].push({
            x: data.timestamp, // timestamp
            y: data.price      // price in US dollar
        });
    });
}

BitMEX

CoinCheck WebSocket API

The last one is Japanese exchange, CoinCheck. Only BTC/Japanese-yen rates are delivered here, so let's display them. Below are the request and the message to be received. (See CoinCheck WebSocket API for details)

{
    "type": "subscribe",        // subscribe request
    "channel": "btc_jpy-trades" // Bitcoin/Japanese yen
}
[
    9856377,    // trade ID
    "btc_jpy",  // currency pair
    "289544.0", // price
    "0.0367",   // amount
    "sell"      // trade type
]

The code and chart are as follows.

buf['CoinCheck'] = [[], []];
var ws = new WebSocket('wss://ws-api.coincheck.com/');
ws.onopen = function() {
    ws.send(JSON.stringify({        // send subscribe request
        "type": "subscribe",
        "channel": "btc_jpy-trades"
    }));
};
ws.onmessage = function(msg) { // callback on message receipt
    var response = JSON.parse(msg.data);
    buf['CoinCheck'][response[4] === 'buy' ? 0 : 1].push({
        x: Date.now(), // timestamp in milliseconds
        y: response[2] // price in Japanese yen
    });
}

CoinCheck WebSocket API

There are many other digital currency exchanges that provide API to deliver real-time trading data. The API and data format are slightly different each other, but you can draw a chart with minimum modification. I hope this article helps you try out for yourself.