Chart in javascript doesn't change color - javascript
I have a problem in javascript with building financial candlestick charts. I made a chart with apex.js and it displays the correct data where it should be but the color of the chart doesn't change, when the stock price is going up candlestick should be green when it goes down it should be red but on some stocks candlestick in always red and on some stocks it works fine. Here are the images, both charts use the same code but different data because it's different stock but that doesn't mean it should be displayed like this.
Here is code for chart:
<div id="chart">
</div>
<script>
var options = {
series: [{
name: 'OHLC',
data: [
{% for stock in stocks %}
{
x: new Date("{{stock.date}}"),
y: [Number("{{stock.open}}"), Number("{{stock.high}}"), Number("{{stock.low}}"), Number("{{stock.price}}")],
},
{% endfor %}
]
},
],
chart: {
type: 'candlestick',
},
title: {
text: '{{ticker}} Stock ',
align: 'center'
},
yaxis: {
tooltip: {
enabled: true
}
}
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
</script>
I am using Django in the backend so here is a function that returns chart data:
#login_required(login_url='stock:login')
def chart(request, ticker):
stocks = Stock.objects.filter(ticker = ticker).order_by('date')
context = {'stocks':stocks, 'ticker':ticker}
return render(request, 'stock_app/chart.html', context)
I am struggling with this for a few days and didn't even make slight progress, can anyone help me or at least tell me where the issue could be I would be really thankful. I check the database, data, and code, switched a few services, and used chart.js, plotly, and a few others and it's always the same issue. I also checked data on yahoo finance for stocks that are not displayed correctly and data is correct.
Usually, when you replace libraries and the issue prevails, the problem cannot be the library; instead the provided data might contain wrong information that lead to an indifferent resolution on your chart.
I see that you don't specify the candlestick colors specifically inside the specification, so this might be worth a shot, albeit its probably only basis configuration:
plotOptions: {
candlestick: {
colors: {
upward: '#3C90EB',
downward: '#DF7D46'
}
}
}
https://apexcharts.com/docs/chart-types/candlestick/
However, if the problem is the data itself you should rather provide an excerpt of a faulty stock and also one where it works just fine. Without stepping too deeply into Apex, maybe High/Low or Opening/Closing is mixed or wrong.
It is hard to say what is going on without your dataset. However, I suspect a lack of configuration of your xaxis, especially because your time scale does not seem consistent. Could you put this in your options and see what happens?
xaxis: {
type: 'datetime'
},
You can also have a category axis with a candlestick chart, but you may need extra configuration (a formatter, for example...). See this official demo: Category x-axis – ApexCharts.js
I paste the example below for convenience.
var options = {
series: [{
name: 'candle',
data: [
{
x: new Date(1538778600000),
y: [6629.81, 6650.5, 6623.04, 6633.33]
},
{
x: new Date(1538780400000),
y: [6632.01, 6643.59, 6620, 6630.11]
},
{
x: new Date(1538782200000),
y: [6630.71, 6648.95, 6623.34, 6635.65]
},
{
x: new Date(1538784000000),
y: [6635.65, 6651, 6629.67, 6638.24]
},
{
x: new Date(1538785800000),
y: [6638.24, 6640, 6620, 6624.47]
},
{
x: new Date(1538787600000),
y: [6624.53, 6636.03, 6621.68, 6624.31]
},
{
x: new Date(1538789400000),
y: [6624.61, 6632.2, 6617, 6626.02]
},
{
x: new Date(1538791200000),
y: [6627, 6627.62, 6584.22, 6603.02]
},
{
x: new Date(1538793000000),
y: [6605, 6608.03, 6598.95, 6604.01]
},
{
x: new Date(1538794800000),
y: [6604.5, 6614.4, 6602.26, 6608.02]
},
{
x: new Date(1538796600000),
y: [6608.02, 6610.68, 6601.99, 6608.91]
},
{
x: new Date(1538798400000),
y: [6608.91, 6618.99, 6608.01, 6612]
},
{
x: new Date(1538800200000),
y: [6612, 6615.13, 6605.09, 6612]
},
{
x: new Date(1538802000000),
y: [6612, 6624.12, 6608.43, 6622.95]
},
{
x: new Date(1538803800000),
y: [6623.91, 6623.91, 6615, 6615.67]
},
{
x: new Date(1538805600000),
y: [6618.69, 6618.74, 6610, 6610.4]
},
{
x: new Date(1538807400000),
y: [6611, 6622.78, 6610.4, 6614.9]
},
{
x: new Date(1538809200000),
y: [6614.9, 6626.2, 6613.33, 6623.45]
},
{
x: new Date(1538811000000),
y: [6623.48, 6627, 6618.38, 6620.35]
},
{
x: new Date(1538812800000),
y: [6619.43, 6620.35, 6610.05, 6615.53]
},
{
x: new Date(1538814600000),
y: [6615.53, 6617.93, 6610, 6615.19]
},
{
x: new Date(1538816400000),
y: [6615.19, 6621.6, 6608.2, 6620]
},
{
x: new Date(1538818200000),
y: [6619.54, 6625.17, 6614.15, 6620]
},
{
x: new Date(1538820000000),
y: [6620.33, 6634.15, 6617.24, 6624.61]
},
{
x: new Date(1538821800000),
y: [6625.95, 6626, 6611.66, 6617.58]
},
{
x: new Date(1538823600000),
y: [6619, 6625.97, 6595.27, 6598.86]
},
{
x: new Date(1538825400000),
y: [6598.86, 6598.88, 6570, 6587.16]
},
{
x: new Date(1538827200000),
y: [6588.86, 6600, 6580, 6593.4]
},
{
x: new Date(1538829000000),
y: [6593.99, 6598.89, 6585, 6587.81]
},
{
x: new Date(1538830800000),
y: [6587.81, 6592.73, 6567.14, 6578]
},
{
x: new Date(1538832600000),
y: [6578.35, 6581.72, 6567.39, 6579]
},
{
x: new Date(1538834400000),
y: [6579.38, 6580.92, 6566.77, 6575.96]
},
{
x: new Date(1538836200000),
y: [6575.96, 6589, 6571.77, 6588.92]
},
{
x: new Date(1538838000000),
y: [6588.92, 6594, 6577.55, 6589.22]
},
{
x: new Date(1538839800000),
y: [6589.3, 6598.89, 6589.1, 6596.08]
},
{
x: new Date(1538841600000),
y: [6597.5, 6600, 6588.39, 6596.25]
},
{
x: new Date(1538843400000),
y: [6598.03, 6600, 6588.73, 6595.97]
},
{
x: new Date(1538845200000),
y: [6595.97, 6602.01, 6588.17, 6602]
},
{
x: new Date(1538847000000),
y: [6602, 6607, 6596.51, 6599.95]
},
{
x: new Date(1538848800000),
y: [6600.63, 6601.21, 6590.39, 6591.02]
},
{
x: new Date(1538850600000),
y: [6591.02, 6603.08, 6591, 6591]
},
{
x: new Date(1538852400000),
y: [6591, 6601.32, 6585, 6592]
},
{
x: new Date(1538854200000),
y: [6593.13, 6596.01, 6590, 6593.34]
},
{
x: new Date(1538856000000),
y: [6593.34, 6604.76, 6582.63, 6593.86]
},
{
x: new Date(1538857800000),
y: [6593.86, 6604.28, 6586.57, 6600.01]
},
{
x: new Date(1538859600000),
y: [6601.81, 6603.21, 6592.78, 6596.25]
},
{
x: new Date(1538861400000),
y: [6596.25, 6604.2, 6590, 6602.99]
},
{
x: new Date(1538863200000),
y: [6602.99, 6606, 6584.99, 6587.81]
},
{
x: new Date(1538865000000),
y: [6587.81, 6595, 6583.27, 6591.96]
},
{
x: new Date(1538866800000),
y: [6591.97, 6596.07, 6585, 6588.39]
},
{
x: new Date(1538868600000),
y: [6587.6, 6598.21, 6587.6, 6594.27]
},
{
x: new Date(1538870400000),
y: [6596.44, 6601, 6590, 6596.55]
},
{
x: new Date(1538872200000),
y: [6598.91, 6605, 6596.61, 6600.02]
},
{
x: new Date(1538874000000),
y: [6600.55, 6605, 6589.14, 6593.01]
},
{
x: new Date(1538875800000),
y: [6593.15, 6605, 6592, 6603.06]
},
{
x: new Date(1538877600000),
y: [6603.07, 6604.5, 6599.09, 6603.89]
},
{
x: new Date(1538879400000),
y: [6604.44, 6604.44, 6600, 6603.5]
},
{
x: new Date(1538881200000),
y: [6603.5, 6603.99, 6597.5, 6603.86]
},
{
x: new Date(1538883000000),
y: [6603.85, 6605, 6600, 6604.07]
},
{
x: new Date(1538884800000),
y: [6604.98, 6606, 6604.07, 6606]
},
]
}],
chart: {
height: 350,
type: 'candlestick',
},
title: {
text: 'CandleStick Chart - Category X-axis',
align: 'left'
},
annotations: {
xaxis: [
{
x: 'Oct 06 14:00',
borderColor: '#00E396',
label: {
borderColor: '#00E396',
style: {
fontSize: '12px',
color: '#fff',
background: '#00E396'
},
orientation: 'horizontal',
offsetY: 7,
text: 'Annotation Test'
}
}
]
},
tooltip: {
enabled: true,
},
xaxis: {
type: 'category',
labels: {
formatter: function(val) {
return dayjs(val).format('MMM DD HH:mm')
}
}
},
yaxis: {
tooltip: {
enabled: true
}
}
};
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs#1.11.7/dayjs.min.js"></script>
<div id="chart"></div>
Thanks to everyone who tried to help I found out what was the problem. The close price was misconfigured by the API provider so the close price was higher by a few cents than the low price which caused the error
Related
Show quarter markers instead of years in JSCharting Gantt Chart
I am new to this JSCharting library and working with it to create a Gantt Chart. When I map my data on the chart, it slices the Y Axis based on years. I am trying to slice it based on 3 months intervals. Instead of 2021, 2022, 2023, I want to show Q1, Q2, Q3, Q4 for each year. One quick and dirty solution I found is below, to create markers on Y Axis like this: { yAxis: { markers: [2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028].reduce( (all: { label: { text: string; color: string }; value: string }[], year) => { return [ ...all, ...[1, 4, 7, 10].map((month) => ({ label: { text: month === 1 ? "Q1" : month === 4 ? "Q2" : month === 7 ? "Q3" : "Q4", color: "#ddd", }, color: "#ddd", value: `${month}/1/${year}`, legendEntry: { visible: false, }, })), ]; }, [] ), }, } However, when I do that, first line of data covers the quarter labels like so. Is there a proper way to do this and show it in the bottom with along with years? I'd appreciate any help.
You can use calendar patterns to create ticks for quarters and months more easily. Here's an example. var chart = JSC.chart('chartDiv', { debug: true, axisToZoom: 'x', margin_right: 10, xAxis: { scale_type: 'time', defaultTick_enabled: false, customTicks: [ { value_pattern: 'month', label_text: '%min' }, { value_pattern: 'quarter', label_text: qTickText }, { value_pattern: 'year', label_text: '%min' } ] }, legend_visible: false, title_label_text: 'Apple iPhone sales (in million units)', series: [ { type: 'line spline', defaultPoint: { marker: { outline: { width: 2, color: 'white' } } }, points: [ { x: '04/1/2016', y: 74.78 }, { x: '07/1/2016', y: 51.19 }, { x: '10/1/2016', y: 40.4 }, { x: '1/1/2017', y: 45.51 }, { x: '04/1/2017', y: 78.29 }, { x: '07/1/2017', y: 50.76 }, { x: '10/1/2017', y: 41.03 }, { x: '1/1/2018', y: 46.68 }, { x: '04/1/2018', y: 67.32 }, { x: '07/1/2018', y: 52.22 }, { x: '10/1/2018', y: 41.3 }, { x: '1/1/2019', y: 46.89 } ] } ] }); function qTickText(v) { return dateToQuarter(v[0]); } function dateToQuarter(d) { d = new Date(d); return 'Q' + (Math.floor(d.getMonth() / 3) + 1); }
Change highcharts data label position
This is my jsfiddle link http://jsfiddle.net/bb1m6xyk/1/ I want that all my labels like my data: 0 etc are positioned at the base and in center of each zone. $('#container').highcharts({ chart: { type: 'area' }, yAxis: { title: { text: 'Percent' } }, plotOptions: { area: { enableMouseTracking: false, showInLegend: false, stacking: 'percent', lineWidth: 0, marker: { enabled: false }, dataLabels: { className:'highlight', enabled: true, formatter: function () { console.log(this); return this.point.myData } } } }, series: [{ name: 'over', color: 'none', data: overData }, { id: 's1', name: 'Series 1', data: data, showInLegend: true, zoneAxis: 'x', zones: zones }] }); Is this possible? I tried it using className on dataLabels but it doesn't take that into effect. Any help is appreciated.
There are a few ways to render labels on a chart. The Renderer Live example: http://jsfiddle.net/11rj6k6p/ You can use Renderer.label to render the label on the chart - this is a low level approach but it gives you full control how the labels will be rendered. You can loop the zones and set x and y attributes of the labels, e.g. like this: const labels = ['l1', 'l2', 'l3', 'l4', 'l5'] function drawLabels() { const zonesLabels = this.zonesLabels const series = this.get('s1') const { yAxis, xAxis } = series const y = yAxis.toPixels(0) - 20 // -20 is an additional offset in px series.zones.reduce((prev, curr, i) => { if (curr.value !== undefined) { const x = (xAxis.toPixels(prev.value) + xAxis.toPixels(curr.value)) / 2 if (!zonesLabels[i]) { zonesLabels.push( this.renderer.label(labels[i], x, y).add().attr({ align: 'center', zIndex: 10 }) ) } else { zonesLabels[i].attr({ x, y }) } } return curr }, { value: series.dataMin }) } Then set the function on load - to render the labels, and on redraw - to reposition the labels if the chart size changed. chart: { type: 'area', events: { load: function() { this.zonesLabels = [] drawLabels.call(this) }, redraw: drawLabels } }, The annotations module Live example: http://jsfiddle.net/a5gb7aqz/ If you do not want to use the Renderer API, you can use the annotations module which allows to declare labels in a chart config. Add the module <script src="https://code.highcharts.com/modules/annotations.js"></script> Map zones to the labels config object const labels = ['l1', 'l2', 'l3', 'l4', 'l5'] function annotationsLabels() { const zonesLabels = [] zones.reduce((prev, curr, i) => { zonesLabels.push({ text: labels[i], point: { x: (prev.value + curr.value) / 2, y: 0, xAxis: 0, yAxis: 0 } }) return curr }, { value: 0 }) return zonesLabels } Set the annotations options annotations: [{ labels: annotationsLabels(), labelOptions: { shape: 'rect', backgroundColor: 'none', borderColor: 'none', x: 0, y: 0 } }], Data labels and a new series Live example: http://jsfiddle.net/wpk1495g/ You can create a new scatter series which will not respond to mouse events and it won't be visible in the legend. The labels can be displayed as data labels. Map zones to series points const labels = ['l1', 'l2', 'l3', 'l4', 'l5'] function seriesData() { const points = [] zones.reduce((prev, curr, i) => { points.push( { x: (prev.value + curr.value) / 2, y: 50, dataLabels: { enabled: true, format: labels[i] } }) return curr }, { value: 0 }) return points } Set the series options in the chart config , { type: 'scatter', enableMouseTracking: false, showInLegend: false, data: seriesData(), zIndex: 10, color: 'none', dataLabels: { style: { textOutline: false }, x: 0, y: 0 } } Output
Canvasjs: different settings according to screen size
I'm using CanvasJs for a bar chart, but I would like to have different settings according to screen size. For example, I would like to hide labels completely on the axisY for screens < 480px. How can this be done? It seems that media queries and css can't be used to customize the charts. Here's an example: <script type="text/javascript"> window.onload = function () { var chart = new CanvasJS.Chart("chartContainer", { title: { text: "Understanding Labels" }, axisY: { labelFontSize: 20 }, axisX: { labelAngle: -30 }, data: [ { type: "column", dataPoints: [ { y: 10, label: "Apples" }, { y: 15, label: "Mangos" }, { y: 25, label: "Oranges" }, { y: 30, label: "Grapes" }, { y: 28, label: "Bananas" } ] } ] }); chart.render(); } </script> I experimented with something like this which obviously doesn't work: axisY: { labelFormatter: function(e) { if ($(window).width() < 480) { return ""; }, }, },
You can define a function for labelFormatter outside of axisY object. axisY: { labelFormatter: axisYLabels, }, You can modify the labels of axisY here based on width of the window. function axisYLabels(e) { if ($(window).width() < 480) { return ""; } else { return e.value; } } You can see a working fiddle here.
Restacking cumulative columns in Highcharts marimekko charts
I've got a basic variable width column chart (aka Marimekko) set up using Highcharts but am having trouble getting it to restack the columns properly to eliminate the data gap once a series has been removed or hidden. JSFIDDLE DEMO <-- I've set up a demo of the issue here. You'll notice clicking on a legend item removes the series from the chart, but it also removes all of the following data points in the array (i.e. clicking on series C removes series C, D, and E whereas it should redraw to A-B-D-E). Since the y-axis data is meant to display a cumulative sum of all series, these should re-shuffle as adjacent columns with no gaps. How can I get this to render properly? THIS POST uses similar demo code and attempting to solve the same problem, however the answer is somewhat elusive and I am unable to get it working. Thanks in advance! $(function () { var dataArray = [ { name: 'A', x: 200, y: 120 }, { name: 'B', x: 380, y: 101 }, { name: 'C', x: 450, y: 84 }, { name: 'D', x: 198, y: 75 }, { name: 'E', x: 95, y: 55 } ]; function makeSeries(listOfData) { var sumX = 0.0; for (var i = 0; i < listOfData.length; i++) { sumX += listOfData[i].x; } var allSeries = [] var x = 0.0; for (var i = 0; i < listOfData.length; i++) { var data = listOfData[i]; allSeries[i] = { name: data.name, data: [ [x, 0], [x, data.y], { x: x + data.x / 2.0, y: data.y, dataLabels: { enabled: false, format: data.x + ' x {y}' } }, [x + data.x, data.y], [x + data.x, 0] ], w: data.x, h: data.y }; x += data.x + 0; } return allSeries; } $('#container').highcharts({ chart: { type: 'area' }, xAxis: { tickLength: 0, labels: { enabled: true} }, yAxis: { title: { enabled: false} }, plotOptions: { series: { events: { legendItemClick: function () { var pos = this.index; var sname = this.name; var chart = $('#container').highcharts(); while(chart.series.length > 0) { chart.series[pos].remove(true); } dataArray[pos]= { name: sname, x: 0, y: 0 }; chart.series[0].setData(dataArray); } } }, area: { lineWidth: 0, marker: { enabled: false, states: { hover: { enabled: false } } } } }, series: makeSeries(dataArray) }); });
Why does this Highcharts graph only show tooltip for the first and last nodes?
I'm just starting out with Highcharts and I'm having a little trouble showing a tooltip for each data item in my graph. At the moment, it just shows a tooltip for the first and last item. var chart1; // globally available $(document).ready(function () { var options = { chart: { renderTo: 'AssessmentChart', marginRight: 100, marginBottom: 40 }, title: { text: 'Assessment history', x: -20 //center }, subtitle: { text: 'Assessment history for this patient', x: -20 //center }, xAxis: { type: 'datetime' }, yAxis: { title: { text: 'Score' }, max: 72, min: 0 }, tooltip: { formatter: function() { return '<b>'+ this.y +'</b>'; } }, legend: { layout: 'vertical', align: 'right', verticalAlign: 'top', x: -10, y: 100, borderWidth: 0 }, series: [] }; // Get the main assessment data... var dlqiData = [], hdnDlqiData = $("#hdnDlqiData"); if (hdnDlqiData.length > 0) { var dlqiDataJson = $.parseJSON(hdnDlqiData.val()); $.each(dlqiDataJson, function (i, item) { dlqiData.push( { x: stringToUtcDate(item.dateCreated), y: item.calculatedScore } ); }); options.series.push({ type: 'spline', name: 'DLQI', data: dlqiData }); } chart1 = new Highcharts.Chart(options); function stringToUtcDate(datestring) { var date = datestring.split('/'); return Date.UTC(parseInt(date[2], 10), parseInt(date[1], 10) - 1, parseInt(date[0], 10)); } }); I'm pulling data in from a hidden form field, so here's a Fiddle showing this in action: http://jsfiddle.net/gfyans/LjRGk/3/ Here's one of the official Fiddle's, http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/highcharts/demo/line-basic/, and they manage it OK, but I can't see from that what I'm dong wrong/differently. I know it'll be something insanely easy, but I can't see it. Can anyone point me in the right direction? Thanks, Greg.
You need to have your data in ascending date order if using a datetime x-axis.