import streamlit as st import pandas as pd import plotly.graph_objects as go import plotly.express as px from datetime import datetime, timedelta from phi.agent.agent import Agent from phi.model.groq import Groq from phi.tools.yfinance import YFinanceTools from phi.tools.duckduckgo import DuckDuckGo from phi.tools.googlesearch import GoogleSearch import yfinance as yf import os from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() # Get API key from environment variables GROQ_API_KEY = os.getenv("GROQ_API_KEY") # Add error handling if not GROQ_API_KEY: st.error("GROQ_API_KEY not found. Please check your .env file.") # Enhanced stock symbol mappings COMMON_STOCKS = { # US Stocks 'NVIDIA': 'NVDA', 'APPLE': 'AAPL', 'GOOGLE': 'GOOGL', 'MICROSOFT': 'MSFT', 'TESLA': 'TSLA', 'AMAZON': 'AMZN', 'META': 'META', 'NETFLIX': 'NFLX', # Indian Stocks - NSE 'TCS': 'TCS.NS', 'RELIANCE': 'RELIANCE.NS', 'INFOSYS': 'INFY.NS', 'WIPRO': 'WIPRO.NS', 'HDFC': 'HDFCBANK.NS', 'TATAMOTORS': 'TATAMOTORS.NS', 'ICICIBANK': 'ICICIBANK.NS', 'SBIN': 'SBIN.NS', 'MARUTI': 'MARUTI.NS', 'BHARTIARTL': 'BHARTIARTL.NS', 'HCLTECH': 'HCLTECH.NS', 'ITC': 'ITC.NS', 'AXISBANK': 'AXISBANK.NS' } # Page configuration st.set_page_config( page_title="Advanced Stock Market Analysis", page_icon="📈", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS with improved styling st.markdown(""" """, unsafe_allow_html=True) # Initialize session state if 'agents_initialized' not in st.session_state: st.session_state.agents_initialized = False st.session_state.watchlist = set() st.session_state.analysis_history = [] st.session_state.last_refresh = None def initialize_agents(): """Initialize all agent instances with improved error handling""" if not st.session_state.agents_initialized: try: st.session_state.web_agent = Agent( name="Web Search Agent", role="Search the web for the information", model=Groq(api_key=GROQ_API_KEY, id="llama-3.3-70b-versatile"), tools=[ GoogleSearch(fixed_language='english', fixed_max_results=5) # DuckDuckGo(fixed_max_results=1) ], instructions=['Always include sources and verification'], show_tool_calls=True, markdown=True ) st.session_state.finance_agent = Agent( name="Financial AI Agent", role="Providing financial insights", model=Groq(api_key=GROQ_API_KEY, id="llama-3.3-70b-versatile"), tools=[ YFinanceTools( stock_price=True, company_news=True, analyst_recommendations=True, historical_prices=True ) ], instructions=["Provide detailed analysis with data visualization"], show_tool_calls=True, markdown=True ) st.session_state.multi_ai_agent = Agent( name='A Stock Market Agent', role='A comprehensive assistant specializing in stock market analysis', model=Groq(api_key=GROQ_API_KEY, id="llama-3.3-70b-versatile"), team=[st.session_state.web_agent, st.session_state.finance_agent], instructions=["Provide comprehensive analysis with multiple data sources"], show_tool_calls=True, markdown=True ) st.session_state.agents_initialized = True return True except Exception as e: st.error(f"Error initializing agents: {str(e)}") return False def get_symbol_from_name(stock_name): """Enhanced function to fetch stock symbol from full stock name""" try: # Clean up input stock_name = stock_name.strip().upper() # First check if it's in our common stocks dictionary if stock_name in COMMON_STOCKS: return COMMON_STOCKS[stock_name] # Check if it's already a valid symbol ticker = yf.Ticker(stock_name) try: info = ticker.info if info and 'symbol' in info: return stock_name except: pass # Try Indian stock market (NSE) try: indian_symbol = f"{stock_name}.NS" ticker = yf.Ticker(indian_symbol) info = ticker.info if info and 'symbol' in info: return indian_symbol except: # Try BSE try: bse_symbol = f"{stock_name}.BO" ticker = yf.Ticker(bse_symbol) info = ticker.info if info and 'symbol' in info: return bse_symbol except: pass st.error(f"Could not find valid symbol for {stock_name}") return None except Exception as e: st.error(f"Error processing {stock_name}: {str(e)}") return None def get_stock_data(symbol, period="1y"): """Enhanced function to fetch stock data with proper cache handling""" try: # Create a new ticker instance stock = yf.Ticker(symbol) # Fetch data with error handling try: info = stock.info if not info: raise ValueError("No data retrieved for symbol") except Exception as info_error: # If .NS suffix is missing for Indian stocks, try adding it if not symbol.endswith('.NS') and not symbol.endswith('.BO'): try: indian_symbol = f"{symbol}.NS" stock = yf.Ticker(indian_symbol) info = stock.info symbol = indian_symbol except: # Try Bombay Stock Exchange try: bse_symbol = f"{symbol}.BO" stock = yf.Ticker(bse_symbol) info = stock.info symbol = bse_symbol except: raise info_error else: raise info_error # Fetch historical data hist = stock.history(period=period, interval="1d", auto_adjust=True) if hist.empty: raise ValueError("No historical data available") return info, hist except Exception as e: st.error(f"Error fetching data for {symbol}: {str(e)}") return None, None def create_price_chart(hist_data, symbol): """Create an interactive price chart using plotly""" fig = go.Figure() # Add candlestick chart fig.add_trace(go.Candlestick( x=hist_data.index, open=hist_data['Open'], high=hist_data['High'], low=hist_data['Low'], close=hist_data['Close'], name='Price' )) # Add moving averages ma20 = hist_data['Close'].rolling(window=20).mean() ma50 = hist_data['Close'].rolling(window=50).mean() fig.add_trace(go.Scatter(x=hist_data.index, y=ma20, name='20 Day MA', line=dict(color='orange'))) fig.add_trace(go.Scatter(x=hist_data.index, y=ma50, name='50 Day MA', line=dict(color='blue'))) fig.update_layout( title=f'{symbol} Stock Price', yaxis_title='Price', template='plotly_white', xaxis_rangeslider_visible=False, height=600 ) return fig def create_volume_chart(hist_data): """Create enhanced volume chart using plotly""" # Calculate volume moving average volume_ma = hist_data['Volume'].rolling(window=20).mean() fig = go.Figure() # Add volume bars fig.add_trace(go.Bar( x=hist_data.index, y=hist_data['Volume'], name='Volume', marker_color='rgba(31, 119, 180, 0.3)' )) # Add volume moving average fig.add_trace(go.Scatter( x=hist_data.index, y=volume_ma, name='20 Day Volume MA', line=dict(color='red') )) fig.update_layout( title='Trading Volume Analysis', yaxis_title='Volume', template='plotly_white', height=400 ) return fig def format_large_number(number): """Format large numbers into readable format""" if number >= 1e12: return f"${number/1e12:.2f}T" elif number >= 1e9: return f"${number/1e9:.2f}B" elif number >= 1e6: return f"${number/1e6:.2f}M" else: return f"${number:,.2f}" def display_metrics(info): """Display enhanced key metrics in a grid""" col1, col2, col3, col4 = st.columns(4) with col1: st.markdown('