import gradio as gr import yfinance as yf import pandas as pd import numpy as np import matplotlib.pyplot as plt import plotly.graph_objects as go import plotly.express as px from datetime import datetime, timedelta import warnings warnings.filterwarnings('ignore') # Hugging Face imports with error handling try: from transformers import pipeline from sentence_transformers import SentenceTransformer import torch HF_AVAILABLE = True except ImportError: HF_AVAILABLE = False print("Hugging Face libraries not available. Running in lightweight mode.") class FinancialSpacesAgent: """Optimized Financial Agent for Hugging Face Spaces""" def __init__(self): print("🚀 Initializing Financial Analysis Agent for Spaces...") self.models_loaded = False self.company_cache = {} self.load_models_optimized() def load_models_optimized(self): """Load models optimized for Spaces memory constraints""" try: if not HF_AVAILABLE: self.models_loaded = False print("Running in lightweight mode (no AI models)") return # Load lightweight models print("đŸ“Ļ Loading AI models...") # 1. Lightweight sentiment model try: self.sentiment_model = pipeline( "text-classification", model="ProsusAI/finbert", device_map="auto" if torch.cuda.is_available() else "cpu", max_length=128, truncation=True ) print("✅ Financial sentiment model loaded") except: print("âš ī¸ Could not load FinBERT, using smaller model") self.sentiment_model = pipeline( "sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english", device_map="auto" if torch.cuda.is_available() else "cpu" ) # 2. Mini summarizer (skip to save memory) self.summarizer = None print("âš ī¸ Summarization model skipped to save memory") # 3. Lightweight embeddings try: self.embedder = SentenceTransformer('all-MiniLM-L6-v2') print("✅ Embeddings model loaded") except: self.embedder = None # 4. Text generation (skip to save memory) self.text_generator = None self.models_loaded = True print("🎉 All models loaded successfully!") except Exception as e: print(f"âš ī¸ Model loading failed: {e}") print("Running in data-only mode") self.models_loaded = False def fetch_company_data(self, ticker): """Fetch and cache company data""" cache_key = f"{ticker}_{datetime.now().strftime('%Y%m%d')}" if cache_key in self.company_cache: return self.company_cache[cache_key] try: print(f"đŸ“Ĩ Fetching data for {ticker}...") stock = yf.Ticker(ticker) # Basic info with timeout protection info = stock.info # Get essential financials data = { 'ticker': ticker, 'info': info, 'balance_sheet': stock.balance_sheet, 'income_statement': stock.financials, 'cash_flow': stock.cashflow, 'history_1y': stock.history(period="1y"), 'history_5y': stock.history(period="5y"), 'dividends': stock.dividends, 'news': getattr(stock, 'news', [])[:5] # Limit news to save memory } # Calculate key metrics data['metrics'] = self.calculate_key_metrics(data) # Cache for today self.company_cache[cache_key] = data return data except Exception as e: print(f"Error fetching data for {ticker}: {e}") return {'error': str(e), 'ticker': ticker} def calculate_key_metrics(self, data): """Calculate essential financial metrics""" metrics = {} try: info = data.get('info', {}) inc = data.get('income_statement', pd.DataFrame()) bal = data.get('balance_sheet', pd.DataFrame()) if not inc.empty and not bal.empty: # Latest period data if len(inc.columns) > 0: latest_inc = inc.iloc[:, 0] if len(bal.columns) > 0: latest_bal = bal.iloc[:, 0] # Profitability if 'Total Revenue' in latest_inc.index and latest_inc['Total Revenue'] != 0: if 'Net Income' in latest_inc.index: metrics['net_margin'] = (latest_inc['Net Income'] / latest_inc['Total Revenue'] * 100) # ROE if 'Total Stockholder Equity' in latest_bal.index and latest_bal['Total Stockholder Equity'] != 0: if 'Net Income' in latest_inc.index: metrics['roe'] = (latest_inc['Net Income'] / latest_bal['Total Stockholder Equity'] * 100) # Liquidity if 'Total Current Assets' in latest_bal.index and 'Total Current Liabilities' in latest_bal.index: if latest_bal['Total Current Liabilities'] != 0: metrics['current_ratio'] = (latest_bal['Total Current Assets'] / latest_bal['Total Current Liabilities']) # Leverage if 'Total Liab' in latest_bal.index and 'Total Stockholder Equity' in latest_bal.index: if latest_bal['Total Stockholder Equity'] != 0: metrics['debt_to_equity'] = (latest_bal['Total Liab'] / latest_bal['Total Stockholder Equity']) # Market metrics if info: metrics['pe_ratio'] = info.get('trailingPE', 'N/A') metrics['pb_ratio'] = info.get('priceToBook', 'N/A') metrics['dividend_yield'] = info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 0 metrics['market_cap'] = info.get('marketCap', 'N/A') metrics['beta'] = info.get('beta', 'N/A') except Exception as e: print(f"Error calculating metrics: {e}") return metrics def create_dashboard(self, ticker, data): """Create interactive Plotly dashboard""" try: metrics = data.get('metrics', {}) history = data.get('history_1y', pd.DataFrame()) # Create subplots from plotly.subplots import make_subplots fig = make_subplots( rows=2, cols=2, subplot_titles=( '📈 Price Trend', '💰 Key Metrics', '📊 Financial Health', 'đŸŽ¯ Recommendation' ), specs=[ [{"type": "scatter"}, {"type": "bar"}], [{"type": "indicator"}, {"type": "indicator"}] ], vertical_spacing=0.2, horizontal_spacing=0.15 ) # 1. Price Trend if not history.empty: fig.add_trace( go.Scatter( x=history.index, y=history['Close'], mode='lines', name='Price', line=dict(color='#2E86AB', width=2), fill='tozeroy', fillcolor='rgba(46, 134, 171, 0.1)' ), row=1, col=1 ) # Add moving average if len(history) > 20: ma20 = history['Close'].rolling(window=20).mean() fig.add_trace( go.Scatter( x=history.index, y=ma20, mode='lines', name='MA20', line=dict(color='#F18F01', width=1, dash='dash') ), row=1, col=1 ) fig.update_xaxes(title_text="Date", row=1, col=1) fig.update_yaxes(title_text="Price ($)", row=1, col=1) # 2. Key Metrics Bar Chart if metrics: metric_keys = ['roe', 'net_margin', 'current_ratio', 'debt_to_equity'] metric_labels = ['ROE %', 'Net Margin %', 'Current Ratio', 'Debt/Equity'] metric_values = [] metric_colors = [] for key in metric_keys: val = metrics.get(key, 0) if isinstance(val, (int, float)): metric_values.append(val) # Color coding if key == 'roe': metric_colors.append('#2E86AB' if val > 15 else '#A23B72' if val > 8 else '#C73E1D') elif key == 'net_margin': metric_colors.append('#2E86AB' if val > 10 else '#A23B72' if val > 5 else '#C73E1D') elif key == 'current_ratio': metric_colors.append('#2E86AB' if val > 1.5 else '#A23B72' if val > 1 else '#C73E1D') elif key == 'debt_to_equity': metric_colors.append('#2E86AB' if val < 1 else '#A23B72' if val < 2 else '#C73E1D') else: metric_values.append(0) metric_colors.append('#CCCCCC') fig.add_trace( go.Bar( x=metric_labels, y=metric_values, marker_color=metric_colors, text=[f'{v:.1f}' if isinstance(v, (int, float)) else 'N/A' for v in metric_values], textposition='auto', name='Metrics' ), row=1, col=2 ) fig.update_xaxes(title_text="Metrics", row=1, col=2, tickangle=45) fig.update_yaxes(title_text="Value", row=1, col=2) # 3. Financial Health Gauge health_score = self.calculate_health_score(metrics) fig.add_trace( go.Indicator( mode="gauge+number", value=health_score, title={'text': "Financial Health"}, gauge={ 'axis': {'range': [None, 10]}, 'bar': {'color': "darkblue"}, 'steps': [ {'range': [0, 3], 'color': "red"}, {'range': [3, 7], 'color': "yellow"}, {'range': [7, 10], 'color': "green"} ], 'threshold': { 'line': {'color': "black", 'width': 4}, 'thickness': 0.75, 'value': health_score } } ), row=2, col=1 ) # 4. Recommendation Indicator recommendation = self.generate_recommendation(metrics) color_map = { 'Strong Buy': 'green', 'Buy': 'lightgreen', 'Hold': 'yellow', 'Sell': 'orange', 'Strong Sell': 'red' } fig.add_trace( go.Indicator( mode="number", value=health_score, title={'text': f"Recommendation: {recommendation}"}, number={'font': {'color': color_map.get(recommendation, 'black'), 'size': 24}}, domain={'row': 2, 'column': 2} ), row=2, col=2 ) # Update layout fig.update_layout( height=700, showlegend=True, title_text=f"📊 Financial Dashboard - {ticker}", title_font_size=20, template="plotly_white" ) return fig except Exception as e: print(f"Error creating dashboard: {e}") # Return simple figure fig = go.Figure() fig.add_annotation( text="Dashboard generation failed
Try a different ticker", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False, font_size=16, align="center" ) fig.update_layout( title=f"Error for {ticker}", height=400 ) return fig def calculate_health_score(self, metrics): """Calculate financial health score (1-10)""" score = 5.0 # Start at neutral try: # ROE contribution roe = metrics.get('roe', 0) if isinstance(roe, (int, float)): if roe > 20: score += 2 elif roe > 10: score += 1 elif roe < 5: score -= 1 # Net Margin contribution margin = metrics.get('net_margin', 0) if isinstance(margin, (int, float)): if margin > 15: score += 1.5 elif margin > 5: score += 0.5 elif margin < 0: score -= 1.5 # Liquidity contribution current = metrics.get('current_ratio', 0) if isinstance(current, (int, float)): if current > 2: score += 1 elif current < 1: score -= 1 # Leverage contribution debt_eq = metrics.get('debt_to_equity', 0) if isinstance(debt_eq, (int, float)): if debt_eq > 2: score -= 1.5 elif debt_eq < 0.5: score += 0.5 # Cap score between 1 and 10 score = max(1, min(10, score)) except: pass return round(score, 1) def generate_recommendation(self, metrics): """Generate investment recommendation""" score = self.calculate_health_score(metrics) if score >= 8: return "Strong Buy" elif score >= 6: return "Buy" elif score >= 4: return "Hold" elif score >= 2: return "Sell" else: return "Strong Sell" def generate_ai_analysis(self, ticker, data): """Generate AI analysis of company""" try: metrics = data.get('metrics', {}) # Create analysis analysis = f""" ## 🤖 AI Analysis for {ticker} ### 📊 Key Metrics Analysis: """ # Add metric analysis metric_analysis = { 'roe': "Return on Equity measures how effectively management is using shareholders' funds.", 'net_margin': "Net Profit Margin shows what percentage of revenue is actual profit.", 'current_ratio': "Current Ratio indicates short-term liquidity and ability to pay debts.", 'debt_to_equity': "Debt-to-Equity ratio shows the company's financial leverage." } for key, desc in metric_analysis.items(): val = metrics.get(key, 'N/A') if isinstance(val, (int, float)): analysis += f"**{key.replace('_', ' ').title()}**: {val:.1f} - {desc}\n\n" # Add overall assessment health_score = self.calculate_health_score(metrics) recommendation = self.generate_recommendation(metrics) analysis += f""" ### đŸĨ Financial Health Score: **{health_score}/10** ### đŸŽ¯ Recommendation: **{recommendation}** ### 📈 Market Outlook: """ # Add simple outlook based on metrics if health_score >= 7: analysis += "The company shows strong financial fundamentals with good profitability and manageable leverage." elif health_score >= 5: analysis += "The company has average financial health with room for improvement in some areas." else: analysis += "The company shows concerning financial metrics that warrant caution." analysis += "\n\n*Note: This analysis is AI-generated for educational purposes only.*" return analysis except Exception as e: return f"Error in AI analysis: {str(e)}" def create_metrics_table(self, metrics): """Create a nice HTML table for metrics""" if not metrics: return "

No metrics available

" table_html = """ """ for key, value in metrics.items(): if isinstance(value, (int, float)): # Format value if 'margin' in key or 'roe' in key or 'yield' in key: display_value = f"{value:.1f}%" elif 'ratio' in key or 'beta' in key: display_value = f"{value:.2f}" else: display_value = f"{value:,.0f}" if value > 1000 else f"{value:.1f}" # Determine assessment assessment = "" css_class = "" if key == 'roe': if value > 15: assessment = "Excellent" css_class = "good" elif value > 8: assessment = "Good" css_class = "good" elif value > 0: assessment = "Poor" css_class = "warning" else: assessment = "Negative" css_class = "poor" elif key == 'net_margin': if value > 15: assessment = "Excellent" css_class = "good" elif value > 5: assessment = "Good" css_class = "good" elif value > 0: assessment = "Low" css_class = "warning" else: assessment = "Negative" css_class = "poor" elif key == 'current_ratio': if value > 2: assessment = "Very Liquid" css_class = "good" elif value > 1: assessment = "Adequate" css_class = "good" else: assessment = "Risk" css_class = "warning" elif key == 'debt_to_equity': if value < 0.5: assessment = "Low Leverage" css_class = "good" elif value < 1: assessment = "Moderate" elif value < 2: assessment = "High" css_class = "warning" else: assessment = "Very High" css_class = "poor" elif key == 'pe_ratio' and isinstance(value, (int, float)): if value < 15: assessment = "Undervalued" css_class = "good" elif value < 25: assessment = "Fair" else: assessment = "Overvalued" css_class = "warning" elif key == 'dividend_yield' and isinstance(value, (int, float)): if value > 4: assessment = "High Yield" css_class = "good" elif value > 2: assessment = "Moderate" else: assessment = "Low" table_html += f""" """ table_html += "
Metric Value Assessment
{key.replace('_', ' ').title()} {display_value} {assessment}
" return table_html # Initialize agent agent = FinancialSpacesAgent() # Create Gradio interface functions def analyze_company_gradio(ticker): """Main Gradio analysis function""" if not ticker: return "Please enter a ticker symbol", None, "", "" ticker = ticker.upper().strip() # Fetch data data = agent.fetch_company_data(ticker) if 'error' in data: return f"❌ Error: {data['error']}", None, "", "" # Generate analysis analysis = agent.generate_ai_analysis(ticker, data) # Create dashboard dashboard = agent.create_dashboard(ticker, data) # Create metrics table metrics_table = agent.create_metrics_table(data.get('metrics', {})) # Basic info info = data.get('info', {}) basic_info = f""" ### 📋 Company Overview **Name:** {info.get('longName', 'N/A')} **Sector:** {info.get('sector', 'N/A')} **Industry:** {info.get('industry', 'N/A')} **Market Cap:** ${info.get('marketCap', 'N/A'):,} **Current Price:** ${info.get('currentPrice', 'N/A')} **52W High:** ${info.get('fiftyTwoWeekHigh', 'N/A')} **52W Low:** ${info.get('fiftyTwoWeekLow', 'N/A')} **Beta:** {info.get('beta', 'N/A')} """ return analysis, dashboard, metrics_table, basic_info def compare_companies_gradio(ticker1, ticker2): """Compare two companies""" if not ticker1 or not ticker2: return "Please enter both ticker symbols", None data1 = agent.fetch_company_data(ticker1.upper()) data2 = agent.fetch_company_data(ticker2.upper()) if 'error' in data1 or 'error' in data2: return "Error fetching data for one or both companies.", None metrics1 = data1.get('metrics', {}) metrics2 = data2.get('metrics', {}) # Create comparison table comparison_metrics = ['roe', 'net_margin', 'current_ratio', 'debt_to_equity', 'pe_ratio', 'dividend_yield'] comparison_data = [] for metric in comparison_metrics: val1 = metrics1.get(metric, 'N/A') val2 = metrics2.get(metric, 'N/A') comparison_data.append({ 'Metric': metric.replace('_', ' ').title(), ticker1: f"{val1:.2f}" if isinstance(val1, (int, float)) else val1, ticker2: f"{val2:.2f}" if isinstance(val2, (int, float)) else val2 }) # Create comparison chart fig = go.Figure() # Get values for first 4 metrics metric_keys = comparison_metrics[:4] ticker1_vals = [] ticker2_vals = [] for metric in metric_keys: val1 = metrics1.get(metric, 0) val2 = metrics2.get(metric, 0) if isinstance(val1, (int, float)) and isinstance(val2, (int, float)): ticker1_vals.append(val1) ticker2_vals.append(val2) if ticker1_vals and ticker2_vals: x_labels = [m.replace('_', ' ').title() for m in metric_keys[:len(ticker1_vals)]] fig.add_trace(go.Bar( name=ticker1, x=x_labels, y=ticker1_vals, marker_color='#2E86AB' )) fig.add_trace(go.Bar( name=ticker2, x=x_labels, y=ticker2_vals, marker_color='#F18F01' )) fig.update_layout( title=f"{ticker1} vs {ticker2} - Key Metrics Comparison", barmode='group', template="plotly_white", height=400 ) # Create comparison text comparison_text = f"## âš–ī¸ {ticker1} vs {ticker2}\n\n" comparison_text += "| Metric | " + ticker1 + " | " + ticker2 + " |\n" comparison_text += "|--------|" + "-" * (len(ticker1) + 2) + "|" + "-" * (len(ticker2) + 2) + "|\n" for comp in comparison_data: comparison_text += f"| {comp['Metric']} | {comp[ticker1]} | {comp[ticker2]} |\n" return comparison_text, fig # Create Gradio Blocks interface with gr.Blocks( title="Financial Analysis AI Agent", theme=gr.themes.Soft(primary_hue="blue"), css=""" .gradio-container {max-width: 1200px !important; margin: auto !important;} .metric-box {background: #f5f5f5; padding: 10px; border-radius: 5px; margin: 5px;} """ ) as demo: gr.Markdown(""" # 🤖 Financial Analysis AI Agent ### Powered by Hugging Face & Yahoo Finance Analyze any company's financial health using AI-powered insights. """) with gr.Tabs(): with gr.TabItem("📈 Company Analysis"): with gr.Row(): with gr.Column(scale=1): ticker_input = gr.Textbox( label="Stock Ticker", placeholder="AAPL, TSLA, GOOGL, etc.", value="AAPL" ) analyze_btn = gr.Button( "🚀 Analyze Company", variant="primary", size="lg" ) gr.Markdown("### 📊 Popular Companies") with gr.Row(): popular_tickers = ["AAPL", "MSFT", "GOOGL", "TSLA"] for t in popular_tickers: btn = gr.Button(t, size="sm") # FIXED: Only update the ticker input, not other outputs btn.click( lambda t=t: t, inputs=None, outputs=ticker_input ) with gr.Column(scale=2): basic_info = gr.Markdown(label="Company Overview") with gr.Row(): with gr.Column(scale=2): plot_output = gr.Plot(label="Financial Dashboard") with gr.Column(scale=1): metrics_output = gr.HTML(label="Key Metrics") with gr.Row(): analysis_output = gr.Markdown(label="AI Analysis Report") with gr.TabItem("âš–ī¸ Compare Companies"): with gr.Row(): with gr.Column(scale=1): ticker1_input = gr.Textbox(label="First Company", placeholder="AAPL", value="AAPL") ticker2_input = gr.Textbox(label="Second Company", placeholder="MSFT", value="MSFT") compare_btn = gr.Button("âš–ī¸ Compare", variant="primary") with gr.Column(scale=2): comparison_plot = gr.Plot(label="Comparison Chart") comparison_output = gr.Markdown(label="Comparison Results") with gr.TabItem("📚 How to Use"): gr.Markdown(""" ## 📖 User Guide ### 🔍 **Company Analysis Tab** 1. Enter a stock ticker symbol (e.g., AAPL for Apple) 2. Click "Analyze Company" or press Enter 3. View the financial dashboard with interactive charts 4. Read the AI-powered analysis ### âš–ī¸ **Compare Companies Tab** 1. Enter two company tickers 2. Click "Compare" 3. View side-by-side financial metrics ### 💡 **Tips for Best Results** - Use valid stock ticker symbols - For international stocks, add exchange suffix (e.g., TSLA for Tesla) - The AI analyzes available financial data from Yahoo Finance ### âš ī¸ **Disclaimer** - This tool is for **educational purposes only** - Not financial advice - Always do your own research before investing - Data may have delays or inaccuracies ### đŸ› ī¸ **Technical Details** - **Data Source**: Yahoo Finance (free API) - **AI Models**: Hugging Face transformers - **Visualization**: Plotly interactive charts - **Hosting**: Hugging Face Spaces ### 🐛 **Troubleshooting** If a company isn't found: 1. Check the ticker symbol is correct 2. Try adding exchange suffix (e.g., .NS for NSE India) 3. Some companies may not have complete financial data --- *Made with â¤ī¸ for the financial analysis community* """) with gr.TabItem("â„šī¸ About"): gr.Markdown(f""" ## About This App ### 🤖 **AI-Powered Financial Analysis** This application uses state-of-the-art AI models from Hugging Face to analyze company financials. ### 📊 **Features Included:** - **Real-time Financial Data** from Yahoo Finance - **AI Analysis** using financial NLP models - **Interactive Dashboards** with Plotly - **Company Comparison** tools - **Financial Health Scoring** ### đŸ—ī¸ **Technology Stack:** - **Backend**: Python, Hugging Face Transformers - **Frontend**: Gradio for interactive UI - **Data**: Yahoo Finance API - **Visualization**: Plotly, Matplotlib ### 🚀 **Model Status:** {'✅ Loaded' if agent.models_loaded else 'âš ī¸ Limited Mode'} ### 📈 **Data Coverage:** - US Stocks: Full coverage - International Stocks: Limited (depends on Yahoo Finance) - Cryptocurrencies: Supported (e.g., BTC-USD) ### âš ī¸ **Limitations:** - Free tier has rate limits - Some companies may have incomplete data - AI analysis is probabilistic, not deterministic ### 👨‍đŸ’ģ **Open Source** This project is open source. Contributions welcome! ### 📞 **Support** For issues or feature requests, please open an issue on the GitHub repository. --- **Version 1.0** | **Last Updated**: {datetime.now().strftime('%Y-%m-%d')} """) # Connect events analyze_btn.click( analyze_company_gradio, inputs=ticker_input, outputs=[analysis_output, plot_output, metrics_output, basic_info] ) ticker_input.submit( analyze_company_gradio, inputs=ticker_input, outputs=[analysis_output, plot_output, metrics_output, basic_info] ) compare_btn.click( compare_companies_gradio, inputs=[ticker1_input, ticker2_input], outputs=[comparison_output, comparison_plot] ) # Auto-load example on startup demo.load( lambda: analyze_company_gradio("AAPL"), inputs=None, outputs=[analysis_output, plot_output, metrics_output, basic_info] ) # For Hugging Face Spaces if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, share=False, show_error=True, debug=False # Disable debug mode for production )