epezent / implot

Immediate Mode Plotting
MIT License
4.77k stars 528 forks source link

Candlestick chart #81

Closed ddovod closed 4 years ago

ddovod commented 4 years ago

Hi. Thank you very much for such an amazing library! Would you mind to implement a candlestick chart? It would be great to have a function with signature like

void PlotCandlestickChart(const char* label_id, const float* xs, const float* opens, const float* closes, const float* lows, const float* highs, int count, int offset, int stride)

I have a very basic implementation which looks like this candles The implementation is very straightforward and based on the error bars code, but I can share it here if you want.

Let me explain some problems I've faced here:

  1. As you can see, I use 2 colors for candles. The main problem is you cannot show a 2-colored marker in the legend box. I see 2 solutions here: either to support a multicolor markers in the legend, or use one color, but mark rising and falling candles with solid and hollow filling. I'm not sure what option is more suitable for implot.
  2. It would be great to be able to show a time on the X axis in some form.
  3. I'm not sure if this is a topic for implot, but it would be great to have some tools to show the candle parameters (OHLC values) when mouse is hovered or clicked the candle.

Please let me know if I can help somehow. Thanks!

ChiefyChief23 commented 4 years ago

Firstly, that looks pretty good!

  1. Just more of a thought but I wonder if it would be possible to split the legend colour cube into two right angled triangles and render the two colours that are used within the plotting? This kind of limits it to two colours if kept with the square but just an initial suggestion?

  2. This is actually currently in the works by @Prinkesh and is under review. You can keep track of when this goes in from MR #55 or issue #34. I've kind of jumped the gun a bit to implement it in my own code and as is it works great.

  3. As far as I'm aware, and correct me if I'm wrong @epezent, but the general outlook is because the library is relatively young any features, i.e. new forms of plots, together with actual use cases are welcome as they are likely to be used. Plus this base design could be used for other applications such as percentile box plots such as below. (I have no idea how candle sticks represent data as I have never delved into financial stuff, so if its what is shown below then my bad!)

image

epezent commented 4 years ago

Hi @ddovod. Thanks for the suggestion! This is actually the first time I've heard of a candlestick plot. Your implementation looks quite good!

I'd rather avoid explicitly adding PlotCandlestickChart as it seems only related to trading/financial applications. My initial thought was that we could instead add PlotBoxPlot, but after a little investigation, the two would need completely different function signatures since you need explicit control over your box/whisker high and low values.

Now, I'm think we might could add a generic plotter such as:

PlotRects(const char* label_id, const float* xs, const float* ys, const float* widths, const float* heights, ... )

This might seem a little strange, but it would enable you to create your own PlotCandlestickChart, e.g.:

void PlotCandlestickChart(const char* label_id, const float* xs, const float* opens, const float* closes, const float* lows, const float* highs, int count, int offset, int stride) {
    PlotErrorBars(label_id, ...); // render your highs/lows
    PlotRects(label_id, ...);     // render your open/closes
    // these will appear as one series in the legend because they have the same label_id
}

This would also open up the possibility of supporting box plots and could even replace the underlying implementation for PlotBars too. Less code to maintain is good.

The tricky party is doing the two colors like you have. You could pre-sort the data and do two passes, such that you'd have a green rising and red falling entry in the legend. I know this is not ideal, but I don't see an easier path forward right now.

As @ChiefyChief23 said, time labels are being worked on. I've been dragging my feet merging @Prinkesh's PR, but hopefully I will get to it soon. Regarding hover, it's been requested for other plot types and ultimately I would like to provide a generic solution. It's not something I've had time to think about yet. It should be possible to do this outside of ImPlot's source yourself, using some of the built-in helper functions, though this would admittedly be a little cumbersome.

Let me know your thoughts.

leeoniya commented 4 years ago

heh, i have a similar todo issue open in uPlot [0] to extract generic bar/box distribution, sizing & drawing logic so it can be re-used for ohlc/candlesticks [1], box/whisker plots [2], bars [3] and x-aligned heatmaps [4]. will be interesting to see what you guys end up with :)

my current thoughts are:

/highjack

[0] https://github.com/leeoniya/uPlot/issues/184 [1] https://leeoniya.github.io/uPlot/demos/candlestick-ohlc.html [2] https://leeoniya.github.io/uPlot/demos/box-whisker.html [3] https://leeoniya.github.io/uPlot/demos/multi-bars.html [4] https://leeoniya.github.io/uPlot/demos/latency-heatmap.html

epezent commented 4 years ago

Thanks, Leon! I'll keep an eye on your progress as well.

ddovod commented 4 years ago

Hi @ChiefyChief23

Just more of a thought but I wonder if it would be possible to split the legend colour cube into two right angled triangles and render the two colours that are used within the plotting? This kind of limits it to two colours if kept with the square but just an initial suggestion?

Yep, that's an option I'm thinking about, but it sounds limiting

Hi @epezent

I'd rather avoid explicitly adding PlotCandlestickChart as it seems only related to trading/financial applications. My initial thought was that we could instead add PlotBoxPlot, but after a little investigation, the two would need completely different function signatures since you need explicit control over your box/whisker high and low values.

It would be great if you come up with some generic solution, but please keep in mind that the visual appearance of the whiskers on error bars and shadows on candles are different enough to not use one code to draw them (note the T-shaped parts of the error bars and I-shaped parts of the candle chart). But, again, generic and probably customizable solution would be awesome to have

ChiefyChief23 commented 4 years ago

@epezent For generic-ness in order to get the CandleSticks we could over lay PlotRects over a new function called PlotStems. Plot stems could just be a vertical line and the declaration could be something similar to

PlotStems(const char* label_id, const float* xs, const float* y_mins, const float* y_maxs, ... );

So this would effectively just draw a vertical line along a given x position between two y values. Which could also have a Horizontal version PlotStemsH so as well as being able to create CandleStick plots we could do plots similar to that below, thoughts?

image image

@ddovod It is kind of limiting. After sleeping on it I kind of had an idea, have you ever seen the progress spinners within imgui? Well using that idea instead of showing different characters within a sequence what if it showed a sequence of colours? Then there is no limiting factor? So the legend as time goes by would switch to the colours used to render the given line??

ddovod commented 4 years ago

I believe the legend indicator should be static. One more option is to use circle instead of rectangle and fill it like a pie chart with equal sized sectors for each color (sorry for layout) one two three

epezent commented 4 years ago

@ddovod , I had the same idea of using pie charts. The rendering code already exists for PlotPieChart, so it would be quite simple to implement. Even simpler would be to continue using a square, but subdivided it either horizontally or vertically for n colors. The only real complication is that each legend entry will have to have dynamic storage for colors instead of just one color. Not that big of a deal, but it would require a few changes under the hood.

Regarding the stems, PlotErrorBars can already plot vertical lines without the horizontal whisker by pushing 0 for ImPlotStyleVar_ErrorBarSize (see slider in Error Bars demo). We could also introduce PlotSegments, similar to PlotRects to handle this pattern of discontinuous line segments.

Actually, now that I look back at the Error Bars demo, it seems that you could in fact accomplish Candlesticks with the current API using only PlotErrorBars:

void PlotCandlestickChart(const char* label_id, const float* xs, const float* opens, const float* closes, const float* lows, const float* highs, int count, int offset, int stride) {
    PushStyleVar(ImPlotStyleVar_ErrorBarSize,0);    // no whiskers
    PushStyleVar(ImPlotStyleVar_ErrorBarWeight,1);  // thin error bar for high/low
    PlotErrorBars(label_id, ...);                   // render your highs/lows
    PushStyleVar(ImPlotStyleVar_ErrorBarWeight,10); // thick error bar for open/close
    PlotErrorBars(label_id, ...);                   // render your open/closes
    PopStyleVar(3);
}
epezent commented 4 years ago

@ddovod, could you provide a test data set that I could play around with. Preferably formatted as float arrays that can be passed to the prototype function signature above.

leeoniya commented 4 years ago

@epezent you can pull some OHLC data from my demo:

https://github.com/leeoniya/uPlot/blob/a80c61962ca4f462a28880122bd94bf58831f7cc/demos/candlestick-ohlc.html#L240-L248

i had to fake/random the volume bars since i could not find any reliable free source for daily gold volume.

ddovod commented 4 years ago

@epezent Sounds good, both subdivided rectangles and drawing candlesticks using error bars. I would like to have a separate function for candlesticks just for convenience to not force everybody make one in their code, this type of chart is widely used.

epezent commented 4 years ago

@ddovod , I'll definitely consider it. This at least has me thinking about how we can extract common rendering patterns and expose those for users to implement their own custom plotters.

epezent commented 4 years ago

@ddovod , I haven't decided whether or not candlesticks belong in the public API yet (I'm leaning toward no), but I did want you to know that I've added an example of creating a custom PlotCandlestick to the demo. This afforded by the recent refactors that expose some of ImPlot's guts in implot_internal.h. You can take a peek inside of this file to see what else is available, and further extend the demo example to suit your needs. Moving forward I hope to improve the API in implot_internal.h, and possibly move some of it to the public API in implot.h.

The two-tone legend icon still isn't resolved, and I'm still dragging on incorporating time formatted axes, but hopefully this is a step in the right direction. Let me know your thoughts.

image

epezent commented 4 years ago

@ddovod , for fun I added an example of custom tooltips to the demo:

candletool

ddovod commented 4 years ago

@epezent Wow, looks great! Thanks a lot, I'll definitely take a look

coolxv commented 4 years ago

This site is worth checking out https://echarts.apache.org/examples/en/index.html#chart-type-candlestick

image

epezent commented 4 years ago

You stock guys may be interested to know that ImPlot now supports time formatted x-axes:

jahnf commented 2 years ago

Sorry for posting here in this closed issue, but it seems to be the best fit instead of opening a new issue.

(and first of all: thank you for the amazing library, just discovered it a week ago)

For testing purposes I loaded a local CSV file with EURUSD M1 data. With M1 data (one OHLC data point / minute) the data gaps (over the weekend) are especially visible:

Is there any way to "just" skip the data gaps?

data_gaps_implot

sweihub commented 2 years ago

Hey, I am playing with the candlestick, how can I remove the big grid lines?

The ImPlotAxisFlags_NoGridLines flag will remove all grid lines, I just don't want the big grid lines, but keep the background small grid lines. Can anyone point it out? Thanks!

image

ArturSztuc commented 11 months ago

Sorry for posting here in this closed issue, but it seems to be the best fit instead of opening a new issue.

(and first of all: thank you for the amazing library, just discovered it a week ago)

For testing purposes I loaded a local CSV file with EURUSD M1 data. With M1 data (one OHLC data point / minute) the data gaps (over the weekend) are especially visible:

Is there any way to "just" skip the data gaps?

@jahnf I know it's been over a year since this question, but did you find a solution? I have the exact same issue.

spliffli commented 5 months ago

@ddovod @epezent Sorry if this has already been answered, but how can I use this implementation of a candlestick chart with imgui? To be more specific, I plan on using a C# .NET wrapper around imgui called ImGui.NET.

I find it unclear from reading whether or not candlestick charts are natively supported by implot, or if it's necessary to modify the implot code to achieve that.

It would be nice to have both candlestick and line charts for representing price data for trading, whether it's stocks, crypto, forex or futures