# app.py """ Yahoo Finance Sentiment Analysis with Gemma LLM Hugging Face Space Application """ import gradio as gr import pandas as pd from datetime import datetime from utils import YahooFinanceScraper, SentimentAnalyzer, LLMAnalyzer from config import POPULAR_STOCKS import plotly.graph_objects as go # Initialize components print("Initializing application...") scraper = YahooFinanceScraper() sentiment_analyzer = SentimentAnalyzer() llm_analyzer = LLMAnalyzer() print("Application ready!") def analyze_stock_news(symbol: str, num_articles: int = 10): """ Main function to analyze stock news Args: symbol: Stock ticker symbol num_articles: Number of articles to analyze Returns: Tuple of (summary, dataframe, chart, llm_insights) """ try: # Fetch news articles = scraper.get_stock_news(symbol, num_articles) if not articles: return "No news found for this symbol.", None, None, "No articles to analyze." # Analyze sentiments sentiments = [] for article in articles: text = f"{article['title']}. {article.get('summary', '')}" sentiment = sentiment_analyzer.analyze_comprehensive(text) sentiments.append(sentiment) # Generate LLM insights market_summary = llm_analyzer.summarize_news(articles) investment_insight = llm_analyzer.generate_investment_insight(symbol, articles, sentiments) # Create dataframe df_data = [] for article, sentiment in zip(articles, sentiments): df_data.append({ 'Title': article['title'], 'Publisher': article['publisher'], 'Sentiment': sentiment['sentiment_label'], 'Score': f"{sentiment['combined_score']:.3f}", 'Confidence': f"{sentiment['confidence']:.2%}", 'VADER': f"{sentiment['vader']['compound']:.3f}", 'FinBERT +': f"{sentiment['finbert']['positive']:.3f}", 'FinBERT -': f"{sentiment['finbert']['negative']:.3f}", }) df = pd.DataFrame(df_data) # Create visualization sentiment_counts = df['Sentiment'].value_counts() fig = go.Figure(data=[ go.Bar( x=sentiment_counts.index, y=sentiment_counts.values, marker_color=['#00cc66' if x=='Positive' else '#ff6666' if x=='Negative' else '#999999' for x in sentiment_counts.index] ) ]) fig.update_layout( title=f"Sentiment Distribution for {symbol}", xaxis_title="Sentiment", yaxis_title="Number of Articles", height=400 ) # Calculate statistics avg_score = sum(s['combined_score'] for s in sentiments) / len(sentiments) positive_pct = (sentiment_counts.get('Positive', 0) / len(sentiments)) * 100 negative_pct = (sentiment_counts.get('Negative', 0) / len(sentiments)) * 100 summary = f""" ## 📊 Analysis Summary for {symbol} **Total Articles Analyzed:** {len(articles)} **Sentiment Distribution:** - đŸŸĸ Positive: {sentiment_counts.get('Positive', 0)} ({positive_pct:.1f}%) - 🔴 Negative: {sentiment_counts.get('Negative', 0)} ({negative_pct:.1f}%) - âšĒ Neutral: {sentiment_counts.get('Neutral', 0)} ({100-positive_pct-negative_pct:.1f}%) **Average Sentiment Score:** {avg_score:.3f} **Overall Sentiment:** {"đŸŸĸ Positive" if avg_score > 0.05 else "🔴 Negative" if avg_score < -0.05 else "âšĒ Neutral"} """ llm_insights = f""" ## 🤖 AI-Generated Insights (Powered by Gemma) ### Market Summary: {market_summary} ### Investment Perspective: {investment_insight} --- *Note: These insights are generated by AI and should not be considered as financial advice.* """ return summary, df, fig, llm_insights except Exception as e: return f"Error: {str(e)}", None, None, "Error generating insights." def analyze_single_headline(headline: str): """ Analyze a single headline Args: headline: News headline text Returns: Analysis results """ try: sentiment = sentiment_analyzer.analyze_comprehensive(headline) # Create a dummy article dict for LLM analysis article = {'title': headline, 'summary': ''} explanation = llm_analyzer.analyze_sentiment_context(article, sentiment) result = f""" ## Sentiment Analysis Results **Headline:** {headline} **Overall Sentiment:** {sentiment['sentiment_label']} (Score: {sentiment['combined_score']:.3f}) **Confidence:** {sentiment['confidence']:.2%} ### Detailed Scores: - **VADER Compound:** {sentiment['vader']['compound']:.3f} - **FinBERT Positive:** {sentiment['finbert']['positive']:.3%} - **FinBERT Negative:** {sentiment['finbert']['negative']:.3%} - **FinBERT Neutral:** {sentiment['finbert']['neutral']:.3%} ### AI Explanation: {explanation} """ return result except Exception as e: return f"Error analyzing headline: {str(e)}" # Create Gradio Interface with gr.Blocks(title="Yahoo Finance Sentiment Analyzer", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 📈 Yahoo Finance Sentiment Analyzer ### Powered by FinBERT + Gemma LLM Analyze market sentiment from Yahoo Finance news using advanced NLP and AI. """) with gr.Tabs(): # Tab 1: Stock Analysis with gr.Tab("📊 Stock Sentiment Analysis"): gr.Markdown("### Analyze sentiment of news for any stock symbol") with gr.Row(): with gr.Column(scale=2): stock_input = gr.Textbox( label="Stock Symbol", placeholder="e.g., AAPL, GOOGL, TSLA", value="AAPL" ) with gr.Column(scale=1): num_articles = gr.Slider( minimum=5, maximum=20, value=10, step=1, label="Number of Articles" ) gr.Markdown("**Quick Select:**") quick_buttons = [] with gr.Row(): for stock in POPULAR_STOCKS[:5]: btn = gr.Button(stock, size="sm") quick_buttons.append(btn) with gr.Row(): for stock in POPULAR_STOCKS[5:10]: btn = gr.Button(stock, size="sm") quick_buttons.append(btn) analyze_btn = gr.Button("🔍 Analyze News", variant="primary", size="lg") summary_output = gr.Markdown(label="Summary") insights_output = gr.Markdown(label="AI Insights") chart_output = gr.Plot(label="Sentiment Distribution") table_output = gr.Dataframe(label="Detailed Results") # Button actions analyze_btn.click( fn=analyze_stock_news, inputs=[stock_input, num_articles], outputs=[summary_output, table_output, chart_output, insights_output] ) # Quick select buttons for btn in quick_buttons: btn.click( fn=lambda x: x, inputs=[btn], outputs=[stock_input] ) # Tab 2: Single Headline Analysis with gr.Tab("📰 Single Headline Analyzer"): gr.Markdown("### Analyze sentiment of a single news headline") headline_input = gr.Textbox( label="News Headline", placeholder="Enter a financial news headline...", lines=3 ) gr.Markdown("**Example Headlines:**") example_headlines = [ "Apple reaches all-time high as iPhone sales surge", "Tesla stock plummets amid production concerns", "Fed maintains interest rates, markets remain stable" ] with gr.Row(): for example in example_headlines: gr.Button(example[:50] + "...", size="sm").click( fn=lambda x: x, inputs=[gr.Textbox(value=example, visible=False)], outputs=[headline_input] ) analyze_headline_btn = gr.Button("🔍 Analyze Headline", variant="primary") headline_output = gr.Markdown(label="Analysis Results") analyze_headline_btn.click( fn=analyze_single_headline, inputs=[headline_input], outputs=[headline_output] ) # Tab 3: About with gr.Tab("â„šī¸ About"): gr.Markdown(""" ## About This Application This application analyzes sentiment from Yahoo Finance news using multiple advanced techniques: ### đŸ› ī¸ Technologies Used: 1. **VADER Sentiment Analysis** - Rule-based sentiment analysis - Good for general text sentiment 2. **FinBERT** - BERT model fine-tuned for financial text - Specialized in financial sentiment analysis - Model: `ProsusAI/finbert` 3. **Gemma LLM** - Google's Gemma language model - Generates human-like insights and summaries - Model: `google/gemma-2-2b-it` ### 📊 Features: - Real-time news scraping from Yahoo Finance - Multi-model sentiment analysis - AI-generated market insights - Interactive visualizations - Batch and single headline analysis ### 📝 Sentiment Scores: - **Positive**: Score > 0.05 - **Negative**: Score < -0.05 - **Neutral**: -0.05 ≤ Score ≤ 0.05 ### âš ī¸ Disclaimer: This tool is for educational and research purposes only. The sentiment analysis and AI-generated insights should NOT be used as financial advice. Always do your own research and consult with financial professionals before making investment decisions. --- **Created with â¤ī¸ using Hugging Face Spaces** """) # Launch the app if __name__ == "__main__": demo.launch()