from stock_data_loader import StockDataLoader import streamlit as st import pandas as pd import yfinance as yf from datetime import datetime, timedelta from plots import Plots, StockChart from utils import ( safe_yfinance_call, get_cached_data, set_cached_data, format_error_message, rate_limiter ) import time import traceback class StockDashboard: def __init__(self): self.tickers = ['NVDA', 'AAPL', 'GOOGL', 'MSFT', 'AMZN'] self.period_map = {'all': 'max','1m': '1mo', '6m': '6mo', '1y': '1y'} def render_sidebar(self): st.sidebar.header("๐Ÿ“Š Choose your filter:") self.ticker = st.sidebar.selectbox('Choose Ticker', options=self.tickers, help='Select a ticker') self.selected_range = st.sidebar.selectbox('Select Period', options=list(self.period_map.keys())) def fetch_stock_data(self, ticker, period): """Fetch stock data with guaranteed sample data fallback""" cache_key = f"stock_data_{ticker}_{period}" # Try to get cached data first cached_data = get_cached_data(cache_key, ttl_seconds=300) # 5 minutes cache if cached_data is not None: st.sidebar.success("๐Ÿ“ฆ Using cached data") return cached_data try: with st.spinner(f'๐Ÿ“ˆ Loading {ticker} data...'): # Since Yahoo Finance is blocked, use sample data directly df_history = safe_yfinance_call(ticker, operation='history', period=period) # This should always return data now (sample data when API fails) if df_history is None or df_history.empty: st.error("โŒ Unable to load any data") return None # All data is currently sample data due to API blocking is_sample_data = True # Try to get company info (also sample data) try: info = safe_yfinance_call(ticker, operation='info') if info and isinstance(info, dict): current_price = info.get('currentPrice', 'N/A') previous_close = info.get('previousClose', 'N/A') company_name = info.get('shortName', ticker) symbol = info.get('symbol', ticker) else: # Fallback to historical data current_price = df_history['Close'].iloc[-1] if not df_history.empty else 'N/A' previous_close = df_history['Close'].iloc[-2] if len(df_history) > 1 else 'N/A' company_name = ticker symbol = ticker except Exception: # Use historical data for price info current_price = df_history['Close'].iloc[-1] if not df_history.empty else 'N/A' previous_close = df_history['Close'].iloc[-2] if len(df_history) > 1 else 'N/A' company_name = ticker symbol = ticker # Package the data data_package = { 'df_history': df_history, 'current_price': current_price, 'previous_close': previous_close, 'company_name': company_name, 'symbol': symbol, 'is_sample_data': is_sample_data } # Cache the data set_cached_data(cache_key, data_package) return data_package except Exception as e: st.error(f"Error loading data: {format_error_message(e)}") return None def load_data(self): """Load stock data with comprehensive error handling""" try: result = self.fetch_stock_data(self.ticker, self.period_map[self.selected_range]) if result is not None: self.df_history = result['df_history'] self.current_price = result['current_price'] self.previous_close = result['previous_close'] self.company_name = result['company_name'] self.symbol = result['symbol'] self.is_sample_data = result.get('is_sample_data', False) self.data_loaded = True else: self.df_history = None self.current_price = 'N/A' self.previous_close = 'N/A' self.company_name = self.ticker self.symbol = self.ticker self.is_sample_data = False self.data_loaded = False except Exception as e: st.error(f"Unexpected error loading data: {format_error_message(e)}") self.data_loaded = False def display_header(self): if not self.data_loaded or self.df_history is None: st.error("Unable to display data due to loading issues.") return try: st.subheader(f'{self.company_name} ({self.symbol}) ๐Ÿ’ฐ') st.divider() # Create columns for metrics col1, col2, col3 = st.columns(3) with col1: if self.current_price != 'N/A' and self.previous_close != 'N/A': try: current_price_float = float(self.current_price) previous_close_float = float(self.previous_close) price_change = current_price_float - previous_close_float price_change_ratio = (abs(price_change) / previous_close_float * 100) price_change_direction = "+" if price_change > 0 else "-" st.metric( label='Current Price', value=f"${current_price_float:.2f}", delta=f"{price_change:.2f} ({price_change_direction}{price_change_ratio:.2f}%)" ) except (ValueError, TypeError): st.metric(label='Current Price', value=str(self.current_price)) else: # Fallback to last available price from historical data if not self.df_history.empty: last_price = self.df_history['Close'].iloc[-1] st.metric(label='Last Available Price', value=f"${last_price:.2f}") else: st.warning("Price information not available") with col2: if not self.df_history.empty: high_52w = self.df_history['High'].max() st.metric(label='52W High', value=f"${high_52w:.2f}") with col3: if not self.df_history.empty: low_52w = self.df_history['Low'].min() st.metric(label='52W Low', value=f"${low_52w:.2f}") except Exception as e: st.error(f"Error displaying header: {str(e)}") def plot_data(self): if not self.data_loaded or self.df_history is None or self.df_history.empty: st.error("No data available to plot.") return try: chart = StockChart(self.df_history) chart.add_price_chart() chart.add_oversold_overbought_lines() chart.add_volume_chart() chart.render_chart() except Exception as e: st.error(f"Error creating chart: {str(e)}") def run(self): st.write("๐Ÿ“Š **Stock Dashboard**") st.write("---") self.render_sidebar() # Add refresh button if st.sidebar.button("๐Ÿ”„ Refresh Data"): # Clear cache for this ticker cache_key = f"stock_data_{self.ticker}_{self.period_map[self.selected_range]}" if cache_key in st.session_state: del st.session_state[cache_key] if f"cache_time_{cache_key}" in st.session_state: del st.session_state[f"cache_time_{cache_key}"] st.sidebar.success("Cache cleared! Please wait...") self.load_data() if self.data_loaded and self.df_history is not None and not self.df_history.empty: # Show data type indicator if hasattr(self, 'is_sample_data') and self.is_sample_data: st.info("โ„น๏ธ **Note**: Displaying sample data due to API limitations. This is for demonstration purposes only.") self.display_header() self.plot_data() # Display data info with st.expander("๐Ÿ“ˆ Data Information"): st.write(f"**Data Points:** {len(self.df_history)}") st.write(f"**Date Range:** {self.df_history.index[0].strftime('%Y-%m-%d')} to {self.df_history.index[-1].strftime('%Y-%m-%d')}") st.write(f"**Period:** {self.selected_range}") if hasattr(self, 'is_sample_data') and self.is_sample_data: st.warning("**โš ๏ธ Sample Data**: This is simulated data for demonstration purposes.") else: st.success("**โœ… Real Data**: This is live market data from Yahoo Finance.") else: st.error("โŒ Unable to load stock data.") st.info(""" ๐Ÿ’ก **Troubleshooting Tips:** - Wait 5-10 minutes if you're seeing rate limit errors - Try a different stock ticker - Use the refresh button to clear cache - Check your internet connection **Sample data is available** for demonstration if real data cannot be fetched. """) # Show fallback message st.markdown(""" ### ๐Ÿšซ Data Loading Issues We're experiencing issues loading fresh data from Yahoo Finance. This can happen due to: 1. **Rate Limiting**: Too many requests in a short time 2. **Network Issues**: Connectivity problems 3. **API Changes**: Yahoo Finance API modifications **What you can do:** - Wait a few minutes and try again - Try a different stock ticker - Sample data will be used automatically when real data is unavailable - Use cached data if available """) # Show cache management in sidebar with st.sidebar: st.markdown("---") if st.button("๐Ÿงน Clear All Cache"): from utils import clear_cache cleared = clear_cache() if cleared > 0: st.success(f"Cleared {cleared} cached items") st.rerun() else: st.info("No cache to clear")