TradingView - Lightweight charts - Realtime histogram ( volume indicator) - lightweight-charts

I managed to get the real time example to work:
https://jsfiddle.net/TradingView/yozeu6k1/
I tried to get a real time histogram underneath, as the usual volume indicator and the behavior is random.
A snapshot of the chart:
enter image description here
As we can see the starting point of those bars differ one from another.
Series definition:
const volumeSeries = chart.addHistogramSeries({
priceFormat: {
type: 'volume',
},
priceScaleId: '',
scaleMargins: {
top: 0.8,
bottom: 0,
}
});
Update:
volumeSeries.update({
time: data.time,
value: data.volume
});
Can anyone point me to an example in order to get a candlestick chart with a volume indicator to work? Both updating in real time.

I got it to work, basically the issue was that the histogram understands negative values as a down facing bar, so in order to show a volume indicator we have to show the absolute value of the volume and change the color.
A working example at: https://jsfiddle.net/rondolfo/0zg7u9tv/57/
//colours
var green = 'rgb(38,166,154)';
var red = 'rgb(255,82,82)';
var black = '#000000';
var white = 'rgba(255, 255, 255, 0.9)';
var grey = 'rgba(42, 46, 57, 0.5)';
// chart definition
var chart = LightweightCharts.createChart(document.body, {
width: 800,
height: 400,
layout: {
backgroundColor: black,
textColor: white,
},
grid: {
vertLines: {
visible: false,
},
horzLines: {
color: grey,
},
},
crosshair: {
mode: LightweightCharts.CrosshairMode.Normal,
}
});
chart.applyOptions({
timeScale: {
borderVisible: false,
borderColor: '#fff000',
visible: true,
timeVisible: true,
minBarSpacing: 0.0,
}
});
const candleStickSeries = chart.addCandlestickSeries({
upColor: green,
downColor: red,
wickUpColor: green,
wickDownColor: red,
borderVisible: false,
priceLineVisible: false,
});
const volumeSeries = chart.addHistogramSeries({
priceFormat: {
type: 'volume',
},
priceScaleId: '',
scaleMargins: {
top: 0.8,
bottom: 0.02,
}
});
//end chart definition
//data loading
jQuery.ajaxSetup({
async: false
});
var url = 'https://raw.githubusercontent.com/AnAlgoTrader/TradingView.LightWeightCharts.Example/main/InputData/prices.json';
var data = [];
$.get(url, function(result) {
data = JSON.parse(result);
});
//end data loading
//real time updates
var index = 0;
setInterval(function() {
if (index > data.length) return;
var item = data[index];
candleStickSeries.update({
time: item.time,
open: item.open,
high: item.high,
low: item.low,
close: item.close
});
var volumeColour = item.volume < 0 ? red : green;
volumeSeries.update({
time: item.time,
value: Math.abs(item.volume),
color: volumeColour
});
index++;
}, 1000);

Related

custom legend hide() does not remove data labels

I am building a project using React with a doughnut and bar chart. Working with Chart.js 3.xx.
I am trying to make my custom legend functional. I want to make data fractions disappear when the user clicks my legend items - like in the native legend, and optimally also remove the data and make the chart present it's updated data after removal.
I also use data labels to present percentage of the data on the fractions.
import ChartDataLabels from 'chartjs-plugin-datalabels';
I came across this topic: ChartJS - Show/hide data individually instead of entire dataset on bar/line charts
and used this suggested code there:
function chartOnClick(evt) {
let chart = evt.chart
const points = chart.getElementsAtEventForMode(evt, 'nearest', {}, true);
if (points.length) {
const firstPoint = points[0];
//var label = myChart.data.labels[firstPoint.index];
//var value = myChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index];
let datasetIndex = firstPoint.datasetIndex, index = firstPoint.index;
if (firstPoint.element.hidden != true) {
chart.hide(datasetIndex, index);
} else {
chart.show(datasetIndex, index);
}
}
}
options: { // chart options
onClick: chartOnClick
}
It almost works, but the hide() method doesn't remove the fraction's percentage data label when activated, whereas when clicking the native legend it does remove it entirely.
I tried looking in the plugin's docs but didn't manage to find how to remove a single label.
How can I achieve what I am looking for?
EDIT:
Options Object:
export const doughnutOptsObj = {
onClick: chartOnClick,
responsive: true,
maintainAspectRatio: false,
layout: { padding: { top: 16, bottom: 16 } },
hoverOffset: 32,
plugins: {
legend: {
display: true,
position: 'bottom',
},
datalabels: {
formatter: (value, dnct1) => {
let sum = 0;
let dataArr = dnct1.chart.data.datasets[0].data;
dataArr.map((data) => {
sum += Number(data);
});
let percentage = ((value * 100) / sum).toFixed() + '%';
return percentage;
},
color: ['#fbfcfd'],
font: { weight: 'bold' },
// display: false, <-- this works and makes all of the data labels disappear
},
},
};
It seems that the onClick function is working properly.
I have tried the attached code, leveraging on toggleDataVisibility API, and it's working as requested (codepen: https://codepen.io/stockinail/pen/abKNJqJ):
function chartOnClick(evt) {
let chart = evt.chart
const points = chart.getElementsAtEventForMode(evt, 'nearest', {}, true);
if (points.length) {
const firstPoint = points[0];
chart.toggleDataVisibility(firstPoint.index);
chart.update();
}
}

Filling a chart with a color above a specific Y value

I've been trying to fill a chart with color above certain Y value and I can't do it.
What I tried to do is writing a condition that if the value is >3.5 that area of the chart will be filled with a different color.
splineSeries.fill(function() {
if (this.value > 3.5)
return '#d3f335 0.4'
else
return '#cdf0a7 0.6'})
However, this doesn't work for me as it fills the whole area of the chart which values are >3.5 and not only the area that it's above the line.
This is how that chunk of code is working in my chart.1
If you know how this can be solved I would really appreciate if you help me :)
Thanks!!
It does not seem AnyChart supports this kind of filling out of the box. You can however get creative with gradients:
var cmin = chart.getStat("yScalesMin");
var cmax = splineSeries.getStat('seriesMax');
var cutoff = 3.5;
splineSeries.fill({
angle: 90,
keys: [{
color: '#cdf0a7',
opacity: 0.6,
offset: (cutoff-cmin) / (cmax-cmin)
}, {
color: '#d3f335',
opacity: 0.4,
offset: (cutoff-cmin) / (cmax-cmin)
}],
thickness: 3
});
Here's a working example:
chart = anychart.area();
var series = chart.area(generateRandomData());
var cmin = chart.getStat("yScalesMin");
var cmax = series.getStat('seriesMax');
var cutoff = 3.5;
series.fill({
angle: 90,
keys: [{
color: '#cdf0a7',
opacity: 0.6,
offset: (cutoff-cmin) / (cmax-cmin)
}, {
color: '#d3f335',
opacity: 0.4,
offset: (cutoff-cmin) / (cmax-cmin)
}],
thickness: 3
});
chart.container("chart");
chart.draw();
function generateRandomData() {
var data = [];
for (let i=0; i<16; i++) {
data.push({x:i, value:Math.random() * 7.5});
}
return data;
}
#chart {
height: 400px;
}
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.11.0/js/anychart-cartesian.min.js"></script>
<div id="chart"></div>
This should also work with charts with yScalesMin > 0

Chartist graphs get re-drawn everytime I click on any button or resize window

I really like this library, but I can't seem to control this issue somehow. If any event occurs the graphs are being re-drawn every time.
I am using React.js and this is how I am creating and displaying the Chartist graphs:
const dailyComplaintsChart = {
data: {
labels: ["M", "T", "W", "T", "F", "S", "S"],
series: [whichType.seriesDaily]
},
options: {
lineSmooth: Chartist.Interpolation.cardinal({
tension: 0
}),
low: 0,
high: highestValue.highestValueDaily, // creative tim: we recommend you to set the high sa the biggest value + something for a better look
chartPadding: {
top: 0,
right: 0,
bottom: 0,
left: 0
}
},
// for animation
animation: {
draw: function (data) {
if (data.type === "line" || data.type === "area") {
data.element.animate({
d: {
begin: 600,
dur: 700,
from: data.path
.clone()
.scale(1, 0)
.translate(0, data.chartRect.height())
.stringify(),
to: data.path.clone().stringify(),
easing: Chartist.Svg.Easing.easeOutQuint
}
});
} else if (data.type === "point") {
data.element.animate({
opacity: {
begin: (data.index + 1) * delays,
dur: durations,
from: 0,
to: 1,
easing: "ease"
}
});
}
}
}
};
return(
<div className="daily-graph">
<ChartistGraph
className="ct-chart-background-daily-complaints"
data={dailyComplaintsChart.data}
type="Line"
options={dailyComplaintsChart.options}
listener={dailyComplaintsChart.animation}
/>
<div className={classes.line}>
<p>Daily Complaints</p>
</div>
</div>
)
This problem is caused by the use of animation, So you have to put all the animate function in an if statement and set a counter outside of draw function.
let animated = 0;
draw(data) {
if (animated <= label.length) {
// animate
data.element.animate(...)
animated++;
}
}

FabricJS and AngularJS – copy and paste object with custom attribute

I'm using some custom attributes while I'm creating my objects. For example in this case "name" and "icon":
$scope.addRoundRect = function () {
var coord = getRandomLeftTop();
var roundrect = (new fabric.Rect({
left: coord.left,
top: coord.top,
fill: '#' + getRandomColor(),
width: 250,
height: 250,
opacity: 1,
scaleX: 1,
scaleY: 1,
angle: 0,
rx: 10,
ry: 10,
strokeWidth: 0,
name: "Rounded Rectangle",
icon: "crop-square"
}));
canvas.add(roundrect).setActiveObject(roundrect);
};
This is my copy/paste function. As you can see I have already tried to paste the relevant attributes – bu I think that they are simply not cloned with the object:
function copy() {
canvas.getActiveObject().clone(function (cloned) {
_clipboard = cloned;
});
}
function paste() {
_clipboard.clone(function (clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
name: clonedObj.name,
icon: clonedObj.icon,
});
if (clonedObj.type === 'activeSelection') {
clonedObj.canvas = canvas;
clonedObj.forEachObject(function (obj) {
canvas.add(obj);
});
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
To make it short: is there a way to clone and paste also this attributes without having to modify the source (ie. impleneting a full fledged custom attribute in the JSO serialization)?
var canvas = new fabric.Canvas('c');
var roundrect = new fabric.Rect({
left: 50,
top: 30,
fill: 'blue',
width: 250,
height: 250,
opacity: 1,
scaleX: 1,
scaleY: 1,
angle: 0,
rx: 10,
ry: 10,
strokeWidth: 0,
name: "Rounded Rectangle",
icon: "crop-square"
});
canvas.add(roundrect).setActiveObject(roundrect);
var customProperties = 'name icon'.split(' ');
function copy() {
canvas.getActiveObject().clone(function(cloned) {
console.log(cloned);
_clipboard = cloned;
}, customProperties);
}
function paste() {
// clone again, so you can do multiple copies.
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
// active selection needs a reference to the canvas.
clonedObj.canvas = canvas;
clonedObj.forEachObject(function (obj) {
canvas.add(obj);
});
// this should solve the unselectability
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
console.log(clonedObj);
_clipboard = clonedObj;
},customProperties);
}
canvas {
border: blue dotted 2px;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.min.js"></script>
<button onclick='copy()'>copy</button>
<button onclick='paste()'>paste</button><br>
<canvas id="c" width="400" height="400"></canvas>
object.clone accepts callback function and any additional property you want to include as another parameter. You can send your name and icon as properties to include.
And in paste you no need to clone that object if you are doing so, make sure there also send you are including your additional properties.

Can not export renderer text using highcharts/highstock when click range selector

I have a question related the chart export.
Please see Jsfiddle here
I added a text label using chart.renderer.text on the Yaxis for the latest value of series.
If I directly click button "Export Image". There is no problem, the label can be displayed. I'm using the following way to export image. draw_labels() is a function to draw yaxis label.
$("#b").click(function () {
chart.exportChart(null, {
chart: {
backgroundColor: '#FFFFFF',
width: 972,
height: 480,
events: {
load: function () {
draw_labels(this);
}
}
}
});
});
The problem is after I clicked range selector or change Xaxis range. When I try to export the
chart to image, there is no labels are drawn. The following is the complete code.
The following is the complete code:
$(function () {
var chart;
$.getJSON('http://www.highcharts.com/samples/data/jsonp.php?filename=aapl-c.json&callback=?', function (data) {
chart = new Highcharts.StockChart({
chart: {
renderTo: 'container',
events: {
load: function () {
draw_labels(this);
$("#b").click(function () {
chart.exportChart(null, {
chart: {
backgroundColor: '#FFFFFF',
width: 972,
height: 480,
events: {
load: function () {
draw_labels(this);
}
}
}
});
});
}
}
},
series: [{
name: 'AAPL',
id: 'test',
data: data,
tooltip: {
valueDecimals: 2
}
}],
navigator: {
enabled: false
},
yAxis: {
tickWidth: 0,
id: 'value_axis',
type: 'linear',
gridLineColor: '#EEE',
lineColor: '#D0CDC9',
lineWidth: 0,
minorTickInterval: null,
opposite: true,
offset: 0
},
xAxis: {
events: {
afterSetExtremes: function (e) {
console.log('test');
$('[id="test_text"]').remove();
draw_labels(chart);
}
}
}
});
});
function draw_labels(chart) {
$(chart.series).each(function (i, serie) {
var s_id = serie.options.id;
var temp_id = s_id;
var point = serie.points[serie.points.length - 1];
if (point) {
var pre, post;
if (point.y) {
var last_value_dis = (point.y).toFixed(1);
yaxis_name = 'value_axis';
//Get Yaxis position
var y_axis = chart.get(yaxis_name);
offsite_yaxis = 0;
element_text = chart.renderer.text(
//the text to render
'<span style="font-size:10px;font-weight:bold;color:' + serie.color + ';">' + last_value_dis + '</span>',
//the 'x' position
y_axis.width + y_axis.offset,
//the 'y' position
chart.plotTop + point.plotY + 3).attr({
id: temp_id + '_text',
zIndex: 999
}).add();
}
}
});
}
});
Here, I have fixed it for you. Here is a saved image:
Following changes have been done:
Added a redraw event to your exportchart
redraw: function () {
$("#test_text").remove() ;
draw_labels(this);
}
Changed this line in afterSetExtremes
$('[id="test_text"]').remove();
to
$("#test_text").remove() ;
Earlier one was not working as expected, so I had to change it.
Problem with disappearing text is related with id, when I removed it, label appears. But then I came across second issue, wrong y position. So i declare global variable, then when you call your function, set position of label, and use in chart exporting this variable. As a result label is exported correct.
http://jsfiddle.net/UGbpJ/11/

Resources