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(""" """, 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