Live Candlestick Graph issue with ExtendData

I am building a live candlestick graph.

Having it redraw and flicker on each callback was annoying, so I found out about using ‘ExtendData’ to bypass this.

I am stuck at a point where the latest live candlestick is stacked on top of each of its last updates:

Right now I’m only using this:
@app.callback(Output('graph', 'extendData'), [Input('interval', 'n_intervals')])

I assume I need to somehow include the current state and remove the last candle before ‘extending’ the dataset?

Hi @sfvasfdvsdfvdsv,

Welcome to the community! :slight_smile:

Are you giving new “x” coordinates for the candles at each update or just reusing them? My instinct would be to say that this is the issue here, but it helps if you could provide the code for your callback to be sure about it.

@app.callback(Output('graph', 'extendData'), [Input('interval', 'n_intervals')])
def update_data(n_intervals):

        candles = get_candles()
        df = pd.DataFrame(candles)
        
        new_data = df[['time', 'high', 'low', 'open', 'close']].iloc[-1]
        new_data = new_data.to_dict()
        new_data['x'] = new_data['time']
        
        for k in new_data.keys():
                new_data[k] = [[new_data[k]]]
        
        return new_data, [0]

I am not giving new ‘x’ coordinates as the ‘live’ candle should stay in the same place?
(but when a new candle arrives it does have an updated x so a new candle gets created)

Each new ‘live candle’ state gets appended to the fig data and I seem to need to get rid of each but the last under that same ‘x’

Any ideas how to edit the existing figure from this (or another) callback?

I did import the figure data as a

[State("graph", "figure")]

but I am not sure how to then export it so removing the needed candle entries reflect on the charted graph

Ok, I see what you mean now. You are not interested in extend the data with new candles (for new timestamps), but to update past N candles with newer data.

There is still a way to do it though… The extendData array expects an optional maxPoints value on the third element. So what you can do is to use the figure state (that you are adding to the callback) to get the candles already defined in the chart, updates the values that you want and send all the points back to extendData. Then make sure to make maxPoints = len(data), so basically the update will remove all old points and add the old points with the new. I believe this should be superior in speed than modifying the trace directly (what you can also do, and return the figure dict).

I thought I have written a similar answer with an example before, but I just stated the same as above (you can find the thread I refer to here).

Let me know if this helps.

It worked!

@app.callback(Output('graph', 'extendData'), [Input('interval', 'n_intervals')], 
[State("graph", "figure")])
def update_data(n_intervals, existing):
        
        # work directly with existing['data'][0] and determine 
        # if you need to append to it or modify its [-1] entry here

        return [
                {
                    "x": [existing["data"][0]["x"]],
                    "open": [existing["data"][0]["open"]],
                    "high": [existing["data"][0]["high"] ],
                    "low": [existing["data"][0]["low"]],
                    "close": [existing["data"][0]["close"]]
                },
                [0],
                len(existing["data"][0]["open"]) 
            ]

1 Like

Hey,

This stopped working recently. Any idea why I get:

dash._grouping.SchemaLengthValidationError: Schema: [<Output `graph.extendData`>]
Path: ()
Expected length: 1
Received value of length 3:

Above disappears when I remove the traceIndices and maxPoints but having them is kinda the point?

That’s odd, @sfvasfdvsdfvdsv… Can you try to wrap the list in the return statement with a second list? Somehow it looks like Dash is unpacking the list and complaining that you don’t have 3 outputs in the decorators.