Introduction
A stock dashboard is the project that makes data engineering feel real: live market data in, decisions out. Using yfinance — a free library wrapping Yahoo Finance data — you get years of price history for any ticker in one call, and this app turns it into a dashboard with interactive charts, moving averages, and multi-ticker comparison.
It is the grown-up version of the Pandas dashboard: same filtering and charting patterns, but the data updates itself from the real world. The caching strategy borrows directly from the currency converter.
Features
Prerequisites
pip install streamlit yfinance pandasStep 1: Create the Script
Save as stock_dashboard.py:
import streamlit as st
import yfinance as yf
import pandas as pd
st.set_page_config(page_title="Stock Dashboard", page_icon="📈", layout="wide")
st.title("📈 Stock Price Dashboard")
tickers_raw = st.text_input("Tickers (comma-separated)", value="AAPL, MSFT")
period = st.selectbox("Period", ["1mo", "3mo", "6mo", "1y", "2y", "5y"], index=3)
show_ma = st.checkbox("Show moving averages", value=True)
tickers = [t.strip().upper() for t in tickers_raw.split(",") if t.strip()]
@st.cache_data(ttl=600)
def load_history(ticker: str, period: str) -> pd.DataFrame:
df = yf.download(ticker, period=period, progress=False)
if isinstance(df.columns, pd.MultiIndex):
df.columns = df.columns.get_level_values(0)
return df
if tickers:
if len(tickers) == 1:
df = load_history(tickers[0], period)
if df.empty:
st.error(f"No data for {tickers[0]} — check the ticker symbol.")
st.stop()
latest = df["Close"].iloc[-1]
c1, c2, c3, c4 = st.columns(4)
c1.metric(tickers[0], f"${latest:,.2f}")
c2.metric("52w high", f"${df['Close'].max():,.2f}")
c3.metric("52w low", f"${df['Close'].min():,.2f}")
c4.metric("Avg volume", f"{df['Volume'].mean() / 1e6:,.1f}M")
plot_df = df[["Close"]].copy()
if show_ma:
plot_df["MA20"] = df["Close"].rolling(20).mean()
plot_df["MA50"] = df["Close"].rolling(50).mean()
st.line_chart(plot_df)
st.bar_chart(df["Volume"])
else:
st.subheader("Normalized performance (start = 100)")
combined = pd.DataFrame()
for t in tickers:
close = load_history(t, period)["Close"]
if not close.empty:
combined[t] = close / close.iloc[0] * 100
st.line_chart(combined)
st.caption("100 = first day of the selected period, so lines compare returns fairly.")
else:
st.info("Enter at least one ticker above.")Step 2: Run the App
streamlit run stock_dashboard.pyTry AAPL alone, then AAPL, MSFT, GOOGL together — compare mode shows which investment actually grew faster.
How It Works
yf.download() returns a DataFrame indexed by date with Open/High/Low/Close/Volume columns. Streamlit's st.line_chart renders date-indexed frames natively — the x-axis, gridlines, and hover values come free. This is the core advantage of keeping data in Pandas shape end-to-end, exactly as in the CSV explorer.
Moving averages are one-liners — .rolling(20).mean() — yet they transform the chart, smoothing daily noise into trend lines traders actually watch. The 20/50 pair is the classic combination: short-term momentum crossing long-term trend.
Compare mode solves a subtle visualization problem: raw prices can't share an axis (a $3,000 stock dwarfs a $50 one). Normalizing each series to start at 100 (close / close.iloc[0] * 100) turns prices into *returns*, making relative growth instantly readable — a trick worth stealing for any multi-series chart.
@st.cache_data(ttl=600) caches each ticker-period pair for ten minutes: instant reruns while you tweak charts, fresh-enough data for daily analysis.
Common Errors & Fixes
BRK-B (dash) not BRK.B (dot); check the exact symbol on Yahoo Finance.get_level_values(0) flattening in the loader handles it.ttl cache and load tickers in a loop *after* checking the cache, never in a tight retry loop.NaN and simply don't plot.Key Concepts
.rolling(n).mean() as trend smoothing.What to Try Next
plotly.graph_objects.Candlestick for OHLC detail.yf.Ticker(t).actions.FAQ
Is yfinance data real-time?
Close — prices are delayed up to 15 minutes for many exchanges, which is fine for dashboards but not for day trading.
Does this cost anything?
No — Yahoo's public data endpoints are free; yfinance just packages them. For production systems, use a paid API with an SLA.
Why normalize in compare mode instead of plotting raw prices?
Because the question is *which grew more*, not *which costs more*. Indexing to 100 answers the first question; raw prices visually answer the wrong one.