Have you ever wanted to look up a stock's performance on a specific date, only to find out the market was closed for a weekend or holiday? In this tutorial, we will build a program that will solve this problem using Python, Plotly, and Dash.
Instead of making a program that will just tell us the price for that date this program will create a local web server that will fetch the real time prices from the yfinance module. It takes the date and symbol chosen by the user, opens a smart 15-day window, handles market closures by redirecting to nearest working date, and makes an interactive candlestick graph.
---### 💼 Project features and result.
By the end of this guide, you will build be able to make a program that manages the following tasks:
- ➔ Real Time Data Extraction: It connects directly to the "Yahoo Finance" API through the
yfinancemodule to fetch the Open, High, Low, and Close prices. - ➔ Relevant window: Creates a relevant 15 days window perfect for studying trends and predicting prices.
- ➔ Market Closure Alerts: It smartly checks whether the stock exchange was active on your target date. If the market was closed, it alerts the user while showing the nearest trading days instead.
- ➔ Able to run perfectly fine on any environment:
It runs perfectly on any terminal system and runs inline i.e., makes the chart and window directly in the terminal on Jupyter and Google Colab notebooks
---### 📦 Step 1: All the imports and dependencies
To start, we will first need to import the libraries that will manage our UI layout, data, and graph making. Create a new file named
app.pyand add this code block to the top of your workspace:
import dash
from dash import dcc, html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
from datetime import datetime, timedelta
import yfinance as yf
import pandas as pd
Why these modules?* yfinance ➔ pulls stock prices
-
dash➔ To make the dashboard along with his UI. -
plotly.graph_objects➔ To make the chart. -
datetime&timedelta➔ To get the date and time -
pandas➔ To structure the data into a more manageable form.
to install these libraries just type in the following line of code in your notebook and run it:
pip install dash pandas plotly yfinance
python
---### 🌐 Step 2: Starting the Server
Afterwards type in the following line of code:-
app = dash.Dash(__name__)
What it does:-
This single line of code sets up our local server. The __name__ part tells Dash where to look for assets, passing this command tells dash where to look for the root folder of the project so it can find assets like styles and images.
---### ✍️ Step 3: Designing the Interface.
Now we choose how we want our interface for the dashboard to look like. I have provided my style as an example but feel free to make changes and style the Interface yourself so its to your liking. In order to do so below this block of code are definitions and explanations of what these commands do.
app.layout = html.Div(style={
'fontFamily': '"Segoe UI", Helvetica, Arial, sans-serif',
'padding': '30px',
'maxWidth': '900px',
'margin': 'auto',
'backgroundColor': '#ffffff'
}, children=[
html.H2("Historical Stock Analysis Dashboard 📊", style={'textAlign': 'center', 'color': '#2c3e50', 'marginBottom': '10px'}),
html.P("Enter a stock ticker and target date to analyze historical market performance.", style={'textAlign': 'center', 'color': '#7f8c8d', 'marginBottom': '30px'}),
html.Div(style={
'backgroundColor': '#f8f9fa',
'padding': '25px',
'borderRadius': '8px',
'marginBottom': '25px',
'boxShadow': '0 4px 6px rgba(0,0,0,0.05)'
}, children=[
html.Label("Stock Ticker Symbol:", style={'fontWeight': 'bold', 'display': 'block', 'marginBottom': '8px', 'color': '#34495e'}),
dcc.Input(id='ticker-input', type='text', value='AAPL', style={
'width': '100%', 'padding': '10px', 'fontSize': '14px', 'marginBottom': '20px', 'borderRadius': '4px', 'border': '1px solid #ccc', 'boxSizing': 'border-box'
}),
html.Label("Select Date (YYYY-MM-DD):", style={'fontWeight': 'bold', 'display': 'block', 'marginBottom': '8px', 'color': '#34495e'}),
dcc.Input(id='date-input', type='text', value='2026-01-15', style={
'width': '100%', 'padding': '10px', 'fontSize': '14px', 'marginBottom': '25px', 'borderRadius': '4px', 'border': '1px solid #ccc', 'boxSizing': 'border-box'
}),
html.Button("Get Market Summary", id='submit-btn', n_clicks=0, style={
'width': '100%', 'padding': '12px', 'backgroundColor': '#007bff', 'color': 'white', 'border': 'none', 'borderRadius': '4px', 'fontSize': '16px', 'cursor': 'pointer', 'fontWeight': 'bold'
})
]),
html.Div(id='status-output', style={'textAlign': 'center', 'fontWeight': 'bold', 'marginBottom': '20px', 'fontSize': '16px'}),
dcc.Graph(id='price-graph', config={'displayModeBar': False})
])
Explaining the UI Components*
html.Div :- what it does is it makes a box containing information (children) you can customise what is in this box using children[] and change how it looks using style{} to change color, font, size, bold, etc.
-
dcc.Input:- Creates a place for the user to input data in this case {ticker symbol} and target date. It is simply a box where the user is to enter the input. -
dcc.GraphCreates a graph to better showcase the data to the user. The settingconfig={'displayModeBar': False}removes excess button trays to maintain a clean and minimalist UI design.
Feel free to play around and experiment alot in these commands so you get the hang of them.
---### 🔗 Step 4: Reactive Callback
To give life to our interface, we use the dash @app.callback() command. This part of the script will just listen for user inputs.
@app.callback(
[Output('price-graph', 'figure'),
Output('status-output', 'children'),
Output('status-output', 'style')],
[Input('submit-btn', 'n_clicks')],
[State('ticker-input', 'value'),
State('date-input', 'value')]
)
def update_dashboard(n_clicks, ticker, date_str):
# This empty base state serves as a clean canvas placeholder
empty_fig = go.Figure(layout=go.Layout(
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
xaxis={'visible': False},
yaxis={'visible': False}
))
-
Output: Points to the exact properties on the page we want to change. Here, we target the graph's internalfigureattribute, along with thechildrentext and colorstyleof our warning alert container. -
Input: Tracks interactions. Whenever the user enters data by an action we track that using the linen_clickswhich detects input and runs the rest of the script immediately. -
State: The state block prevents the program from showing error while the user is still entering data For example say the user chose to check price of AAPL the state function will make sure the program doesn't run when the user has only entered A but will wait for the user to click the button to run the program.
---### 🧮 Step 5: Financial logic.
We will place our Financial Logic. Directly underneath our empty_fig block inside the update_dashboard function, we will add our data, logic, and graph generation code:
# 1. Check for empty fields
if not ticker or not date_str:
return empty_fig, "Please input a ticker symbol and target date.", {'color': '#e74c3c'}
try:
# 2. Parse input strings into datetime objects safely
target_date = datetime.strptime(date_str.strip(), "%Y-%m-%d").date()
# 3. Formulate our 15-day context timeline window (10 days before, 5 days after)
start_date = target_date - timedelta(days=10)
end_date = target_date + timedelta(days=5)
# 4. Sanitize and normalize input text criteria
ticker_clean = ticker.strip().upper()
stock = yf.Ticker(ticker_clean)
# 5. Extract historical information windows from API lines
history = stock.history(start=start_date.strftime("%Y-%m-%d"),
end=(end_date + timedelta(days=1)).strftime("%Y-%m-%d"))
if history.empty:
return empty_fig, f"No transactional data found for symbol '{ticker_clean}' around {date_str}.", {'color': '#e74c3c'}
Re-index dates into plain text strings to allow clean key lookups
history.index = history.index.strftime('%Y-%m-%d')
# 6. Assemble the custom Plotly Candlestick configuration arrays
fig = go.Figure(data=[go.Candlestick(
x=history.index,
open=history['Open'],
high=history['High'],
low=history['Low'],
close=history['Close'],
increasing_line_color='#2ecc71', # Professional soft green for gains
decreasing_line_color='#e74c3c' # Clean red for losses
)])
fig.update_layout(
title=f"{ticker_clean} Performance Matrix around Target Date: {date_str}",
yaxis_title="Stock Value (USD)",
xaxis_title="Trading Session Date",
template="plotly_white",
xaxis_rangeslider_visible=False,
margin=dict(l=40, r=40, t=60, b=40)
)
# 7. Evaluate operational closures and match messaging strategies
if date_str in history.index:
t_open = history.loc[date_str, 'Open']
t_close = history.loc[date_str, 'Close']
msg = f"➔ Success! Target Date Metrics Found ({date_str}) -> Open: ${t_open:,.2f} | Close: ${t_close:,.2f}"
status_style = {'color': '#27ae60'}
else:
msg = f"⚠️ Target date ({date_str}) was a market closure day. Visualizing nearest open sessions."
status_style = {'color': '#f39c12'}
return fig, msg, status_style
except ValueError:
return empty_fig, "Invalid layout structure. Please use YYYY-MM-DD format.", {'color': '#e74c3c'}
except Exception as e:
return empty_fig, f"Network Extraction Alert: {str(e)}", {'color': '#e74c3c'}
Main Logic:-
- Polishing input: We use .strip() and .upper() immediately after the input so the user's input will be processed without making it too grammatically heavy for the user. For example say the user wanted to check prices for tesla shares with ticker symbol
TSLAthen they can just writetslaorTsla, etc without the program rejecting the input. - Pandas: By using this line
history.index.strftime('%Y-%m-%d'), we match our data to the standard dates used on a calendar. This lets us check market history with simple if/else conditions. - API Boundary Tracking: When getting the target date window limits, we add a single day buffer using
timedelta(days=1)onto our end_date calculation. This makes sure that Yahoo finance gets the data in a valid time period efficiently.
🌐 Step 6: Multiple-Environment
To complete our script dashboard, we will add the final piece of operational logic that we will place at the very end of our app.py script:
if __name__ == '__main__':
app.run( debug=True,
jupyter_mode='inline',
jupyter_height=750 )
This blocks ensures that the script will run on any compiler and will run inline that is run directly in the terminal window as well show the dashboard window in the terminal window on jupyter and Google Colab notebooks.
🎉 Conclusion and Summary of Achievements
Congratulations! You have made a highly accurate, cross platform, financial dashboard using basic python. By breaking out of traditional code you have demonstrated amazing skills in dashboard and plotly modules in python.
To take your dashboard code to the next level, experiment with the following upgrades:
- ➔ Modify your Plotly graph to match different styles try adding toggle-able dark mode Directly in the program.
- ➔ Expand your callback to also accept secondary data, allowing the user to compare two ticker prices on one window in using one simple program.
At the end of this tutorial you should have a program that should look something like this
Top comments (0)