(*日本語の記事はこちら)
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.
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.
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
});
});
});
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
});
});
}
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
});
}
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.