(*日本語の記事はこちら)
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.
- Moment.js: Date & time library
- Chart.js: Chart library
- chartjs-plugin-streaming: Chart.js plugin for streaming data
- Pusher: Pub/Sub messaging library
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.