Spaces:
Sleeping
Sleeping
| from datetime import date | |
| import numpy as np | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import streamlit as st | |
| import yfinance as yf | |
| # ------------------------------------------------- | |
| # Streamlit Configuration | |
| # ------------------------------------------------- | |
| st.set_page_config( | |
| page_title="Financial Dashboard", | |
| layout="wide", | |
| page_icon="๐ค" | |
| ) | |
| # ------------------------------------------------- | |
| # Initialize Session State for Navigation and Carousel | |
| # ------------------------------------------------- | |
| if "selected_tab" not in st.session_state: | |
| st.session_state.selected_tab = "Home" # Default tab | |
| if 'carousel_slide' not in st.session_state: | |
| st.session_state.carousel_slide = 0 # Initialize carousel slide index | |
| # ------------------------------------------------- | |
| # Define All Possible Tabs | |
| # ------------------------------------------------- | |
| tabs = [ | |
| ("๐ Home", "Home"), | |
| ("๐ฑ Forex", "Forex"), | |
| ("๐ Stocks", "Stocks"), | |
| ("๐ฐ Crypto", "Crypto"), | |
| ("๐ง Commodities", "Commodities"), | |
| ("๐ Markets", "Markets"), | |
| ("๐ ETFs/Mutual Funds", "ETFs/Mutual Funds"), | |
| ("๐น Investments", "Investments") | |
| ] | |
| # ------------------------------------------------- | |
| # Sidebar with Navigation | |
| # ------------------------------------------------- | |
| st.sidebar.title("๐ค Financial Dashboard") | |
| def sidebar_navigation(tabs): | |
| for display_name, internal_name in tabs: | |
| if st.sidebar.button(display_name): | |
| st.session_state.selected_tab = internal_name | |
| sidebar_navigation(tabs) | |
| # ------------------------------------------------- | |
| # Date Range & Timeframe Selection | |
| # ------------------------------------------------- | |
| st.sidebar.markdown("---") | |
| st.sidebar.markdown("#### Date Range & Timeframe") | |
| start_date = st.sidebar.date_input("Start Date", date(2023, 1, 1)) | |
| end_date = st.sidebar.date_input("End Date", date.today()) | |
| time_increments = ["Daily", "Weekly", "Monthly"] | |
| selected_time_increment = st.sidebar.selectbox("Time Increment", time_increments) | |
| time_increment_mapping = {"Daily": "1d", "Weekly": "1wk", "Monthly": "1mo"} | |
| time_increment_for_yahoo = time_increment_mapping[selected_time_increment] | |
| # ------------------------------------------------- | |
| # Tickers Map | |
| # ------------------------------------------------- | |
| tickers_map = { | |
| "Stocks": ["AAPL", "MSFT", "AMZN", "GOOGL", "NVDA", "TSLA", "META"], | |
| "Forex": ["EURUSD=X", "USDJPY=X", "GBPUSD=X", "AUDUSD=X", "USDCAD=X", "NZDUSD=X", "USDCHF=X"], | |
| "Crypto": ["BTC-USD", "ETH-USD", "USDT-USD", "BNB-USD", "XRP-USD", "ADA-USD", "DOGE-USD"], | |
| "Commodities": ["GLD", "SLV", "USO", "UNG", "PPLT", "COPX", "GDX"], | |
| "Markets": ["^GSPC", "^DJI", "^IXIC", "^FTSE", "^N225", "^HSI", "^GDAXI"], | |
| "ETFs/Mutual Funds": ["SPY", "VTI", "VOO", "QQQ", "ARKK", "IWM", "XLF"] | |
| } | |
| # ------------------------------------------------- | |
| # 1) Fetch Data (matching your snippet) | |
| # ------------------------------------------------- | |
| def fetch_data(tickers, start_date, end_date, time_increment): | |
| """ | |
| Downloads data using yfinance, then resets index and ensures | |
| each of Open, High, Low, Close, Volume is 1D by calling squeeze(). | |
| """ | |
| data = {} | |
| for ticker in tickers: | |
| try: | |
| df = yf.download( | |
| ticker, | |
| start=start_date, | |
| end=end_date, | |
| interval=time_increment, | |
| progress=False | |
| ) | |
| if not df.empty: | |
| df = df.reset_index() | |
| # Flatten columns if necessary | |
| for col in ["Open", "High", "Low", "Close", "Volume"]: | |
| if col in df.columns: | |
| df[col] = df[col].squeeze() # from your working snippet | |
| data[ticker] = df | |
| else: | |
| st.warning(f"No data available for {ticker}.") | |
| except Exception as e: | |
| st.error(f"Error fetching data for {ticker}: {e}") | |
| return data | |
| # ------------------------------------------------- | |
| # 2) Plot Combined Data (matching your snippet) | |
| # ------------------------------------------------- | |
| def plot_combined_data(data, title): | |
| combined_df = pd.DataFrame() | |
| # Gather all unique dates | |
| all_dates = pd.concat([df["Date"] for df in data.values() if not df.empty]).drop_duplicates().sort_values() | |
| combined_df["Date"] = all_dates | |
| # Align "Close" data for each ticker | |
| for ticker, df in data.items(): | |
| if not df.empty: | |
| df = df.set_index("Date").reindex(all_dates).reset_index() | |
| combined_df[ticker] = df["Close"].values.flatten() | |
| # Melt and plot | |
| if not combined_df.empty: | |
| combined_df = combined_df.melt(id_vars=["Date"], var_name="Symbol", value_name="Price") | |
| fig = px.line(combined_df, x="Date", y="Price", color="Symbol", title=title) | |
| return fig | |
| else: | |
| st.warning("No data available for the selected symbols.") | |
| return None | |
| # ------------------------------------------------- | |
| # 3) Plot Individual Data (matching your snippet) | |
| # ------------------------------------------------- | |
| def plot_individual_data(data, ticker): | |
| df = data.get(ticker) | |
| if df is not None and not df.empty: | |
| close_prices = df["Close"].values.flatten() # from your snippet | |
| fig = px.line( | |
| df, | |
| x="Date", | |
| y=close_prices, | |
| title=f"{ticker} Prices", | |
| labels={"x": "Date", "y": "Price"} | |
| ) | |
| return fig | |
| else: | |
| st.warning(f"No data available for {ticker}.") | |
| return None | |
| # ------------------------------------------------- | |
| # 4) Historical Data for Investments (same as before) | |
| # ------------------------------------------------- | |
| def fetch_historical_data(df, default_start_date, default_end_date): | |
| historical_data = {} | |
| for asset in df['Asset']: | |
| try: | |
| hist_df = yf.download(asset, start=default_start_date, end=default_end_date, progress=False) | |
| if not hist_df.empty: | |
| hist_df = hist_df['Close'].rename(asset) | |
| historical_data[asset] = hist_df | |
| else: | |
| st.warning(f"No historical data available for {asset}.") | |
| except Exception as e: | |
| st.error(f"Error fetching historical data for {asset}: {e}") | |
| if historical_data: | |
| portfolio_df = pd.DataFrame(historical_data) | |
| portfolio_df.fillna(method='ffill', inplace=True) | |
| portfolio_df.fillna(method='bfill', inplace=True) | |
| return portfolio_df | |
| else: | |
| return None | |
| # ------------------------------------------------- | |
| # 5) Display Title | |
| # ------------------------------------------------- | |
| def display_title(title_text): | |
| st.markdown( | |
| f"<h2 style='text-align: left; color: white;'>{title_text}</h2>", | |
| unsafe_allow_html=True | |
| ) | |
| # ------------------------------------------------- | |
| # 6) Display Investments Portfolio with Carousel | |
| # ------------------------------------------------- | |
| def display_investments_portfolio(default_start_date, default_end_date): | |
| uploader_col, sample_col = st.columns(2) | |
| with uploader_col: | |
| st.markdown("#### ๐ฅ Upload Your Investment Data") | |
| uploaded_file = st.file_uploader("Upload your investment data (CSV)", type=["csv"]) | |
| st.info("Ensure your CSV contains: Asset, Quantity, Purchase Price, Current Price.") | |
| with sample_col: | |
| st.markdown("#### ๐ Sample Investment Data") | |
| sample_data = { | |
| 'Asset': ['AAPL', 'MSFT', 'BTC-USD', 'EURUSD=X', 'GLD'], | |
| 'Quantity': [10, 5, 0.5, 1000, 2], | |
| 'Purchase Price': [150, 250, 30000, 1.10, 1800], | |
| 'Current Price': [200, 300, 45000, 1.20, 1900] | |
| } | |
| sample_df = pd.DataFrame(sample_data) | |
| sample_df['Total Invested'] = sample_df['Quantity'] * sample_df['Purchase Price'] | |
| sample_df['Current Value'] = sample_df['Quantity'] * sample_df['Current Price'] | |
| sample_df['Profit/Loss'] = sample_df['Current Value'] - sample_df['Total Invested'] | |
| sample_df['Profit/Loss (%)'] = (sample_df['Profit/Loss'] / sample_df['Total Invested']) * 100 | |
| st.dataframe(sample_df) | |
| if uploaded_file is not None: | |
| try: | |
| df = pd.read_csv(uploaded_file) | |
| st.subheader("Uploaded Investment Data") | |
| st.dataframe(df) | |
| required_columns = {'Asset', 'Quantity', 'Purchase Price', 'Current Price'} | |
| if not required_columns.issubset(df.columns): | |
| missing = required_columns - set(df.columns) | |
| st.error(f"Uploaded CSV is missing the following required columns: {', '.join(missing)}") | |
| return | |
| numeric_columns = ['Quantity', 'Purchase Price', 'Current Price'] | |
| for col in numeric_columns: | |
| df[col] = pd.to_numeric(df[col], errors='coerce') | |
| if df[numeric_columns].isnull().any().any(): | |
| st.error("Some entries in your CSV have invalid numerical data. Please correct them and try again.") | |
| return | |
| df['Total Invested'] = df['Quantity'] * df['Purchase Price'] | |
| df['Current Value'] = df['Quantity'] * df['Current Price'] | |
| df['Profit/Loss'] = df['Current Value'] - df['Total Invested'] | |
| df['Profit/Loss (%)'] = (df['Profit/Loss'] / df['Total Invested']) * 100 | |
| st.subheader("Investment Portfolio Summary") | |
| total_invested = df['Total Invested'].sum() | |
| total_current = df['Current Value'].sum() | |
| total_profit_loss = df['Profit/Loss'].sum() | |
| total_profit_loss_pct = (total_profit_loss / total_invested) * 100 | |
| col1, col2, col3, col4 = st.columns(4) | |
| col1.metric("Total Invested", f"${total_invested:,.2f}") | |
| col2.metric("Current Value", f"${total_current:,.2f}") | |
| col3.metric("Total Profit/Loss", f"${total_profit_loss:,.2f}", | |
| delta=f"{total_profit_loss_pct:.2f}%") | |
| col4.metric("Number of Assets", len(df)) | |
| st.subheader("Investment Portfolio Visualizations") | |
| fig1 = px.pie( | |
| df, | |
| names='Asset', | |
| values='Current Value', | |
| title='Portfolio Allocation by Asset', | |
| hole=0.4, | |
| color='Asset', | |
| color_discrete_sequence=px.colors.qualitative.Pastel | |
| ) | |
| fig1.update_traces(textposition='inside', textinfo='percent+label') | |
| fig2 = px.treemap( | |
| df, | |
| path=['Asset'], | |
| values='Current Value', | |
| title='Portfolio Allocation Treemap', | |
| color='Profit/Loss', | |
| color_continuous_scale='RdYlGn' | |
| ) | |
| fig3 = px.scatter( | |
| df, | |
| x='Current Value', | |
| y='Profit/Loss', | |
| text='Asset', | |
| title='Profit/Loss vs. Current Value by Asset', | |
| hover_data=['Quantity', 'Purchase Price'], | |
| color='Profit/Loss', | |
| color_continuous_scale='RdYlGn' | |
| ) | |
| fig3.update_traces(textposition='top center') | |
| fig4 = go.Figure(go.Indicator( | |
| mode="gauge+number", | |
| value=total_profit_loss_pct, | |
| title={'text': "Total Profit/Loss (%)"}, | |
| gauge={ | |
| 'axis': {'range': [-100, 100]}, | |
| 'bar': {'color': "darkblue"}, | |
| 'steps': [ | |
| {'range': [-100, 0], 'color': "red"}, | |
| {'range': [0, 100], 'color': "green"} | |
| ], | |
| 'threshold': { | |
| 'line': {'color': "black", 'width': 4}, | |
| 'thickness': 0.75, | |
| 'value': 0 | |
| } | |
| } | |
| )) | |
| fig5 = px.bar( | |
| df, | |
| x='Asset', | |
| y='Profit/Loss', | |
| color='Profit/Loss', | |
| title='Profit/Loss by Asset', | |
| color_continuous_scale='RdYlGn', | |
| labels={'Profit/Loss': 'Profit/Loss ($)'}, | |
| hover_data=['Quantity', 'Purchase Price', 'Current Price'] | |
| ) | |
| fig5.update_layout(showlegend=False) | |
| # Mock portfolio growth from historical data | |
| historical_df = fetch_historical_data(df, default_start_date, default_end_date) | |
| if historical_df is not None: | |
| weights = df['Current Value'] / df['Current Value'].sum() | |
| portfolio_growth = historical_df.multiply(weights, axis=1).sum(axis=1) | |
| portfolio_growth_df = portfolio_growth.reset_index() | |
| portfolio_growth_df.columns = ['Date', 'Portfolio Value'] | |
| fig6 = px.line( | |
| portfolio_growth_df, | |
| x='Date', | |
| y='Portfolio Value', | |
| title='Portfolio Growth Over Time', | |
| labels={'Portfolio Value': 'Value ($)', 'Date': 'Date'}, | |
| color_discrete_sequence=['#636EFA'] | |
| ) | |
| else: | |
| fig6 = go.Figure() | |
| fig6.add_annotation(text="No historical data available.", x=0.5, y=0.5, showarrow=False) | |
| fig6.update_layout(title='Portfolio Growth Over Time') | |
| carousel_figs = [fig1, fig2, fig3, fig4, fig5, fig6] | |
| prev_col, next_col = st.columns([1, 1]) | |
| with prev_col: | |
| if st.button("โฎ๏ธ Prev", key="prev"): | |
| st.session_state.carousel_slide = (st.session_state.carousel_slide - 1) % len(carousel_figs) | |
| with next_col: | |
| if st.button("โญ๏ธ Next", key="next"): | |
| st.session_state.carousel_slide = (st.session_state.carousel_slide + 1) % len(carousel_figs) | |
| st.plotly_chart(carousel_figs[st.session_state.carousel_slide], use_container_width=True) | |
| st.markdown(f"**Slide {st.session_state.carousel_slide + 1} of {len(carousel_figs)}**") | |
| st.info( | |
| """ | |
| **Navigate Through Visualizations:** | |
| Use the Previous and Next buttons above to browse through different portfolio visualizations. | |
| **Expand Plots:** | |
| Click the full-screen icon on the top-right of each plot to view it in full screen. | |
| """ | |
| ) | |
| except Exception as e: | |
| st.error(f"Error processing the uploaded file: {e}") | |
| else: | |
| st.subheader("Sample Portfolio Visualizations") | |
| sample_data = { | |
| 'Asset': ['AAPL', 'MSFT', 'BTC-USD', 'EURUSD=X', 'GLD'], | |
| 'Quantity': [10, 5, 0.5, 1000, 2], | |
| 'Purchase Price': [150, 250, 30000, 1.10, 1800], | |
| 'Current Price': [200, 300, 45000, 1.20, 1900] | |
| } | |
| sample_df = pd.DataFrame(sample_data) | |
| sample_df['Total Invested'] = sample_df['Quantity'] * sample_df['Purchase Price'] | |
| sample_df['Current Value'] = sample_df['Quantity'] * sample_df['Current Price'] | |
| sample_df['Profit/Loss'] = sample_df['Current Value'] - sample_df['Total Invested'] | |
| sample_df['Profit/Loss (%)'] = (sample_df['Profit/Loss'] / sample_df['Total Invested']) * 100 | |
| fig1 = px.pie( | |
| sample_df, | |
| names='Asset', | |
| values='Current Value', | |
| title='Sample Portfolio Allocation by Asset', | |
| hole=0.4, | |
| color='Asset', | |
| color_discrete_sequence=px.colors.qualitative.Pastel | |
| ) | |
| fig1.update_traces(textposition='inside', textinfo='percent+label') | |
| fig2 = px.treemap( | |
| sample_df, | |
| path=['Asset'], | |
| values='Current Value', | |
| title='Sample Portfolio Allocation Treemap', | |
| color='Profit/Loss', | |
| color_continuous_scale='RdYlGn' | |
| ) | |
| fig3 = px.scatter( | |
| sample_df, | |
| x='Current Value', | |
| y='Profit/Loss', | |
| text='Asset', | |
| title='Sample Profit/Loss vs. Current Value by Asset', | |
| hover_data=['Quantity', 'Purchase Price'], | |
| color='Profit/Loss', | |
| color_continuous_scale='RdYlGn' | |
| ) | |
| fig3.update_traces(textposition='top center') | |
| fig4 = go.Figure(go.Indicator( | |
| mode="gauge+number", | |
| value=sample_df['Profit/Loss (%)'].sum(), | |
| title={'text': "Sample Total Profit/Loss (%)"}, | |
| gauge={ | |
| 'axis': {'range': [-100, 100]}, | |
| 'bar': {'color': "darkblue"}, | |
| 'steps': [ | |
| {'range': [-100, 0], 'color': "red"}, | |
| {'range': [0, 100], 'color': "green"} | |
| ], | |
| 'threshold': { | |
| 'line': {'color': "black", 'width': 4}, | |
| 'thickness': 0.75, | |
| 'value': 0 | |
| } | |
| } | |
| )) | |
| fig5 = px.bar( | |
| sample_df, | |
| x='Asset', | |
| y='Profit/Loss', | |
| color='Profit/Loss', | |
| title='Sample Profit/Loss by Asset', | |
| color_continuous_scale='RdYlGn', | |
| labels={'Profit/Loss': 'Profit/Loss ($)'}, | |
| hover_data=['Quantity', 'Purchase Price', 'Current Price'] | |
| ) | |
| fig5.update_layout(showlegend=False) | |
| # Mock portfolio growth data | |
| sample_growth = pd.DataFrame({ | |
| 'Date': pd.date_range(start=start_date, end=end_date, periods=100), | |
| 'Portfolio Value': (sample_df['Current Value'].sum()) * (1 + 0.01 * np.arange(100)) | |
| }) | |
| fig6 = px.line( | |
| sample_growth, | |
| x='Date', | |
| y='Portfolio Value', | |
| title='Sample Portfolio Growth Over Time', | |
| labels={'Portfolio Value': 'Value ($)', 'Date': 'Date'}, | |
| color_discrete_sequence=['#636EFA'] | |
| ) | |
| carousel_figs = [fig1, fig2, fig3, fig4, fig5, fig6] | |
| left_space, prev_col, next_col, right_space = st.columns([2.5, .5, .5, 2.5]) | |
| with prev_col: | |
| if st.button("โฎ๏ธ Prev", key="prev"): | |
| st.session_state.carousel_slide = ( | |
| st.session_state.carousel_slide - 1 | |
| ) % len(carousel_figs) | |
| with next_col: | |
| if st.button("โญ๏ธ Next", key="next"): | |
| st.session_state.carousel_slide = ( | |
| st.session_state.carousel_slide + 1 | |
| ) % len(carousel_figs) | |
| st.plotly_chart(carousel_figs[st.session_state.carousel_slide], use_container_width=True) | |
| st.markdown(f"**Slide {st.session_state.carousel_slide + 1} of {len(carousel_figs)}**") | |
| st.info( | |
| """ | |
| **Navigate Through Visualizations:** | |
| Use the Previous and Next buttons above to browse through different portfolio visualizations. | |
| **Expand Plots:** | |
| Click the full-screen icon on the top-right of each plot to view it in full screen. | |
| """ | |
| ) | |
| # ------------------------------------------------- | |
| # Main Logic for Each Tab | |
| # ------------------------------------------------- | |
| if st.session_state.selected_tab == "Home": | |
| display_title("Home") | |
| st.title("Financial Dashboard") | |
| st.markdown( | |
| """ | |
| Welcome to the **Financial Dashboard**! This platform provides comprehensive insights into various financial instruments. | |
| ### ๐ Features: | |
| - **Stocks:** Analyze major stock performance. | |
| - **Forex:** Monitor currency exchange rates. | |
| - **Crypto:** Track cryptocurrency trends. | |
| - **Commodities:** Observe commodity market movements. | |
| - **Markets:** Overview of major market indices. | |
| - **ETFs/Mutual Funds:** Evaluate ETF and mutual fund performances. | |
| - **Investments:** Upload and track your personal investment portfolio. | |
| ### ๐ How to Use: | |
| 1. **Navigate:** Use the sidebar buttons to select a financial category. | |
| 2. **Customize:** Choose your desired date range and time increment. | |
| 3. **Visualize:** View combined and individual plots for selected tickers. | |
| 4. **Investments:** Upload your investment CSV file above to track your portfolio. | |
| """ | |
| ) | |
| elif st.session_state.selected_tab in tickers_map: | |
| display_title(st.session_state.selected_tab) | |
| # Let user input custom tickers | |
| st.sidebar.markdown("---") | |
| st.sidebar.markdown(f"#### Enter Custom {st.session_state.selected_tab} Symbols") | |
| default_symbols = ",".join(tickers_map[st.session_state.selected_tab]) | |
| user_input = st.sidebar.text_input( | |
| f"Enter {st.session_state.selected_tab} symbols (comma-separated):", | |
| default_symbols | |
| ) | |
| user_tickers = [ticker.strip().upper() for ticker in user_input.split(",") if ticker.strip()] | |
| if not user_tickers: | |
| st.warning("Please enter at least one ticker symbol.") | |
| else: | |
| data = fetch_data(user_tickers, start_date, end_date, time_increment_for_yahoo) | |
| if data: | |
| tab_names = ["Combined"] + user_tickers | |
| plot_tabs = st.tabs(tab_names) | |
| # Combined Data Tab | |
| with plot_tabs[0]: | |
| fig = plot_combined_data(data, f"{st.session_state.selected_tab} Combined Prices") | |
| if fig: | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Individual Data Tabs | |
| for i, ticker in enumerate(user_tickers): | |
| with plot_tabs[i + 1]: | |
| fig = plot_individual_data(data, ticker) | |
| if fig: | |
| st.plotly_chart(fig, use_container_width=True) | |
| else: | |
| st.warning("No data fetched. Please adjust your ticker symbols or date range.") | |
| else: | |
| # Investments Tab | |
| if st.session_state.selected_tab == "Investments": | |
| display_title("Investments") | |
| st.markdown( | |
| """ | |
| ### ๐ฅ Upload Your Investment Data | |
| Use the uploader above to upload a CSV file containing your investment portfolio details. | |
| **Required Columns:** | |
| - **Asset:** Name or symbol of the investment. | |
| - **Quantity:** Number of units held. | |
| - **Purchase Price:** Price at which each unit was purchased. | |
| - **Current Price:** Current market price of each unit. | |
| """ | |
| ) | |
| display_investments_portfolio(default_start_date=start_date, default_end_date=end_date) |