In this tutorial, we set out to build an advanced interactive dashboard using Dash, Plotly, and Bootstrap. We highlight not only how these tools enable us to design layouts and visualizations, but also how Dashโs callback mechanism links controls to outputs, allowing for real-time responsiveness. By combining local execution with the ability to run in cloud platforms like Google Colab, we explore a workflow that is both flexible and practical. Check out theย FULL CODES here.
!pip install dash plotly pandas numpy dash-bootstrap-components
import dash
from dash import dcc, html, Input, Output, callback, dash_table
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import dash_bootstrap_components as dbc
print("Generating sample data...")
np.random.seed(42)We begin by installing and importing the necessary components, including Dash, Plotly, Pandas, NumPy, and Bootstrap, to set up our dashboard environment. We also initialize random seeds and generate sample data so that we can consistently test the interactive features as we build them. Check out theย FULL CODES here.
start_date = datetime(2023, 1, 1)
end_date = datetime(2024, 12, 31)
dates = pd.date_range(start=start_date, end=end_date, freq='D')
stock_names = ['AAPL', 'GOOGL', 'MSFT', 'AMZN', 'TSLA']
all_data = []
base_prices = {'AAPL': 150, 'GOOGL': 120, 'MSFT': 250, 'AMZN': 100, 'TSLA': 200}
for stock in stock_names:
print(f"Creating data for {stock}...")
base_price = base_prices[stock]
n_days = len(dates)
returns = np.random.normal(0.0005, 0.025, n_days)
prices = np.zeros(n_days)
prices[0] = base_price
for i in range(1, n_days):
prices[i] = prices[i-1] * (1 + returns[i])
volumes = np.random.lognormal(15, 0.5, n_days).astype(int)
stock_df = pd.DataFrame({
'Date': dates,
'Stock': stock,
'Price': prices,
'Volume': volumes,
'Returns': np.concatenate([[0], np.diff(prices) / prices[:-1]]),
'Sector': np.random.choice(['Technology', 'Consumer', 'Automotive'], 1)[0]
})
all_data.append(stock_df)
df = pd.concat(all_data, ignore_index=True)
df['Date'] = pd.to_datetime(df['Date'])
df_sorted = df.sort_values(['Stock', 'Date']).reset_index(drop=True)
print("Calculating technical indicators...")
df_sorted['MA_20'] = df_sorted.groupby('Stock')['Price'].transform(lambda x: x.rolling(20, min_periods=1).mean())
df_sorted['Volatility'] = df_sorted.groupby('Stock')['Returns'].transform(lambda x: x.rolling(30, min_periods=1).std())
df = df_sorted.copy()
print(f"Data generated successfully! Shape: {df.shape}")
print(f"Date range: {df['Date'].min()} to {df['Date'].max()}")
print(f"Stocks: {df['Stock'].unique().tolist()}")We generate synthetic stock data, including prices, volumes, and returns, for multiple tickers across a specified date range. We calculate moving averages and volatility to enrich the dataset with useful technical indicators, providing a strong foundation for building interactive visualizations. Check out theย FULL CODES here.
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
html.H1("๐ Advanced Financial Dashboard", className="text-center mb-4"),
html.P(f"Interactive dashboard with {len(df)} data points across {len(stock_names)} stocks",
className="text-center text-muted"),
html.Hr()
])
]),
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H5("๐๏ธ Dashboard Controls", className="card-title"),
html.Label("Select Stocks:", className="fw-bold mt-3"),
dcc.Dropdown(
id='stock-dropdown',
options=[{'label': f'{stock} ({base_prices[stock]})', 'value': stock}
for stock in stock_names],
value=['AAPL', 'GOOGL'],
multi=True,
placeholder="Choose stocks to analyze..."
),
html.Label("Date Range:", className="fw-bold mt-3"),
dcc.DatePickerRange(
id='date-picker-range',
start_date='2023-06-01',
end_date='2024-06-01',
display_format='YYYY-MM-DD',
style={'width': '100%'}
),
html.Label("Chart Style:", className="fw-bold mt-3"),
dcc.RadioItems(
id='chart-type',
options=[
{'label': ' Line Chart', 'value': 'line'},
{'label': ' Area Chart', 'value': 'area'},
{'label': ' Scatter Plot', 'value': 'scatter'}
],
value='line',
labelStyle={'display': 'block', 'margin': '5px'}
),
dbc.Checklist(
id='show-ma',
options=[{'label': ' Show Moving Average', 'value': 'show'}],
value=[],
style={'margin': '10px 0'}
),
])
], className="h-100")
], width=3),
dbc.Col([
dbc.Card([
dbc.CardHeader("๐ Stock Price Analysis"),
dbc.CardBody([
dcc.Graph(id='main-chart', style={'height': '450px'})
])
])
], width=9)
], className="mb-4"),
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4(id="avg-price", className="text-primary mb-0"),
html.Small("Average Price", className="text-muted")
])
])
], width=3),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4(id="total-volume", className="text-success mb-0"),
html.Small("Total Volume", className="text-muted")
])
])
], width=3),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4(id="price-range", className="text-info mb-0"),
html.Small("Price Range", className="text-muted")
])
])
], width=3),
dbc.Col([
dbc.Card([
dbc.CardBody([
html.H4(id="data-points", className="text-warning mb-0"),
html.Small("Data Points", className="text-muted")
])
])
], width=3)
], className="mb-4"),
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("๐ Trading Volume"),
dbc.CardBody([
dcc.Graph(id='volume-chart', style={'height': '300px'})
])
])
], width=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("๐ Returns Distribution"),
dbc.CardBody([
dcc.Graph(id='returns-chart', style={'height': '300px'})
])
])
], width=6)
], className="mb-4"),
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("๐ Latest Stock Data"),
dbc.CardBody([
dash_table.DataTable(
id='data-table',
columns=[
{'name': 'Stock', 'id': 'Stock'},
{'name': 'Date', 'id': 'Date'},
{'name': 'Price ($)', 'id': 'Price', 'type': 'numeric',
'format': {'specifier': '.2f'}},
{'name': 'Volume', 'id': 'Volume', 'type': 'numeric',
'format': {'specifier': ',.0f'}},
{'name': 'Daily Return (%)', 'id': 'Returns', 'type': 'numeric',
'format': {'specifier': '.2%'}}
],
style_cell={'textAlign': 'center', 'fontSize': '14px', 'padding': '10px'},
style_header={'backgroundColor': 'rgb(230, 230, 230)', 'fontWeight': 'bold'},
style_data_conditional=[
{
'if': {'filter_query': '{Returns} > 0'},
'backgroundColor': '#d4edda'
},
{
'if': {'filter_query': '{Returns} < 0'},
'backgroundColor': '#f8d7da'
}
],
page_size=15,
sort_action="native",
filter_action="native"
)
])
])
])
])
], fluid=True)We define the app layout with Bootstrap rows and cards, where we place controls (dropdown, date range, chart style, MA toggle) alongside the main graph. We add metric cards, two secondary graphs, and a sortable/filterable data table, so we organize everything into a responsive, clean interface that we can wire up to callbacks next. Check out theย FULL CODES here.
@callback(
[Output('main-chart', 'figure'),
Output('volume-chart', 'figure'),
Output('returns-chart', 'figure'),
Output('data-table', 'data'),
Output('avg-price', 'children'),
Output('total-volume', 'children'),
Output('price-range', 'children'),
Output('data-points', 'children')],
[Input('stock-dropdown', 'value'),
Input('date-picker-range', 'start_date'),
Input('date-picker-range', 'end_date'),
Input('chart-type', 'value'),
Input('show-ma', 'value')]
)
def update_all_charts(selected_stocks, start_date, end_date, chart_type, show_ma):
print(f"Callback triggered with stocks: {selected_stocks}")
if not selected_stocks:
selected_stocks = ['AAPL']
filtered_df = df[
(df['Stock'].isin(selected_stocks)) &
(df['Date'] >= start_date) &
(df['Date'] <= end_date)
].copy()
print(f"Filtered data shape: {filtered_df.shape}")
if filtered_df.empty:
filtered_df = df[df['Stock'].isin(selected_stocks)].copy()
print(f"Using all available data. Shape: {filtered_df.shape}")
if chart_type == 'line':
main_fig = px.line(filtered_df, x='Date', y='Price', color='Stock',
title=f'Stock Prices - {chart_type.title()} View',
labels={'Price': 'Price ($)', 'Date': 'Date'})
elif chart_type == 'area':
main_fig = px.area(filtered_df, x='Date', y='Price', color='Stock',
title=f'Stock Prices - {chart_type.title()} View',
labels={'Price': 'Price ($)', 'Date': 'Date'})
else:
main_fig = px.scatter(filtered_df, x='Date', y='Price', color='Stock',
title=f'Stock Prices - {chart_type.title()} View',
labels={'Price': 'Price ($)', 'Date': 'Date'})
if 'show' in show_ma:
for stock in selected_stocks:
stock_data = filtered_df[filtered_df['Stock'] == stock]
if not stock_data.empty:
main_fig.add_scatter(
x=stock_data['Date'],
y=stock_data['MA_20'],
mode='lines',
name=f'{stock} MA-20',
line=dict(dash='dash', width=2)
)
main_fig.update_layout(height=450, showlegend=True, hovermode='x unified')
volume_fig = px.bar(filtered_df, x='Date', y='Volume', color='Stock',
title='Daily Trading Volume',
labels={'Volume': 'Volume (shares)', 'Date': 'Date'})
volume_fig.update_layout(height=300, showlegend=True)
returns_fig = px.histogram(filtered_df.dropna(subset=['Returns']),
x='Returns', color='Stock',
title='Daily Returns Distribution',
labels={'Returns': 'Daily Returns', 'count': 'Frequency'},
nbins=50)
returns_fig.update_layout(height=300, showlegend=True)
if not filtered_df.empty:
avg_price = f"${filtered_df['Price'].mean():.2f}"
total_volume = f"{filtered_df['Volume'].sum():,.0f}"
price_range = f"${filtered_df['Price'].min():.0f} - ${filtered_df['Price'].max():.0f}"
data_points = f"{len(filtered_df):,}"
table_data = filtered_df.nlargest(100, 'Date')[
['Stock', 'Date', 'Price', 'Volume', 'Returns']
].round(4).to_dict('records')
for row in table_data:
row['Date'] = row['Date'].strftime('%Y-%m-%d') if pd.notnull(row['Date']) else ''
else:
avg_price = "No data"
total_volume = "No data"
price_range = "No data"
data_points = "0"
table_data = []
return (main_fig, volume_fig, returns_fig, table_data,
avg_price, total_volume, price_range, data_points)We wire up Dashโs callback to connect our controls to every output, so changing any input instantly updates charts, stats, and the table. We filter the dataframe by selections and dates, build figures (plus optional MA overlays), and compute summary metrics. Finally, we format recent rows for the table so we can inspect the latest results at a glance. Check out theย FULL CODES here.
if __name__ == '__main__':
print("Starting Dash app...")
print("Available data preview:")
print(df.head())
print(f"Total rows: {len(df)}")
app.run(mode='inline', port=8050, debug=True, height=1000)
# app.run(debug=True)We set up the entry point for running the app. We print a quick preview of the dataset to determine whatโs available, and then launch the Dash server. In Colab, we can run it inline. For local development, we can simply switch to the regular app.run(debug=True) for desktop development.
In conclusion, we integrate interactive charts, responsive layouts, and Dashโs callback mechanism into a cohesive application. We see how the callbacks orchestrate communication between user input and dynamic updates, turning static visuals into powerful interactive tools. With the ability to operate smoothly both locally and online, this approach provides a versatile foundation that we can extend for broader applications.
Check out theย FULL CODES here.ย Feel free to check out ourย GitHub Page for Tutorials, Codes and Notebooks.ย Also,ย feel free to follow us onย Twitterย and donโt forget to join ourย 100k+ ML SubRedditย and Subscribe toย our Newsletter.
Asif Razzaq is the CEO of Marktechpost AI Media Inc.. As a visionary entrepreneur and engineer, Asif is committed to harnessing the potential of Artificial Intelligence for social good. His most recent endeavor is the launch of an Artificial Intelligence Media Platform, Marktechpost, which stands out for its in-depth coverage of machine learning and deep learning news that is both technically sound and easily understandable by a wide audience. The platform boasts of over 2 million monthly views, illustrating its popularity among audiences.






