Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import yfinance as yf | |
| import pandas as pd | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| import datetime as dt | |
| # Page config | |
| st.set_page_config( | |
| page_title="Stock Analysis Dashboard", | |
| page_icon="π", | |
| layout="wide" | |
| ) | |
| # Sidebar | |
| st.sidebar.header("Settings") | |
| ticker = st.sidebar.text_input("Ticker Symbol", "AAPL").upper() | |
| start = st.sidebar.date_input("Start Date", dt.date.today() - dt.timedelta(days=365)) | |
| end = st.sidebar.date_input("End Date", dt.date.today()) | |
| # Cache data | |
| def fetch_data(ticker, start, end): | |
| return yf.download(ticker, start=start, end=end) | |
| # Main | |
| st.title("π Stock Analysis Dashboard") | |
| st.subheader(f"{ticker} ({start} β {end})") | |
| # Fetch | |
| data = fetch_data(ticker, start, end) | |
| if data.empty: | |
| st.error("No data found. Check ticker or date range.") | |
| st.stop() | |
| # KPIs | |
| latest_close = data["Adj Close"].iloc[-1] | |
| prev_close = data["Adj Close"].iloc[-2] | |
| change = latest_close - prev_close | |
| pct_change = (change / prev_close) * 100 | |
| col1, col2, col3, col4 = st.columns(4) | |
| col1.metric("Latest Close", f"${latest_close:,.2f}", f"{pct_change:+.2f}%") | |
| col2.metric("Volume", f"{data['Volume'].iloc[-1]:,.0f}") | |
| col3.metric("High", f"${data['High'].max():,.2f}") | |
| col4.metric("Low", f"${data['Low'].min():,.2f}") | |
| # Chart | |
| fig = make_subplots( | |
| rows=2, cols=1, | |
| shared_xaxes=True, | |
| vertical_spacing=0.03, | |
| subplot_titles=("Price & Volume", "Volume"), | |
| row_heights=[0.7, 0.3] | |
| ) | |
| fig.add_trace( | |
| go.Candlestick( | |
| x=data.index, | |
| open=data["Open"], | |
| high=data["High"], | |
| low=data["Low"], | |
| close=data["Close"], | |
| name="Candle" | |
| ), | |
| row=1, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Bar( | |
| x=data.index, | |
| y=data["Volume"], | |
| name="Volume", | |
| marker_color="#636EFA" | |
| ), | |
| row=2, col=1 | |
| ) | |
| fig.update_layout( | |
| title=f"{ticker} Price & Volume", | |
| xaxis_rangeslider_visible=False, | |
| template="plotly_dark", | |
| height=600, | |
| showlegend=False | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Moving averages | |
| ma_window = st.sidebar.slider("Moving Average Window", 5, 200, 20) | |
| data[f"MA{ma_window}"] = data["Close"].rolling(ma_window).mean() | |
| st.subheader("Moving Average") | |
| ma_fig = go.Figure() | |
| ma_fig.add_trace(go.Scatter(x=data.index, y=data["Close"], name="Close")) | |
| ma_fig.add_trace(go.Scatter(x=data.index, y=data[f"MA{ma_window}"], name=f"MA{ma_window}")) | |
| ma_fig.update_layout(title=f"{ticker} Close vs MA{ma_window}", height=400) | |
| st.plotly_chart(ma_fig, use_container_width=True) | |
| # Download | |
| def convert_df(df): | |
| return df.to_csv().encode("utf-8") | |
| csv = convert_df(data) | |
| st.sidebar.download_button( | |
| label="Download CSV", | |
| data=csv, | |
| file_name=f"{ticker}_{start}_{end}.csv", | |
| mime="text/csv" | |
| ) |