File size: 5,590 Bytes
29e42d5
78c1fff
 
6af8332
908b282
72f2309
908b282
47ffeb1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
908b282
47ffeb1
 
908b282
 
72f2309
78c1fff
47ffeb1
78c1fff
 
 
e3ea312
 
 
 
 
 
 
78c1fff
e3ea312
 
 
 
78c1fff
e3ea312
 
 
 
 
78c1fff
e3ea312
 
 
 
78c1fff
72f2309
e3ea312
 
 
 
 
78c1fff
 
 
 
 
 
 
 
 
 
 
 
29e42d5
47ffeb1
 
 
 
 
 
e3ea312
 
47ffeb1
29e42d5
78c1fff
47ffeb1
822643b
47ffeb1
 
 
 
 
 
 
 
 
78c1fff
47ffeb1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78c1fff
47ffeb1
 
 
 
72f2309
47ffeb1
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import streamlit as st
import requests
from bs4 import BeautifulSoup
from transformers import pipeline
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import time

# ----------- Page Layout & Custom Styling -----------
st.set_page_config(page_title="Stock News Sentiment Analysis", layout="centered")

st.markdown("""
    <style>
        .main { background-color: #f9fbfc; }
        .stTextInput>div>div>input {
            font-size: 16px;
            padding: 0.5rem;
        }
        .stButton>button {
            background-color: #4CAF50;
            color: white;
            font-size: 16px;
            padding: 0.5rem 1rem;
            border-radius: 8px;
        }
        .stButton>button:hover {
            background-color: #45a049;
        }
    </style>
""", unsafe_allow_html=True)

# ----------- Model Setup -----------
model_id = "LinkLinkWu/Boss_Stock_News_Analysis"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
sentiment_pipeline = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)

# ----------- Function Definitions -----------
def fetch_news(ticker):
    try:
        url = f"https://finviz.com/quote.ashx?t={ticker}"
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Referer': 'https://finviz.com/',
            'Connection': 'keep-alive',
        }
        response = requests.get(url, headers=headers)
        if response.status_code != 200:
            st.error(f"Failed to fetch page for {ticker}: Status code {response.status_code}")
            return []
        
        soup = BeautifulSoup(response.text, 'html.parser')
        title = soup.title.text if soup.title else ""
        if ticker not in title:
            st.error(f"Page for {ticker} not found or access denied.")
            return []
        
        news_table = soup.find(id='news-table')
        if news_table is None:
            st.error(f"News table not found for {ticker}. The website structure might have changed.")
            return []
        
        news = []
        for row in news_table.findAll('tr')[:50]:  # Fetch up to 50 articles
            a_tag = row.find('a')
            if a_tag:
                title = a_tag.get_text()
                link = a_tag['href']
                news.append({'title': title, 'link': link})
        return news
    except Exception as e:
        st.error(f"Failed to fetch news for {ticker}: {e}")
        return []

def analyze_sentiment(text):
    try:
        result = sentiment_pipeline(text)[0]
        return "Positive" if result['label'] == 'POSITIVE' else "Negative"
    except Exception as e:
        st.error(f"Sentiment analysis failed: {e}")
        return "Unknown"

# ----------- Streamlit UI -----------
st.title("📊 Stock News Sentiment Analysis")
st.markdown("""
This tool parses stock tickers and analyzes the sentiment of related news articles.

💡 *Example input:* `META, NVDA, AAPL, NTES, NCTY`

**Note:** If news fetching fails, it might be due to changes in the Finviz website structure or access restrictions. Please verify the website manually or try again later.
""")

# Input field for stock tickers
tickers_input = st.text_input("Enter stock tickers separated by commas:", "META, NVDA, AAPL, NTES, NCTY")

# Parse and display cleaned tickers in real-time
if tickers_input:
    tickers = [ticker.strip().upper() for ticker in tickers_input.split(",") if ticker.strip()]
    cleaned_input = ", ".join(tickers)
    st.markdown(f"🔎 **Parsed Tickers:** `{cleaned_input}`")
else:
    tickers = []

# Button to trigger sentiment analysis
if st.button("Get News and Sentiment"):
    if not tickers:
        st.warning("Please enter at least one stock ticker.")
    else:
        progress_bar = st.progress(0)
        total_stocks = len(tickers)
        for idx, ticker in enumerate(tickers):
            st.subheader(f"Analyzing {ticker}...")
            news_list = fetch_news(ticker)
            
            if news_list:
                # Analyze sentiment for all news articles (up to 50)
                sentiments = []
                for news in news_list:
                    sentiment = analyze_sentiment(news['title'])
                    sentiments.append(sentiment)
                
                # Determine overall sentiment based on majority
                positive_count = sentiments.count("Positive")
                negative_count = sentiments.count("Negative")
                overall_sentiment = "Positive" if positive_count > negative_count else "Negative"
                
                # Display top 3 news articles with sentiment
                st.write(f"**Top 3 News Articles for {ticker}**")
                for i, news in enumerate(news_list[:3], 1):
                    sentiment = sentiments[i-1]
                    st.markdown(f"{i}. [{news['title']}]({news['link']}) - **{sentiment}**")
                
                # Display overall sentiment
                st.write(f"**Overall Sentiment for {ticker}: {overall_sentiment}**")
            else:
                st.write(f"No news available for {ticker}.")
            
            # Update progress bar
            progress_bar.progress((idx + 1) / total_stocks)
            time.sleep(0.1)  # Simulate processing time