I am running into an issue with dash/plotly. I have an html page with an Input box, a button and an area for a graph. I used a callback to update the graph (based on the input box entry) once the button is pressed. However when I load my page for the first time, it seems plotly is forcing the evaluation of the callback function although the input box is empty. Hence I get a "callback error updating...".
Here is my code:
layout = html.Div([
dbc.Container([
dbc.Row([
dbc.Col(html.H1("1Y Stock Plot", className="text-center")
, className="mb-5 mt-5")
]),
html.Div([
dcc.Input(id='stockplot_symbol', placeholder='enter a stock ticker'),
html.Button("Plot", id='submit_stockplot', n_clicks=0),
]),
html.Div(id='output-graph')
])
])
and the callback:
@app.callback(Output('output-graph', 'children'), #'stock_plot_graph', 'figure'),
[Input('submit_stockplot', 'n_clicks')],
[State('stockplot_symbol', 'value')]
)
def update_stockplot(n_clicks, symbol):
if n_clicks is not None:
ticker = str(symbol).upper()
df = yf.Ticker(ticker).history(period='1y', interval='1d')
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1, row_heights=[0.7, 0.2])
op = df['Open'].astype(float)
hi = df['High'].astype(float)
lo = df['Low'].astype(float)
cl = df['Close'].astype(float)
vo = df['Volume'].astype(float)
upper, middle, lower = talib.BBANDS(cl, timeperiod=10, nbdevup=2, nbdevdn=2)
fig.add_trace(go.Candlestick(x=df.index,
open=op,
high=hi,
low=lo,
close=cl, name=symbol), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=upper, name='Upper Band'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=middle, name='Middle Band'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.index, y=lower, name='Lower Band'), row=1, col=1)
fig.add_trace(go.Bar(x=df.index, y=vo, name='volume'), row=2, col=1)
fig.update_layout(xaxis_rangeslider_visible=False,
height=800,
xaxis=dict(type="date"),
hovermode='x unified'
)
return dcc.Graph(id='stock_plot_graph', figure=fig)
else:
return dash.exceptions.PreventUpdate
I do set n_clicks to 0 in the layout, but somehow dash/plotly is forcing the evaluation of what is inside the if/else statement and since there is nothing in the input box, the dataframe is empty and therefore this creates an issue when I call talib.BANDS (which is the function dash/plotly is complaining with the callback error updating... df is empty)
My idea was to have the graph area being updated or drawn only when the button is clicked. This works fine after the first call but not when loading up the page for the first time. I tried to use an if statement to test whether the input box is empty or not but get the same callabck error. I could set an initial/default value to avoid that but that's not optimal for what I would like to do. I don't want to have the function inside the callback evaluated on a dummy input because later the processing time for that function will not be as fast as in the above case.
How do you avoid this?