Would really appreciate some help regarding how to do a proper callback for my Dash web app. I have the following code below. Its supposed to return a graph with various lines about a stocks' financials. It takes data from an API. But I'm not sure how to define my function for the app callback. I've tried following online tutorials but have had any success. I apologise if its too messy or inefficient, I'm new to this.
'''
#!/usr/bin/env python
import pandas as pd
import requests
import json
import plotly
import chart_studio.plotly as py
import plotly.graph_objs as go
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input
ticker = input("Insert company ticker: ")
qoy = input("QUARTERLY or YEARLY: ")
if qoy == "Yearly" or qoy == "yearly" or qoy == "YEARLY":
IS = requests.get("https://financialmodelingprep.com/api/v3/financials/income-statement/" + ticker)
elif qoy == "Quarterly" or qoy == "quarterly" or qoy == "QUARTERLY":
IS = requests.get(
"https://financialmodelingprep.com/api/v3/financials/income-statement/" + ticker + "?period=quarter")
IS = IS.json()
IS = IS['financials']
IS = pd.DataFrame.from_dict(IS)
IS = IS.set_index("date")
if qoy == "Yearly" or qoy == "yearly" or qoy == "YEARLY":
BS = requests.get("https://financialmodelingprep.com/api/v3/financials/balance-sheet-statement/" + ticker)
elif qoy == "Quarterly" or qoy == "quarterly" or qoy == "QUARTERLY":
BS = requests.get(
"https://financialmodelingprep.com/api/v3/financials/balance-sheet-statement/" + ticker + "?period=quarter")
BS = BS.json()
BS = BS['financials']
BS = pd.DataFrame.from_dict(BS)
BS = BS.set_index("date")
if qoy == "Yearly" or qoy == "yearly" or qoy == "YEARLY":
CF = requests.get("https://financialmodelingprep.com/api/v3/financials/cash-flow-statement/" + ticker)
elif qoy == "Quarterly" or qoy == "quarterly" or qoy == "QUARTERLY":
CF = requests.get(
"https://financialmodelingprep.com/api/v3/financials/cash-flow-statement/" + ticker + "?period=quarter")
CF = CF.json()
CF = CF['financials']
CF = pd.DataFrame.from_dict(CF)
CF = CF.set_index("date")
df_FS = pd.concat([IS, BS, CF], axis=1, sort=True)
Date = df_FS.index
df_FS.fillna(0, inplace=True)
print(df_FS)
from plotly.subplots import make_subplots
# EARNINGS & REVENUE
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=Date, y=df_FS['Revenue'],
mode='lines+markers',
name='Revenue'), secondary_y=False, )
fig.add_trace(go.Bar(x=Date, y=df_FS['Profit Margin'],
opacity=0.2,
name='Profit Margin'), secondary_y=True, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Consolidated Income'],
mode='lines+markers',
name='Earnings'), secondary_y=False, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Operating Cash Flow'],
mode='lines+markers',
name='Operating Cash Flow'), secondary_y=False, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Free Cash Flow'],
mode='lines+markers',
name='Free Cash Flow'), secondary_y=False, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Operating Expenses'],
mode='lines+markers',
name='Operating Expenses'), secondary_y=False, )
fig.update_layout(title="EARNINGS & REVENUE", barmode='group', hovermode='x')
fig.update_yaxes(title_text="in USD", secondary_y=False)
fig.update_yaxes(title_text="Profit Margin", secondary_y=True)
fig.update_xaxes(rangeslider_visible=True)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
# Input the stock ticker
html.Div([
dcc.Input(id="stock-input", value=ticker, type="text"),
dcc.RadioItems(
id="quarterlyoryearly",
options=[
{'label': 'Quarterly', 'value': 'quarterly'},
{'label': 'Yearly', 'value': 'yearly'}
],
value=qoy
)
]),
# Banner of app
html.Div([
html.H2("Stock App")
], className="banner"),
# Graphs
html.Div([
# Earnings & Revenue Graph
html.Div([
dcc.Graph(
id="Earnings & Revenue",
figure=fig
)
], className="six columns"),
], className="row")
])
app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})
@app.callback(dash.dependencies.Output("Earnings & Revenue","figure"),
[dash.dependencies.Input("stock-input","value"),
dash.dependencies.Input("quarterlyoryearly","value")]
)
#def update_fig(ticker,qoy):
if __name__ == '__main__':
app.run_server(debug=False)
'''
You have to perform all the data processing in the callback since they require the input and button values. There are various ways to go about this depending on how you want the app to respond to the inputs. One way is this. Here, the main input is qoy
and the callback uses the state of the stock input
. So entering the stock input will not update the app until you choose qoy
.
#!/usr/bin/env python
from plotly.subplots import make_subplots
import pandas as pd
import requests
import json
import plotly
import chart_studio.plotly as py
import plotly.graph_objs as go
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Output, Input, State
from dash.exceptions import PreventUpdate
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
# Input the stock ticker
html.Div([
dcc.Input(id="stock-input",
placeholder='Please insert stock', type="text"),
dcc.RadioItems(
id="quarterlyoryearly",
options=[
{'label': 'Quarterly', 'value': 'quarterly'},
{'label': 'Yearly', 'value': 'yearly'}
]
)
]),
# Banner of app
html.Div([
html.H2("Stock App")
], className="banner"),
# Graphs
html.Div([
# Earnings & Revenue Graph
html.Div([
dcc.Graph(
id="Earnings & Revenue",
# figure=fig
)
], className="six columns"),
], className="row")
])
@app.callback(Output("quarterlyoryearly", "value"),
[Input("stock-input", "n_submit")],
[State("quarterlyoryearly", "value")])
def enter_key(n_sub, qoy):
if n_sub:
return qoy
@app.callback(dash.dependencies.Output("Earnings & Revenue", "figure"),
[dash.dependencies.Input("quarterlyoryearly", "value")],
[dash.dependencies.State("stock-input", "value")]
)
def update_fig(*args):
if not any(args):
raise PreventUpdate
else:
qoy, ticker = args
if qoy.lower() == "yearly":
IS = requests.get(
"https://financialmodelingprep.com/api/v3/financials/income-statement/" + ticker)
elif qoy.lower() == "quarterly":
IS = requests.get(
"https://financialmodelingprep.com/api/v3/financials/income-statement/" + ticker + "?period=quarter")
IS = IS.json()
IS = IS['financials']
IS = pd.DataFrame.from_dict(IS)
IS = IS.set_index("date")
if qoy == "Yearly" or qoy == "yearly" or qoy == "YEARLY":
BS = requests.get(
"https://financialmodelingprep.com/api/v3/financials/balance-sheet-statement/" + ticker)
elif qoy == "Quarterly" or qoy == "quarterly" or qoy == "QUARTERLY":
BS = requests.get(
"https://financialmodelingprep.com/api/v3/financials/balance-sheet-statement/" + ticker + "?period=quarter")
BS = BS.json()
BS = BS['financials']
BS = pd.DataFrame.from_dict(BS)
BS = BS.set_index("date")
if qoy == "Yearly" or qoy == "yearly" or qoy == "YEARLY":
CF = requests.get(
"https://financialmodelingprep.com/api/v3/financials/cash-flow-statement/" + ticker)
elif qoy == "Quarterly" or qoy == "quarterly" or qoy == "QUARTERLY":
CF = requests.get(
"https://financialmodelingprep.com/api/v3/financials/cash-flow-statement/" + ticker + "?period=quarter")
CF = CF.json()
CF = CF['financials']
CF = pd.DataFrame.from_dict(CF)
CF = CF.set_index("date")
df_FS = pd.concat([IS, BS, CF], axis=1, sort=True)
Date = df_FS.index
df_FS.fillna(0, inplace=True)
# EARNINGS & REVENUE
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=Date, y=df_FS['Revenue'],
mode='lines+markers',
name='Revenue'), secondary_y=False, )
fig.add_trace(go.Bar(x=Date, y=df_FS['Profit Margin'],
opacity=0.2,
name='Profit Margin'), secondary_y=True, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Consolidated Income'],
mode='lines+markers',
name='Earnings'), secondary_y=False, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Operating Cash Flow'],
mode='lines+markers',
name='Operating Cash Flow'), secondary_y=False, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Free Cash Flow'],
mode='lines+markers',
name='Free Cash Flow'), secondary_y=False, )
fig.add_trace(go.Scatter(x=Date, y=df_FS['Operating Expenses'],
mode='lines+markers',
name='Operating Expenses'), secondary_y=False, )
fig.update_layout(title="EARNINGS & REVENUE",
barmode='group', hovermode='x')
fig.update_yaxes(title_text="in USD", secondary_y=False)
fig.update_yaxes(title_text="Profit Margin", secondary_y=True)
fig.update_xaxes(rangeslider_visible=True)
return fig
if __name__ == '__main__':
app.run_server(debug=False)
You can play around around with the input and state options and see how the app responds. Cheers!
PS: I added another callback so after the first qoy
and ticker
values, you can simply change ticker
in the input field and hit the enter button (since qoy
would have already been selected) to update your app. Hopefully this will give you more insight about how callbacks work.