tradingview / lightweight-charts

Performant financial charts built with HTML5 canvas
https://www.tradingview.com/lightweight-charts/
Apache License 2.0
8.77k stars 1.54k forks source link

Multiple panes support #50

Open nothingalike opened 5 years ago

nothingalike commented 5 years ago

Looking through the documentation i dont see anything along these lines. I want to have my main chart but then add some additional data in a separate chart window. I believe some apps call it an Indicator Window. I was curious if its possible with this library.

timocov commented 5 years ago

Do you want to add additional series to the new pane or to existing one? Something like this https://jsfiddle.net/TradingView/srwoh679/?

nothingalike commented 5 years ago

I'm looking more for a new pane. which i assume i can just create another chart option but im afraid that would separate the zooming and paning causing both panes to be out of sync

nothingalike commented 5 years ago

that being said, if i do need to create a new chart, is it possible for me to sync the zooming/paning via events. ex: get event from one chart and apply to the second

timocov commented 5 years ago

If you'll create the new pane, the main one will be synced with that pane by visible range (also different series will share time scale and every series will extend points on time scale).

If you'll create the new chart, then you need to synchronize them yourself, but the charts is independent.

I would say you need to decide what do you want to get 🙂 Also, you can provide screenshots/links to any other platform where you've seen that.

is it possible for me to sync the zooming/paning via events

You can use https://github.com/tradingview/lightweight-charts/blob/8cf07af2223e8010ede7ce844e0b6d81e5da969f/src/api/ichart-api.ts#L109-L113 to get updates of visible time range changes and https://github.com/tradingview/lightweight-charts/blob/8cf07af2223e8010ede7ce844e0b6d81e5da969f/src/api/itime-scale-api.ts#L38-L42 to set visible time range to other chart. Unfortunately we don't have documentation for that methods and we'll add them soon.

nothingalike commented 5 years ago

Ideally i would not like to sync them myself, but it seems that i can take the 'pane' approach and use the itime-scale. that is probably what im looking for.

But for the sake of clarity, here is an image that can help visualize what im asking for. The top being the main pane and the bottom part being the 'indicator' window or pane

image

timocov commented 5 years ago

Ah, ok, you want to use several panes on the same chart. Currently we haven't API to create additional panes but we've planned to add it in the future. For now you can do something like that https://jsfiddle.net/q3u2khzt/.

nothingalike commented 5 years ago

I see that the chart-model has a createPane method on it, could I just use this? or would i not be able to add a data series to the new pane if i created it this way?

timocov commented 5 years ago

could I just use this?

It's not public API and we cannot guarantee you anything in this case.

nothingalike commented 5 years ago

ah i see, so when adding a series of data via the chart-api it adds this new series to pane[0]. if this particular feature isn't currently being worked on, could i throw a pull request your way in the next few days?

timocov commented 5 years ago

if this particular feature isn't currently being worked on, could i throw a pull request your way in the next few days?

Which one? Supporting multiple panes?

nothingalike commented 5 years ago

yes, supporting mutiple panes

timocov commented 5 years ago

Unfortunately we've started discuss about the design of that feature some time ago and there is some edge cases which requires some detailed investigation. It's one of our "after release features" and we'll add it soon.

timocov commented 5 years ago

I believe that all issues which can be taken by community will be marked with "help wanted" label. But this is a big feature and we need to be very accurate with it, its API and so on.

Thank you for your understanding.

nothingalike commented 5 years ago

oh ok, cool. sounds good. ill keep my eye out for that feature. really appreciate your time

triorr commented 5 years ago

Hello,

In the meantime, waiting for a better feature implementation , I've got some nice result adding some lines to the source code following timocov hint about the subscribeVisibleTimeRangeChange function.

First i added this function

public getBarSpacing(): number {
    return this._timeScale().barSpacing();
}

here https://github.com/tradingview/lightweight-charts/blob/1d26e2d2ab1a936bfa943a48de5f13b673674e7d/src/api/time-scale-api.ts#L31 and this line

getBarSpacing(): number;

here https://github.com/tradingview/lightweight-charts/blob/8cf07af2223e8010ede7ce844e0b6d81e5da969f/src/api/itime-scale-api.ts#L19 and now we can do something like this in our javascript

chart.timeScale().subscribeVisibleTimeRangeChange( syncHandler)
function syncHandler(e) {
    var barSpacing1 = chart.timeScale().getBarSpacing();
    var scrollPosition1 = chart.timeScale().scrollPosition();
    chart2.timeScale().applyOptions({rightOffset: scrollPosition1,barSpacing: barSpacing1})
}
yuyic commented 4 years ago

@triorr @timocov Is there any way to get/set the width of price bar? because the width is dynamic, I want to keep the width of two panels consistent.

timocov commented 4 years ago

@yuyic use barSpacing option https://github.com/tradingview/lightweight-charts/blob/master/docs/customization.md#time-axis.

michelpmcdonald commented 4 years ago

..... Also, you can provide screenshots/links to any other platform where you've seen that.

Try this site for an example of a chart that supports multiple synced panes: www.tradingview.com

0x48415a484952 commented 4 years ago

Hello,

In the meantime, waiting for a better feature implementation , I've got some nice result adding some lines to the source code following timocov hint about the subscribeVisibleTimeRangeChange function.

First i added this function

public getbarSpacing(): number {
  return this._timeScale().barSpacing();
}

here

https://github.com/tradingview/lightweight-charts/blob/1d26e2d2ab1a936bfa943a48de5f13b673674e7d/src/api/time-scale-api.ts#L31

and this line

getbarSpacing(): number;

here

https://github.com/tradingview/lightweight-charts/blob/8cf07af2223e8010ede7ce844e0b6d81e5da969f/src/api/itime-scale-api.ts#L19

and now we can do something like this in our javascript

chart.subscribeVisibleTimeRangeChange( syncHandler)
function syncHandler(e) {
    var barSpacing1 = chart.timeScale().getbarSpacing();
    var scrollPosition1 = chart.timeScale().scrollPosition();
    chart2.timeScale().applyOptions({rightOffset: scrollPosition1,barSpacing: barSpacing1})
}

hello, what is the equivalent code for standalone version ?

0x48415a484952 commented 4 years ago

this feature is really cool if implemented like we could add an RSI which the range is between 0 and 100 and above that we could have the price candlesticks i tried to create such thing but could not achieve it here are some screen shots that in the second one after plotting RSI on the same chart the price candles get very unclear tried to tweak the scaleMargin but could not achieve a good result also i tried to create this on another chart below this chart which is ok but when the user scrolls the two different charts get out of sync so i think we should be able to plot them on one chart, i tried to look into the source code to add this feature as Triorr mentiond in here https://github.com/tradingview/lightweight-charts/issues/50#issuecomment-504787139 but couldn't find the equivalent of this code on the standalone version. KBTUSD Screenshot_2019-09-04 Septillion

karunkrishna commented 4 years ago

Unfortunately we've started discuss about the design of that feature some time ago and there is some edge cases which requires some detailed investigation. It's one of our "after release features" and we'll add it soon.

Is there any update on this feature There are are only two ways to render indicators studies;

  1. Overlay that use the same values as price (currently achievable)
  2. Paneled that use secondary values (Rendered in secondary chart, while time series is linked between charts)

We are missing support for 2.

Is there any documentation/jfiddle examples on using subscribeVisibleTimeRangeChange, getbarSpacing() if the feature will not be available in the near future?

AurelReb commented 4 years ago

Hi, any update? I'm looking exactly for this feature. Multiple panes would be very useful! I also had an idea to handle this: there is an API to get the crosshair coordinates. Is it possible to place the crosshair on custom coordinates when the mouse isn't on the chart?

This way, we could get the time range, and the crosshair coordinates in the main chart, and copy it on the second chart.

timocov commented 4 years ago

Multiple panes would be very useful!

I'm sure no one will argue with that though 🙂

Is it possible to place the crosshair on custom coordinates when the mouse isn't on the chart?

No, we don't have such API for that.

Instead, I'd suggest everyone who needs this, trying to make a proposal for this feature. We'll happy to implement it (almost everything what needed for this feature already is implemented inside the lightweight-charts), but it's hard to provide good, readable and easy understandable API. That's the issue right now. If you have any ideas for that - please write them out here.

ghost commented 4 years ago

@triorr i added 2 charts like you said and they are synced timescale with events. In the top is candlestick and bottom is histogram and my question is how can i make candlesticks and histogram bars stay in the same position? as you can see there is big space between candlesticks but histogram is still same.

@timocov It would be great if you have an idea about that.

Screen Shot 2020-05-18 at 4 12 07 PM

triorr commented 4 years ago

@lejyoner-ds Sorry for the late response Let me try and understand what you want. I guess that you have synced both charts to each other and you want to sync them pixel by pixel, is that it? For that, I am sorry I can't help you, as I haven't dug deeper on the code. If you did not sync both charts here is jsfiddle.net demo. https://jsfiddle.net/trior/q3kt6psb/3/ ps: make sure you have the same number of bars and the same dates on both charts.

octapudus commented 4 years ago

Hey guys, sorry if i ask you too, but i would like to use your libraries to reproduce the same chart as in the binance.com, OR tradingview, like in the photo image

What I can't find in the documentation is how to create the indicators in the same chart ..

can you build something like > chart.addIndicatorSeres(); ????

so in this way, it could be possible easily to do that.

if you can reply to me I will be grateful,

Thank you

akamal88 commented 4 years ago

@timocov is this feature available in latest version ? I mean, having multiple panes like this image

image

timocov commented 4 years ago

@akamal88 just wonder why you're asking, because this issue even isn't closed... Nope, the feature isn't implemented yet.

akamal88 commented 4 years ago

@timocov Thanks, but i asked because i see this property in series class

/** Target price scale to bind new series to */
priceScaleId?: string;
triorr commented 4 years ago

@lejyoner-ds here is an example that use the subscribeCrosshairMove function to get a pixel to pixel sync between the two charts.

https://codepen.io/trior/pen/bGEoqoP

and another one with subscribeVisibleLogicalRangeChange , this one is better I think https://codepen.io/trior/pen/JjGOYNP

@AhmadzadehHazhir My standalone library with the getBarSpacing function is hosted here https://codepen.io/trior/pen/XWXeMqR.js feel free to copy and use it

breathmix commented 4 years ago

Hi, The following worked for me, without changing the source:

    var chart_1 = LightweightCharts.createChart(document.body, {
        width: 800,
        height: 300,
    });

    var chart_2 = LightweightCharts.createChart(document.body, {
        width: 800,
        height: 300,
    });

    var candleSeries = chart_1.addCandlestickSeries({});
    candleSeries.setData([
        { time: '2018-10-19', open: 180.34, high: 180.99, low: 178.57, close: 179.85 },
        { time: '2018-10-22', open: 180.82, high: 181.40, low: 177.56, close: 178.75 },
        { time: '2018-10-23', open: 175.77, high: 179.49, low: 175.44, close: 178.53 },
        { time: '2018-10-24', open: 180.77, high: 179.49, low: 175.44, close: 178.53 },
        { time: '2018-10-25', open: 190.75, high: 195.49, low: 188.44, close: 190.53 },
        { time: '2018-10-26', open: 200.77, high: 211.49, low: 200.44, close: 205.53 },

    ]);

    var lineSeries = chart_2.addLineSeries({});
    lineSeries.setData([
        { time: '2018-10-19', value: 180.34 },
        { time: '2018-10-22', value: 180.82 },
        { time: '2018-10-23', value: 175.77 },
        { time: '2018-10-24', value: 165.77 },
        { time: '2018-10-25', value: 225.77 },
        { time: '2018-10-26', value: 195.77 },
    ]);

    chart_1.timeScale().subscribeVisibleLogicalRangeChange(
        function syncHandler(e) {

            var range = chart_1.timeScale().getVisibleLogicalRange();
            chart_2.timeScale().setVisibleLogicalRange(range);
            scrollPosition = chart_1.timeScale().scrollPosition();
            chart_2.timeScale().applyOptions({ rightOffset: scrollPosition })
        });

    chart_2.timeScale().subscribeVisibleLogicalRangeChange(
        function syncHandler(e) {

            range = chart_2.timeScale().getVisibleLogicalRange();
            chart_1.timeScale().setVisibleLogicalRange(range);
            scrollPosition = chart_2.timeScale().scrollPosition();
            chart_1.timeScale().applyOptions({ rightOffset: scrollPosition })
        });
jordanmb commented 3 years ago

I've used the suggested multiple chart workaround above in the absence of this feature, but I have an issue with the price scales having different widths. This causes the inner chart area to be different and thus unaligned. I cannot find any way to syncronize these, including by padding the price formatting strings. I see that there was a chart.priceScale().width() added, but there is no way to set it. Anyone have any recommendations?

RubberArchind commented 3 years ago

@jordanmb I have the same problem, are you making progress with it?

jordanmb commented 3 years ago

@RubberArchind unfortunately no, i gave up and switched to d3.

Usey95 commented 3 years ago

@jordanmb I have the same problem, are you making progress with it?

There's no way to do that unless you change the source code(pretty easy though).

tlopo commented 3 years ago

@timocov would you have an example of two charts with synchronised time?

mishurov commented 3 years ago

hello, what is the equivalent code for standalone version ?

@AhmadzadehHazhir

chart1.timeScale().subscribeVisibleLogicalRangeChange(range => {
  chart2.timeScale().setVisibleLogicalRange(range)
})
chart2.timeScale().subscribeVisibleLogicalRangeChange(range => {
  chart1.timeScale().setVisibleLogicalRange(range)
})

@jordanmb I create the second chart without the price scale and then resize it: something like parentChart.width - parentChart.priceScale.width. Nonetheless, it doesn't solve all the problems, for example the crosshair should also be visible on both charts to match visually a candlestick with indicator curves and so on. I ended up drawing custom crosshairs and an axis with d3 on top of the TV charts from TV crosshair callbacks.

TV charts are blazingly fast because of the canvas, I haven't tried d3 with canvas, with svg it is unfortunately quite slow in FF when you add lots of candles and indicators and needs to implement plenty of basic stuff.

Kayrakan commented 3 years ago

Hello, is there any update for multiple panes ?

dss010101 commented 3 years ago

Are multiple panes supported as yet?

tomlister commented 3 years ago

🙏

h0wXD commented 3 years ago

Just sharing this in case anyone is interested, I've achieved the requested behavior with crosshair using multiple charts. You will need to implement subscribeVisibleLogicalRangeChange / setVisibleLogicalRange as mentioned above in https://github.com/tradingview/lightweight-charts/issues/50#issuecomment-787940852

It also depends on two commits, and a local build of the lib https://github.com/h0wXD/lightweight-charts/commit/dc4fe680d7f3417f1752ed3ada3839242a02b3c0 to make charts have equal widths and https://github.com/h0wXD/lightweight-charts/commit/149b47af574046179e867487861d506ab5283953 to support moving the crosshair

The charts will have to be equal widths, and so do the price scale, or the vertical cursor will not be in sync. Set a price scale value shared by all charts

const chartOptions = {
  rightPriceScale: {
    width: 60
  }
};

Add following to sync crosshairs from the main chart, and also the inverse logic for each axis subscribe, and update the main / other axis, in my case I have one main chart and multiple y axis (each y axis is a new chart).

let isCrossHairMoving = false;
chart.subscribeCrosshairMove(param => {
  // param.hoveredMarkerId
  if (!param.point) return;
  if (!param.time) return;
  if (isCrossHairMoving) return;

  uniqueAxis.map(axis => {
    const axisWrapper = this.axis[axis].current;
    const axisChart = axisWrapper.chart;
    const axisTimeScale = axisChart.timeScale();
    const coord = axisTimeScale.timeToCoordinate(param.time);
    if (!coord) return;

    isCrossHairMoving = true;
    axisChart.moveCrosshair({
      ...param.point,
      x: coord
    });
    isCrossHairMoving = false;
  });
});

To not have a weird crosshair placement, I've updated the chartOptions for the axis to only show the vertical line

crosshair: {
  horzLine: {
    visible: false,
    labelVisible: false
  }
}

Example of cursor sync: image

maxchaos1985 commented 3 years ago

Hi h0wXD I'm relativrly new to this, I've managed to stack charts and was looking to try out your sunc cursor but I'm getting an error Uncaught ReferenceError: uniqueAxis is not defined", what is uniqueAxis then? Thanks

h0wXD commented 3 years ago

@maxchaos1985 basically I stored each chart as an axis, and you will have to call moveCrosshair on the chart instance of each axis. May I suggest you to start off with a hardcoded version first? I've created a hardcoded sample for demonstration here: https://jsfiddle.net/h0wXD/g75Lmady/

If you've got a stable version you can start to make it more dynamic, e.g. add things like settings timeScale visible properties only on the bottom axis, adding an overlay positioned absolute on each div to toggle the axis' visibility etc.

maxchaos1985 commented 3 years ago

Got it, thanks a bunch, will have a play.

Shepless commented 3 years ago

@h0wXD Thanks for all your efforts on this and the brilliant examples. Do you have any plans to PR this back into the main repo? Just conscious of having to use a fork and not the live npm package longer term.

ntf commented 2 years ago

I just started exploring this library over the weekend and spent a day to fix the multiple panes support and the pane separator.

It is based on the code commented at 22c6b03897c926cb9a0295d9b0bbc4fe2b5d83a5 by @timocov.

https://jsfiddle.net/adrianntf/6qea5ytv/2/

@timocov, is there any concern if I make a pull request and defer regression test / new unit testcases to you guys at TradingView ?

timocov commented 2 years ago

is there any concern if I make a pull request and defer regression test / new unit testcases to you guys at TradingView ?

@ntf the main concern now is about API for panes, because it might increase complexity of the API a lot and we want to make it as simple as possible. If you have any suggestion about API for panes, please share your thoughts. But please don't rush with implementing the solution to avoid misunderstanding or confusing. Let's design the feature first.

ntf commented 2 years ago

@timocov , I see, you can take a look my pull request and put your thought there #824 . I will update the API from time to time until it also satisfy with my own use case.

I have added so far in terms of API:

timocov commented 2 years ago

added pane: number to SeriesOptionsCommon

What number it should be? How to know how many panes we have at the time? What should happen if you remove the last source from the pane, should this number be updated in options as well? What should happen with series' price scale if you move a series from one pane to another one?

subscribePaneResize(handler: PaneEventHandler)

Does it allow you to subscribe on height change of any pane? Or only specific one?

ntf commented 2 years ago

What number it should be? The i-th as in this._panes[paneIndex] of the ChartModel the series should be in. How to know how many panes we have at the time? how about a new API getPaneCount() ? Similarly how do we know how many serieses has added to the chart currently?

What should happen if you remove the last source from the pane, should this number be updated in options as well? We could remove the pane altogether. Regarding the number, we can either update the option or emit an series add/change/remove event so that user would handle in the user code.

What should happen with series' price scale if you move a series from one pane to another one? Same as remove a series and re-create a series with a different pane number ?

Does it allow you to subscribe on height change of any pane? Or only specific one? Any pane for now. In fact, it may not be necessary if there is an API to expose the reference to the root DOM elements for each pane.

Demo: https://jsfiddle.net/adrianntf/6qea5ytv/