Whenever we are dealing with large amounts of data, it can be a better approach to fetch data asynchronously based on the range selected by the user (by zooming, panning, etc). This reduces the time required to fetch data and also speeds up the overall rendering of Charts. Though CanvasJS can easily deal with hundreds of thousands of dataPoints without much lag, you will notice significant performance improvement by using asynchronous loading technique while dealing with millions of dataPoints.
This technique allows you control the resolution of data being shown based on the range. You can also preprocess data on the server side based on the range selected which can be useful while displaying various indicator like Moving Average, MACD, etc. This will reduce processing on the client side and improve overall performance of your application.
Below is an example where we create a StockChart by asynchronously fetching data from an external data source each time the range is updated(by panning, zooming, etc).
JSON format which we will be using is as shown below:
[{dateTime: timestamp, open: number, high: number, low: number, close: number}]
To fetch the data asynchronously we will be doing AJAX calls (using jQuery) and convert the data received to CanvasJS format – { x: Number/Date-Time , y: Number}. Please refer to this documentation page for more information on data formats supported.
var dataPoints1 = [],dataPoints2 = []; var stockChart = new CanvasJS.StockChart("chartContainer",{ title:{ text:"BTC/USD" }, charts: [{ axisY2: { prefix: "$" }, data: [{ yValueFormatString: "$#,###.##", axisYType: "secondary", type: "candlestick", dataPoints : dataPoints1 }] }], navigator: { data: [{ type: "line", dataPoints : dataPoints2 }] } }); $.getJSON("https://canvasjs.com/services/data/datapoints-bitcoinusd.php", function(data) { for(var i = 0; i < data.length; i++){ dataPoints1.push({x: new Date(data[i].dateTime*1000), y: [Number(data[i].open), Number(data[i].high), Number(data[i].low), Number(data[i].close)]}) dataPoints2.push({x: new Date(data[i].dateTime*1000), y: Number(data[i].close)}); } stockChart.render(); });
In the above example data is fetched once and doesn’t get updated upon changing the range. The data remains at the low resolution even after updating the range.
Now let’s try to update the data to high resolution when you zoom-in by fetching data dynamically, when the range gets updated. We can update the data whenever rangeChanged event is fired. Also, we will be setting dynamicUpdate property of navigator to false, so that range of StockChart gets updated only upon releasing the mouse – while updating range using slider.
var dataPoints1 = [], dataPoints2 = []; var stockChart = new CanvasJS.StockChart("chartContainer",{ title:{ text:"Async Data Loading in StockChart" }, rangeChanged: rangeChanged, charts: [{ axisY2: { prefix: "$" }, data: [{ type: "candlestick", yValueFormatString: "$#,###.##", axisYType: "secondary", dataPoints : dataPoints1 }] }], navigator: { dynamicUpdate: false, data: [{ dataPoints: dataPoints2 }], slider: { minimum: new Date(2015, 05, 01), maximum: new Date(2019, 05, 01) } } }); function addData(data) { stockChart.options.charts[0].data[0].dataPoints = []; for(var i = 0; i < data.length; i++){ stockChart.options.charts[0].data[0].dataPoints.push({x: new Date(data[i].dateTime*1000), y: [ Number(data[i].open), Number(data[i].high), Number(data[i].low), Number(data[i].close) ]}); } stockChart.render(); } function rangeChanged(e) { var minimum = parseInt(e.minimum / 1000); //Converting milliseconds to seconds var maximum = parseInt(e.maximum / 1000); //Converting milliseconds to seconds var url = "https://canvasjs.com/services/data/datapoints-bitcoinusd.php?minimum="+minimum+"&maximum="+maximum; $("#loader").css("display", "block"); $.getJSON(url, function(data) { addData(data); $("#loader").css("display", "none"); }); } $("#loader").css("display", "block"); $.getJSON("https://canvasjs.com/services/data/datapoints-bitcoinusd.php", function(data) { for(var i = 0; i < data.length; i++){ //dateTime is multiplied by 1000 to convert it to milliseconds from seconds dataPoints1.push({x: new Date(data[i].dateTime*1000), y: [Number(data[i].open), Number(data[i].high), Number(data[i].low), Number(data[i].close)]}); dataPoints2.push({x: new Date(data[i].dateTime*1000), y: Number(data[i].close)}); } $("#loader").css("display", "none"); stockChart.render(); });