I'm trying to use yahoo finance data to generate a Highcharts candlestick chart like this http://www.highcharts.com/stock/demo/candlestick-and-volume. But I keep getting this error: http://www.highcharts.com/errors/15
Highcharts Error #15
Highcharts expects data to be sorted
This happens when you are trying to create a line series or a stock chart where the data is not sorted in ascending X order. For performance reasons, Highcharts does not sort the data, instead it is required that the implementer pre-sorts the data.
My code is as follows.
$(function () {
$.getJSON('http://websitescraper.heroku.com/?url=http://ichart.finance.yahoo.com/table.csv?s=000338.sz&callback=?', function (csvdata) {
//console.log(csvdata);
var arr = csvdata.split('\n').slice(1);
var data = [];
for (var i = arr.length-1; i >= 0; --i) {
//console.log(arr[i]);
var line = arr[i].split(',');
line[0] = Date.parse(line[0]);
line = $.map(line, function(v) {
return parseFloat(v);
});
line = line.slice(0,6);
//var j = JSON.stringify(line.slice(0,0+6));
console.log(line);
data.push(line);
}
data = JSON.stringify(data.slice(1));
console.log(data);
run(data);
});
});
function run(data) {
// split the data set into ohlc and volume
var ohlc = [],
volume = [],
dataLength = data.length,
// set the allowed units for data grouping
/*groupingUnits = [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]],*/
i = 0;
for (i; i < dataLength; i += 1) {
ohlc.push([
data[i][0], // the date
data[i][1], // open
data[i][2], // high
data[i][3], // low
data[i][4] // close
]);
volume.push([
data[i][0], // the date
data[i][5] // the volume
]);
}
// create the chart
$('#container2').highcharts('StockChart', {
rangeSelector: {
selected: 1
},
title: {
text: 'Shanghai Composite Index Historical'
},
yAxis: [{
labels: {
align: 'right',
x: -3
},
title: {
text: 'OHLC'
},
height: '60%',
lineWidth: 2
}, {
labels: {
align: 'right',
x: -3
},
title: {
text: 'Volume'
},
top: '65%',
height: '35%',
offset: 0,
lineWidth: 2
}],
series: [{
type: 'candlestick',
upLineColor: 'red',
downLineColor: 'green',
name: 'SSE',
data: ohlc,
/*dataGrouping: {
units: groupingUnits
}*/
}, {
type: 'column',
name: 'Volume',
data: volume,
yAxis: 1
/*dataGrouping: {
units: groupingUnits
}*/
}]
});
}
Can somebody help? Thanks a lot!
The problem is the data = JSON.stringify(data.slice(1));. It turns the array to a string, therefore Highstock doesn't recognize it. Remove JSON.stringify and it will work fine:
data = data.slice(1);
Here's the DEMO.
Related
Here's what I'm trying to do:
Use papa parse to parse a CSV file.
Create two JS Objects(ohlc and volume) with that parsed data.
Then that data is used to create a highstocks chart.
Parsing with papa parse example:
function doStuff(data) {
//do stuff here
console.log(data);
}
function parseData(url, callBack) {
Papa.parse(url, {
download: true,
dynamicTyping: true,
complete: function(results) {
callBack(results.data);
}
});
}
parseData("https://www.quandl.com/api/v3/datasets/WIKI/AAPL.csv", doStuff);
A working Highchart example: jsfiddle
Me trying to combine the top two examples: jsfiddle
$(function () {
var ohlc = [],
volume = [],
dataLength = data.length,
// set the allowed units for data grouping
groupingUnits = [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]],
i = 1;
function parseData(url, callBack) {
Papa.parse(url, {
download: true,
dynamicTyping: true,
complete: function(results) {
callBack(results.data);
}
});
}
function setObjects(data) {
console.log(data[i][0]);
for (i; i < dataLength; i += 1) {
ohlc.push([
data[i][0], // the date
data[i][1], // open
data[i][2], // high
data[i][3], // low
data[i][4] // close
]);
volume.push([
data[i][0], // the date
data[i][5] // the volume
]);
}
}
parseData("https://www.quandl.com/api/v3/datasets/WIKI/AAPL.csv", setObjects);
// create the chart
$('#container').highcharts('StockChart', {
rangeSelector: {
selected: 1
},
title: {
text: 'AAPL Historical'
},
yAxis: [{
labels: {
align: 'right',
x: -3
},
title: {
text: 'OHLC'
},
height: '60%',
lineWidth: 2
}, {
labels: {
align: 'right',
x: -3
},
title: {
text: 'Volume'
},
top: '65%',
height: '35%',
offset: 0,
lineWidth: 2
}],
series: [{
type: 'candlestick',
name: 'AAPL',
data: ohlc,
dataGrouping: {
units: groupingUnits
}
}, {
type: 'column',
name: 'Volume',
data: volume,
yAxis: 1,
dataGrouping: {
units: groupingUnits
}
}]
});
});
Can anyone help me out with what I am doing wrong? I know two things that haven't been done. The data needs to be reverse so that it is in ascending order by date. And the date needs to be converted to milliseconds. However it would help me to get the current data at least inserting to the objects first and then go from there.
This bit
var i = 1;
dataLength = data.length;
Should be in the first lines of the setObjects function, where data is present and the value dataLength is actually used.
I have some UV Sensors (currently running on Thingspeak) - but I need to have multiple series on the same chart, so I made a sample .php page on my website.
I have the basic chart working nicely, but I have not been able to get it to do live updates - my coding skills are very lacking & I would appreciate any help I can get!
The sample chart is here: http://www.sesupply.co.nz/test.php
I have the code on JSFiddle here: https://jsfiddle.net/SESupply/9xn65qrL/9/
// variables for the first series
var series_1_channel_id = 43330;
var series_1_field_number = 4;
var series_1_read_api_key = '7ZPHNX2SXPM0CA1K';
var series_1_results = 480;
var series_1_color = '#d62020';
var series_1_name = 'Zims Sensor';
// variables for the second series
var series_2_channel_id = 45473;
var series_2_field_number = 2;
var series_2_read_api_key = 'N12T3CWQB5IWJAU9';
var series_2_results = 480;
var series_2_color = '#00aaff';
var series_2_name = 'UVM30A';
// chart title
var chart_title = 'UV Sensors Zim / UVM30A';
// y axis title
var y_axis_title = 'UV Index';
// user's timezone offset
var my_offset = new Date().getTimezoneOffset();
// chart variable
var my_chart;
// when the document is ready
$(document).on('ready', function () {
// add a blank chart
addChart();
// add the first series
addSeries(series_1_channel_id, series_1_field_number, series_1_read_api_key, series_1_results, series_1_color, series_1_name);
// add the second series
addSeries(series_2_channel_id, series_2_field_number, series_2_read_api_key, series_2_results, series_2_color, series_2_name);
});
// add the base chart
function addChart() {
// variable for the local date in milliseconds
var localDate;
// specify the chart options
var chartOptions = {
chart: {
renderTo: 'chart-container',
defaultSeriesType: 'spline',
zoomType: 'x', // added here
panning: true,
panKey: 'shift',
backgroundColor: '#ffffff',
events: {
load: addSeries
}
},
title: {
text: chart_title
},
subtitle: {
text: 'Click and drag to zoom in. Hold down shift key to pan.'
},
plotOptions: {
series: {
marker: {
radius: 2
},
animation: true,
step: false,
borderWidth: 0,
turboThreshold: 0
}
},
scrollbar: {
enabled: true
// barBackgroundColor: 'gray',
// barBorderRadius: 7,
// barBorderWidth: 0,
// buttonBackgroundColor: 'gray',
// buttonBorderWidth: 0,
// buttonArrowColor: 'yellow',
// buttonBorderRadius: 7,
// rifleColor: 'yellow',
// trackBackgroundColor: 'white',
// trackBorderWidth: 1,
// trackBorderColor: 'silver',
// trackBorderRadius: 7
},
tooltip: {
// reformat the tooltips so that local times are displayed
formatter: function () {
var d = new Date(this.x + (my_offset * 60000));
var n = (this.point.name === undefined) ? '' : '<br>' + this.point.name;
return this.series.name + ':<b>' + this.y + '</b>' + n + '<br>' + d.toDateString() + '<br>' + d.toTimeString().replace(/\(.*\)/, "");
}
},
xAxis: {
type: 'datetime',
title: {
text: 'Date'
}
},
rangeSelector: {
enabled: true,
buttons: [{
type: 'minute',
count: 60,
text: 'Hour'
}, {
type: 'day',
count: 1,
text: 'Day'
}, {
type: 'week',
count: 1,
text: 'Week'
}, {
type: 'all',
text: 'All'
}]
},
yAxis: {
title: {
text: y_axis_title
}
},
exporting: {
enabled: true
},
legend: {
enabled: true
},
credits: {
text: 'ThingSpeak.com',
href: 'https://thingspeak.com/',
style: {
color: '#D62020'
}
}
};
// draw the chart
my_chart = new Highcharts.Chart(chartOptions);
}
// add a series to the chart
function addSeries(channel_id, field_number, api_key, results, color, name) {
var field_name = 'field' + field_number;
// get the data with a webservice call
$.getJSON('https://api.thingspeak.com/channels/' + channel_id + '/fields/' + field_number + '.json?offset=0&round=2&results=' + results + '&api_key=' + api_key, function (data) {
// blank array for holding chart data
var chart_data = [];
// iterate through each feed
$.each(data.feeds, function () {
var point = new Highcharts.Point();
// set the proper values
var value = this[field_name];
point.x = getChartDate(this.created_at);
point.y = parseFloat(value);
// add location if possible
if (this.location) {
point.name = this.location;
}
// if a numerical value exists add it
if (!isNaN(parseInt(value))) {
chart_data.push(point);
}
});
// add the chart data
my_chart.addSeries({
data: chart_data,
name: name,
color: color
});
});
setTimeout(addSeries, 1000);
}
cache: false;
// converts date format from JSON
function getChartDate(d) {
// offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct
return Date.parse(d) - (my_offset * 60000);
}
I have tried following the livedata example but seem to be failing miserably. The sensors update about every 60 seconds (only during the day - as there is no UV at night, I put the sensors into "sleep" mode to save battery power)
Need to create a chart similar to the one you see here: http://imgbin.org/index.php?page=image&id=20802. So far managed to put together a jsfiddle that displays both charts but its far from complete.
How would you correctly place the bottom chart (column) over the first to achieve the effect you see in example? http://jsfiddle.net/e106L47h/6/
$(function () {
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=aapl-ohlcv.json&callback=?', function (data) {
// split the data set into ohlc and volume
var ohlc = [],
volume = [],
dataLength = data.length,
// set the allowed units for data grouping
groupingUnits = [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]],
i = 0;
for (i; i < dataLength; i += 1) {
ohlc.push([
data[i][0], // the date
data[i][1], // open
data[i][2], // high
data[i][3], // low
data[i][4] // close
]);
volume.push([
data[i][0], // the date
data[i][5] // the volume
]);
}
// create the chart
$('#container').highcharts('StockChart', {
navigator: {
enabled: false
},
rangeSelector: {
selected: 1,
inputEnabled: false
},
credits: {
enabled: false
},
yAxis: [{
height: '60%',
lineWidth: 0
}, {
top: '65%',
height: '35%',
offset: 0,
lineWidth: 0,
// gridLineWidth: 0,
labels:
{
//enabled: false
}
}],
series: [{
type: 'candlestick',
name: 'AAPL',
data: ohlc,
dataGrouping: {
units: groupingUnits
}
}, {
type: 'column',
name: 'Volume',
data: volume,
yAxis: 1,
dataGrouping: {
units: groupingUnits
}
}]
});
});
});
I would remove the height 35%/65% split and show both series with the same baseline.
Then I would hide the volume axis to reduce clutter.
You can modify the height of the volume bars by setting the max of the second hidden axis rather than the height (I have used maxValue*3 to approximate your 35% height value).
Find the maximum value in the volume series like this:
var maxVolume = Math.max.apply(Math, volume.map(function(v) { return v[1]}))
Example: http://jsfiddle.net/cvezpup7/
I want to process data from a .csv file to:
Divide the data coming in by 10, e.g., 588 => 58.8
Remove outliers from the data or to change to zero, e.g., 8888 => 0
Here is my javascript, I appreciate the help!!
$.get('http://www.geoinc.org/Dropbox/geo/sites/GC_ROOM/charts/hassayampa.csv', function(data)
{
// Split the lines
var lines = data.split('\n');
var i = 0;
var csvData = [];
// Iterate over the lines and add categories or series
$.each(lines, function(lineNo, line)
{
csvData[i] = line.split(',');
i = i + 1;
});
var columns = csvData[0];
var categories = [], series = [];
for(var colIndex=0,len=columns.length; colIndex<len; colIndex++)
{
//first row data as series's name
var seriesItem=
{
data:[],
name:csvData[0][colIndex]
};
for(var rowIndex=1,rowCnt=csvData.length; rowIndex<rowCnt; rowIndex++)
{
//first column data as categories,
if (colIndex == 0)
{
categories.push(csvData[rowIndex][0]);
}
else if(parseFloat(csvData[rowIndex][colIndex])) // <-- here
{
seriesItem.data.push(parseFloat(csvData[rowIndex][colIndex]));
}
};
//except first column
if(colIndex>0)series.push(seriesItem);
}
// Create the chart
var chart = new Highcharts.Chart(
{
chart:
{
renderTo: 'test',
type: 'line',
zoomType: 'x',
},
title: {
text: 'Daily Average Temperature',
x: -20 //center
},
subtitle: {
text: 'Source: HASSAYAMPA',
x: -20
},
xAxis:
{
categories: categories,
labels:
{
step: 80,
},
tickWidth: 0
},
yAxis:
{
title: {
text: 'Temperature (\xB0C)'
},
//min: 0
},
tooltip:
{
formatter: function()
{
return '<b>'+ this.series.name +'</b><br/>'+ this.x +': '+ this.y +'\xB0C';
}
},
legend:
{
layout: 'vertical',
//backgroundColor: '#FFFFFF',
//floating: true,
align: 'left',
//x: 100,
verticalAlign: 'top',
//y: 70,
borderWidth: 0
},
plotOptions:
{
area:
{
animation: false,
stacking: 'normal',
lineColor: '#666666',
lineWidth: 1,
marker:
{
lineWidth: 1,
lineColor: '#666666'
}
}
},
series: series
});
});
I'm not sure what you are asking, but I'll take a shot at it...
First things first, this snippet of code is not sound. It'll not only skip NaNs but 0s as well (which is valid numeric data):
else if(parseFloat(csvData[rowIndex][colIndex]))
{
seriesItem.data.push(parseFloat(csvData[rowIndex][colIndex]));
}
Instead I'd do:
//first column data as categories,
if (colIndex == 0)
{
categories.push(csvData[rowIndex][0]);
}
else
{
var fVal = parseFloat(csvData[rowIndex][colIndex]);
if (!isNaN(fVal))
{
fVal = fVal / 10.0; //<-- here's the division!!
seriesItem.data.push(fVal);
}
}
As far as how to exclude outliers, the big question there is how do you want to exclude outliers? A simple min/max criteria? Then just check that fVal is within those limits before seriesItem.data.push...
I cannot figure out how to get the 'to' and 'from' dates from my data into the tooltips. Tried various methods I found around SO. Anyone got any tips? I normally load data from CSV. Right now the data is hard-coded in the code.
var options = {
chart: {
zoomType: 'y',
borderWidth: '0',
borderRadius: '15',
renderTo: 'container',
inverted: true,
backgroundColor: {
linearGradient: [0, 0, 500, 500],
stops: [
[0, 'rgb(44, 44, 58)'],
[1, 'rgb(62, 62, 62)']
]
},
plotBackgroundColor: 'rgba(255, 255, 255, .9)'
},
tooltip: {
formatter: function () {
var point = this.point;
return '<b>' + point.category +
'</b><br/>' + Highcharts.dateFormat('%b %e, %Y', this.y) +
' - ' + Highcharts.dateFormat('%b %e, %Y', this.series[0]);
}
},
legend: {
enabled: false
},
title: {
text: 'EVMS Calendar'
},
xAxis: {
categories: []
},
plotOptions: {
series: {
grouping: false
}
},
yAxis: {
type: 'datetime',
minRange: '604800000',
startOnTick: false,
endOnTick: false,
title: {
text: ''
}
},
series: []
},
categories = [];;
//// This is the data processing section \\\\
// Hard Coded Data
var data ="valid data";
// Split the lines
var lines = data.split('\n');
// Iterate over the lines and add categories or series
// Split the data by comma
// Get the number of items in the object (iLen)
// Series start
// Series type is columnrange
// Servies name is item 0 of the line (employees name)
$.each(lines, function (lineNo, line) {
var items = line.split(','),
iLen = items.length,
series = {
type: 'columnrange',
data: [],
name: items[0]
};
// Start categories
// for each items (0) get the row data (dates) and push to categories(line number, from and to)
categories.push(items[0]);
for (var i = 1; i < iLen; i += 2) {
var from = (new Date(items[i])).getTime(),
to = (new Date(items[i + 1])).getTime();
if (!isNaN(from) && !isNaN(to)) {
series.data.push([lineNo, from, to]);
}
};
options.series.push(series);
});
options.xAxis.categories = categories;
// Create the chart
var chart = new Highcharts.Chart(options);
ah, my bad. Its this.point.high / low. I found out by looking through the elements in chrome