Two plots on one figure, one updates inplace and interactively with zoom

I am trying to build a plot that displays the price of a stock as a line plot, overlaid against the corresponding sum of volumes at that price point as a horizontal bar chart. Upon altering the view via zoom/pan, the bar chart should be redrawn according to the portion of the data in the line plot now visible on the screen. I have been trying to implement the dcc.Graph.extendData API with no success. Essentially, I want to completely overwrite the previous hbar trace data without affecting the view of the now-zoomed line plot. Here is what I have so far:

# abstractly defining the stuff that happens during initialization
x0_data, y0_data = ...
static_line_plot = go.Scatter(x=x0_data, y=y0_data, ...)
x1_data, y1_data = ...
initial_hbar = go.Bar(x=x1_data, y=y1_data, ...)

layout = go.Layout(...)
fig = make_subplots()
fig.update_layout(layout)
fig.add_trace(static_line_plot)
fig.add_trace(initial_hbar)
graph = dcc.Graph(figure=fig, id="stacked charts")

# the part I'm stuck on
@app.callback(
    Output("stacked charts", "extendData"),
    Input("stacked charts", "relayoutData"),
)
def redraw_hbar_on_zoom(relayout_data):

  # retrieve current visible timeseries range from json
  data = json.loads(json.dumps(relayout_data, indent=2))

  # if graph has just been initialized
  if data is None:
      return [dict(x=[[y1_data.numpy()]], y=[[x1_data.numpy()]]), [1]]
  
  # if user uses autoscale to reset view, hbar is set to the default range
  data_keys = data.keys()
  if "xaxis.range[0]" not in data_keys:
      return [dict(x=[[y1_data.numpy()]], y=[[x1_data.numpy()]]), [1]]
  
  # get the current x axis datetime range
  xmin = data["xaxis.range[0]"]
  xmax = data["xaxis.range[1]"]
  
  # do some math to extract volume data from this corresponding 
  # timespan and rebuild the hbar
  this_x1_data, this_y1_data = ...

  # failed attempt to overwrite the old graph's figure's data before
  # invoking extendData
  graph.figure.data[1].x = []
  graph.figure.data[1].y = []

  # now "extend" this now empty plot with the data we just calculated
  return [dict(x=this_y1_data.numpy(), y=this_x1_data.numpy()), [1]]

However, nothing seems to happen during the callback. The callback does run when it’s supposed to, and I have confirmed that the new data is calculated correctly.
Thanks for the help :slight_smile:

Welcome @weeeeeeeee!

Not directly what you are after, but maybe a good start:

I ended up splitting the charts into two figures for simplicity, but I appreciate it, thank you! See the current product here: TickerAnalysisDemos/volume_weighted_bars.webm at main · liamllh/TickerAnalysisDemos · GitHub

1 Like