from datetime import datetime import time import dash_table import plotly.graph_objects as go import dash_core_components as dcc import dash_html_components as html from dash import Dash from dash_table.Format import Format from dash.dependencies import Input, Output colorway = ["#9a58cc", '#FF4F00', '#375CB1', '#FF7400', '#FFF400', '#FF0056'] PAGE_SIZE = 20 def load_dash(comp_kpi, rec_data_mod, div_data, his_data): start_time = time.time() print(" -- Rendering STOCKDASH @ %s -----" % datetime.fromtimestamp(start_time)) app = Dash(__name__) app.layout = html.Div(children=[ html.Div(className='row', children=[html.Div(className='three columns div-user-controls', children=[ html.H2('STOCKDASH'), html.P('''Visualising data with Plotly - Dash'''), html.P('''Pick one or more KPIs from the dropdown below.'''), html.Div( className='div-for-dropdown', children=[ dcc.Dropdown(id='stockselector', options=[{'label': i, 'value': i} for i in comp_kpi._get_numeric_data().columns], multi=True, value=[comp_kpi._get_numeric_data().columns[0]], style={'backgroundColor': '#1E1E1E'}, className='stockselector') ], style={'color': '#1E1E1E'}) ]), html.Div(className='nine columns div-for-charts bg-grey', style={'padding': 0}, children=[ dash_table.DataTable( id='company-kpi-data', columns= [{"name": i, "id": i, 'deletable': True, 'type': 'numeric', 'format': Format(group=',')} if i in comp_kpi._get_numeric_data().columns else {"name": i, "id": i, 'deletable': True} for i in comp_kpi.columns], style_as_list_view=True, style_data_conditional=[{ 'if': {'column_editable': False}, 'backgroundColor': 'rgba(50, 50, 50, 0.5)', 'textAlign': 'left', 'color': 'white', 'padding': 7 }], style_filter_conditional=[{ 'if': {'column_editable': False}, 'backgroundColor': 'rgba(40, 40, 40,0.5)', 'textAlign': 'left', 'color': 'white' }], style_header_conditional=[{ 'if': {'column_editable': False}, 'backgroundColor': 'rgba(30, 30, 30,0.5)', 'textAlign': 'left', 'fontWeight': 'bold', 'color': 'white' }], page_current=0, page_size=PAGE_SIZE, page_action='custom', filter_action='custom', filter_query='', sort_action='custom', sort_mode='multi', sort_by=[] ), dcc.Graph( id='bar-chart-marketcap', className='bg-grey', hoverData={'points': [{'x': 'AAPL'}]}, animate=True), dcc.Graph( id='timeseries-chart-price', className='bg-grey', config={'displayModeBar': False}, animate=False), dcc.Graph( id='recom-bar-chart', className='bg-grey', config={'displayModeBar': False}, animate=True) ]) ]) ]) @app.callback(Output('bar-chart-marketcap', 'figure'), [Input('company-kpi-data', 'data'), Input('stockselector', 'value')]) def update_graph(data, selected_columns): used_symbols = [x['symbol'] for x in data] figure = go.Figure( layout=go.Layout( colorway=colorway, template='plotly_dark', paper_bgcolor='rgba(0, 0, 0, 0)', plot_bgcolor='rgba(0, 0, 0, 0)', margin={'b': 15}, hovermode='x', autosize=True, title={'text': 'Market Data', 'font': {'color': 'white'}, 'x': 0.5} )) val = dict() val["xaxis"] = dict(domain=[0.15, 0.85]) for i, column in enumerate(selected_columns): i += 1 figure.add_trace(go.Bar(name=column, x=used_symbols, y=[x[column] for x in data], yaxis='y' + str(i), offsetgroup=i)) val["yaxis%s" % i] = dict( title=column, titlefont=dict(color=colorway[i - 1]), tickfont=dict(color=colorway[i - 1]), ) if i == 2: val["yaxis2"].update(dict( anchor="x", overlaying="y", side="right" )) elif i == 3: val["yaxis3"].update(dict( anchor="free", overlaying="y", side="left", position=0.05 )) elif i == 4: val["yaxis4"].update(dict( anchor="free", overlaying="y", side="right", position=0.95 )) figure.update_layout(val) figure.update_yaxes( showgrid=True, zeroline=True, zerolinewidth=1, zerolinecolor='White', ) return figure @app.callback(Output('recom-bar-chart', 'figure'), [Input('company-kpi-data', 'data')]) def update_graph(data): used_symbols = [x['symbol'] for x in data] df = rec_data_mod.loc['2020-12-31'].reset_index() df_tmp = df.loc[df['Symbol'].isin(used_symbols)] figure = go.Figure(layout=go.Layout( colorway=colorway, template='plotly_dark', paper_bgcolor='rgba(0, 0, 0, 0)', plot_bgcolor='rgba(0, 0, 0, 0)', margin={'b': 15}, hovermode='x', autosize=True, title={'text': 'Recommendation Data', 'font': {'color': 'white'}, 'x': 0.5}, barmode='stack' )) figure.add_trace(go.Bar(x=used_symbols, y=df_tmp['Positive'].tolist(), name='Positive Outlook', marker_color='#41B3A3')) figure.add_trace(go.Bar(x=used_symbols, y=df_tmp['Neutral'].tolist(), name='Neutral Outlook', marker_color='#E8A87C')) figure.add_trace(go.Bar(x=used_symbols, y=df_tmp['Negative'].tolist(), name='Negative Outlook', marker_color='#E27D60')) return figure @app.callback(Output('timeseries-chart-price', 'figure'), [Input('bar-chart-marketcap', 'hoverData')]) def update_graph(hoverData): trace1 = [] columns = ['Close', 'priceMA50', 'priceMA200'] df_sub = his_data[his_data['Symbol'] == hoverData['points'][0]['x']] for column in columns: trace1.append(go.Scatter(x=df_sub['Date'], y=df_sub[column], mode='lines', opacity=0.7, name=hoverData['points'][0]['x'] + "-" + column, textposition='bottom center')) traces = [trace1] data = [val for sublist in traces for val in sublist] figure = {'data': data, 'layout': go.Layout( colorway=colorway, template='plotly_dark', paper_bgcolor='rgba(0, 0, 0, 0)', plot_bgcolor='rgba(0, 0, 0, 0)', margin={'b': 15}, hovermode='x', autosize=True, title={'text': 'Stock Prices', 'font': {'color': 'white'}, 'x': 0.5}, xaxis={'range': [df_sub['Date'].min(), df_sub['Date'].max()]}, yaxis={'range': [0, df_sub['Close'].max() + df_sub['Close'].max() / 10]} ), } return figure def split_filter_part(filter_part): operators = [['ge ', '>='], ['le ', '<='], ['lt ', '<'], ['gt ', '>'], ['ne ', '!='], ['eq ', '='], ['contains '], ['datestartswith ']] for operator_type in operators: for operator in operator_type: if operator in filter_part: name_part, value_part = filter_part.split(operator, 1) name = name_part[name_part.find('{') + 1: name_part.rfind('}')] value_part = value_part.strip() v0 = value_part[0] if v0 == value_part[-1] and v0 in ("'", '"', '`'): value = value_part[1: -1].replace('\\' + v0, v0) else: try: value = float(value_part) except ValueError: value = value_part return name, operator_type[0].strip(), value return [None] * 3 @app.callback( Output('company-kpi-data', "data"), Input('company-kpi-data', "page_current"), Input('company-kpi-data', "page_size"), Input('company-kpi-data', "sort_by"), Input('company-kpi-data', 'filter_query')) def update_table(page_current, page_size, sort_by, filter): filtering_expressions = filter.split(' && ') dff = comp_kpi for filter_part in filtering_expressions: col_name, operator, filter_value = split_filter_part(filter_part) if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'): dff = dff.loc[getattr(dff[col_name], operator)(filter_value)] elif operator == 'contains': dff = dff.loc[dff[col_name].str.contains(filter_value)] elif operator == 'datestartswith': dff = dff.loc[dff[col_name].str.startswith(filter_value)] if len(sort_by): dff = dff.sort_values( [col['column_id'] for col in sort_by], ascending=[ col['direction'] == 'asc' for col in sort_by ], inplace=False ) page = page_current size = page_size return dff.iloc[page * size: (page + 1) * size].to_dict('records') print("Rendering loaded after %ss" % (time.time()-start_time)) app.run_server(debug=True)