| |
| """ |
| 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 |
|
|
| |
| 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: |
| |
| articles = scraper.get_stock_news(symbol, num_articles) |
| |
| if not articles: |
| return "No news found for this symbol.", None, None, "No articles to analyze." |
| |
| |
| sentiments = [] |
| for article in articles: |
| text = f"{article['title']}. {article.get('summary', '')}" |
| sentiment = sentiment_analyzer.analyze_comprehensive(text) |
| sentiments.append(sentiment) |
| |
| |
| market_summary = llm_analyzer.summarize_news(articles) |
| investment_insight = llm_analyzer.generate_investment_insight(symbol, articles, sentiments) |
| |
| |
| 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) |
| |
| |
| 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 |
| ) |
| |
| |
| 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) |
| |
| |
| 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)}" |
|
|
|
|
| |
| 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(): |
| |
| 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") |
| |
| |
| analyze_btn.click( |
| fn=analyze_stock_news, |
| inputs=[stock_input, num_articles], |
| outputs=[summary_output, table_output, chart_output, insights_output] |
| ) |
| |
| |
| for btn in quick_buttons: |
| btn.click( |
| fn=lambda x: x, |
| inputs=[btn], |
| outputs=[stock_input] |
| ) |
| |
| |
| 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] |
| ) |
| |
| |
| 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** |
| """) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch() |
|
|
| |