Hi,
I am trying to create a plotly dash dashboard using multi-level dropdown list/ radio button options which will load plotly graph figures as well as html.iframe embedded charts. The problem is I am unable to configure the dropdown option equity
to the html.iframe object. I need this MongoDB iFrame plot to be visible only when the dropdown option “Equity” is selected and the iframe should load in the same area where the dcc.Graph figure is shown. That is iFrame plot should move up into the dcc.Graph figure area and the label Chart X-Axis
needs to get hidden.
Also, the dcc.Graph object will need to become visible only when the dropdown options
Balance
and EquityDailyAggregation
is selected and remain hidden when dropdown option = Equity
The code for this project is written below:
## Load Libraries
from dash import Dash, html, dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import plotly.express as px
import plotly.graph_objects as go
import json
import pandas as pd
import numpy as np
######
# Instantiate our App and incorporate BOOTSTRAP theme stylesheet
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
def max_drawdown(df: pd.DataFrame, equity_col_name:str = "equity") -> tuple:
'''
Computes the points of maximum drawdown on the equity series data
and returns the indices and drawdown peak/trough values.
Parameters
----------
df : pd.DataFrame, dataframe containing the equity curve series data
equity_col_name : str, column name of the dataframe containing the equity data.
Returns
-------
max_drawdown, df : tuple unpacking -> (float, pd.DataFrame)
float of max_drawdown, pd.DataFrame containing peak and trough values and indices location.
'''
xs = df[[equity_col_name]].values
# j = np.argmax(np.maximum.accumulate(xs) - xs) # end of the period
j = np.argmax((np.maximum.accumulate(xs) - xs) / np.maximum.accumulate(xs))
i = np.argmax(xs[:j]) # start of period
df_i_max = df.iloc[[i]].to_dict(orient = 'records')
df_j_min = df.iloc[[j]].to_dict(orient = 'records')
recorded_peak_balance = df["equity"].iloc[i]
floating_lowest_equity = df["equity"].iloc[j]
max_drawdown_percent = (recorded_peak_balance - floating_lowest_equity)/recorded_peak_balance
print("Recorded Peak Balance:",recorded_peak_balance ,"\nFloating Lowest Equity:",floating_lowest_equity,
"\nMaximum Drawdown: {} %".format(np.round(max_drawdown_percent*100, 3)))
output_data = df_i_max + df_j_min
return pd.DataFrame(output_data), max_drawdown_percent
## Plotly Figure
def equity_plot(line_plot_df : pd.DataFrame, scatterplot_df : pd.DataFrame, x_axis_label : str = "time", y_axis_label: str = "Floating Equity (US$)") -> go.Figure():
''' Returns the Equity plot vs. time or Equity plot vs. nr. of closed positions along with drawdown markers.
Parameters
----------
line_plot_df : pd.DataFrame,
input dataframe for the lineplot, containing the equity data
scatterplot_df: pd.DataFrame,
input dataframe for the scatterplot, containing the peak and trough of the max. drawdown
x_axis_label : str, {"time", "ClosedPositions"}, default = "time"
x-axis label of the plot and also the column name in line_plot_df and scatterplot_df
y_axis_label : str, default = "Floating Equity (US$)",
y-axis label of the plot, no reference to any dataframe column names
Returns
-------
fig_final : Plotly graph objects figure
'''
fig1 = px.line(line_plot_df, x = x_axis_label, y="equity", markers = True, height=1920)
fig2 = px.scatter(scatterplot_df, x = x_axis_label, y="equity", height=1920)
fig2.update_traces(marker={'size': 12, 'color' : ["limegreen","red"], 'symbol' : ['triangle-up','triangle-down']})
## Draw Dotted Line connecting maximum drawdown
fig3 = go.Scatter(x = scatterplot_df[x_axis_label].to_list(),y = scatterplot_df['equity'].to_list(),name = "MaxDrawDown",
mode = 'lines',line = dict(color='firebrick', width=1, dash='dot'))
fig_final = go.Figure(data=fig1.data + fig2.data).add_trace(fig3)
fig_final.update_layout(title="{} vs. {}".format(y_axis_label,x_axis_label),title_x = 0.5,
xaxis_title = x_axis_label, yaxis_title= y_axis_label,
titlefont=dict(family="Courier New",size=16,color="RoyalBlue"),
legend=dict(yanchor="top",y=0.99,xanchor="left", x=0.01),
plot_bgcolor = "beige",
font=dict(family="monospace",size=10,color="RebeccaPurple"))
return fig_final
chart_url = "https://charts.mongodb.com/charts-project-0-nvgoj/embed/charts?id=624eb117-33c6-43f2-879f-a9158b315fb3&maxDataAge=10&theme=dark&autoRefresh=true"
# Build the layout to define what will be displayed on the page
app.layout = dbc.Container(
[
dbc.Row([
dbc.Col([html.H1("Charts")], width=10)], justify="center"),
dbc.Row([
dbc.Col([
html.Label('Chart Y-Axis:'),
dcc.Dropdown(id='chart_y_axis',
options=[{'label' : 'Equity', 'value' : 'equity'},
{'label' : 'Equity_DailyAggregation', 'value' : 'equity_agg'},
{'label' : 'Balance', 'value' : 'balance'}],
# placeholder="Select chart",
value = "balance",
optionHeight = 25, style = {'width':'75%'})
], width=4),
dbc.Col([
html.Label('Chart X-Axis:'),
dcc.RadioItems(id='chart_x_axis',value='time',inline=True,
labelStyle = {'width':'13%'}, inputStyle={"margin-right": "5px"})
], width=8)
]),
# <iframe style="background: #21313C; border: none; border-radius: 2px; box-shadow: 0 2px 10px 0 rgba(70, 76, 79, .2);" width="1280" height="720" ></iframe>
html.Div([dcc.Graph(id='graph')]),
html.Iframe(id='mongoplot',src=chart_url, style={'background' : '#21313C', 'border': 'none','width': "100%", 'height': "540px"}),
]
)
# callback is used to create app interactivity
#---------------------------------------------------------------
@app.callback(
Output('chart_x_axis', 'options'),
Input('chart_y_axis', 'value'))
def filter_radio_options(first_dropdown):
if first_dropdown == "equity_agg":
options = [ {'label' : 'Time', 'value' : 'time'} ]
if first_dropdown == "equity":
options = []
else:
options = [{'label' : 'Time', 'value' : 'time'},{'label' : 'Nr. of Trades', 'value' : 'ClosedPositions'}]
return options
#---------------------------------------------------------------
@app.callback(
Output(component_id='graph', component_property='figure'),
[Input(component_id='chart_y_axis', component_property='value'),
Input(component_id='chart_x_axis', component_property='value')])
def update_graph(chart_y_axis, chart_x_axis):
if chart_y_axis == "balance":
y_axis_label = "Closed Balance (US$)"
balance_curve = pd.read_csv('ClosedTradesBalance.csv')
balance_curve['ClosedPositions'] = balance_curve.index
line_plot = balance_curve
drawdown_df, max_dd = max_drawdown(balance_curve)
elif chart_y_axis == "equity_agg":
y_axis_label = "DailyFloatingEquity.Aggregated (US$)"
df_fedr = pd.read_csv('FloatingEquityDailyReport.csv')
df1 = df_fedr[["timestamp_min_equity","MinEquity"]].rename(columns={"timestamp_min_equity": "time", "MinEquity": "equity"})
df2 = df_fedr[["timestamp_max_equity","MaxEquity"]].rename(columns={"timestamp_max_equity": "time", "MaxEquity": "equity"})
FloatingEquityAgg = pd.concat([df1,df2], ignore_index=True, join = "outer", verify_integrity=True, axis = 0).sort_values(by=['time']).reset_index(drop = True)
line_plot = FloatingEquityAgg
drawdown_df, max_dd = max_drawdown(FloatingEquityAgg)
# elif chart_y_axis == "equity":
## How to Load iFrame instead of Plot
figure = equity_plot(line_plot, drawdown_df, x_axis_label = chart_x_axis, y_axis_label = y_axis_label)
return figure
# Run the App
if __name__ == '__main__':
app.run_server(port=8001,debug=True)
The datasets for this app are uploaded in dropbox:
1.) ClosedTradesBalance.csv
2.) FloatingEquityDailyReport.csv
Would greatly appreciate if a plotly expert here could provide any solution to the above issue of loading html iframe object along with dcc.graph objects using a dropdown list and also tell me how to center the label “Charts” and the dropdown list “Chart Y-Axis” and RadioButtons “Chart X-axis” to the center of the web page?
Thanks and Regards,
Dilip