LinkLinkWu commited on
Commit
99b85b9
·
verified ·
1 Parent(s): eb4439d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -37
app.py CHANGED
@@ -2,7 +2,7 @@ import streamlit as st
2
  import requests
3
  from bs4 import BeautifulSoup
4
  from transformers import pipeline
5
- from transformers import AutoTokenizer, AutoModelForSequenceClassification
6
  import time
7
 
8
  # ----------- Page Layout & Custom Styling -----------
@@ -29,18 +29,22 @@ st.markdown("""
29
  """, unsafe_allow_html=True)
30
 
31
  # ----------- Model Setup -----------
32
- model_id = "LinkLinkWu/Boss_Stock_News_Analysis"
33
- tokenizer = AutoTokenizer.from_pretrained(model_id)
34
- model = AutoModelForSequenceClassification.from_pretrained(model_id)
35
- sentiment_pipeline = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
36
 
37
- # ----------- Function Definitions -----------
 
 
 
 
38
  def fetch_news(ticker):
39
  try:
40
  url = f"https://finviz.com/quote.ashx?t={ticker}"
41
  headers = {
42
- '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',
43
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
44
  'Accept-Language': 'en-US,en;q=0.5',
45
  'Referer': 'https://finviz.com/',
46
  'Connection': 'keep-alive',
@@ -49,20 +53,20 @@ def fetch_news(ticker):
49
  if response.status_code != 200:
50
  st.error(f"Failed to fetch page for {ticker}: Status code {response.status_code}")
51
  return []
52
-
53
  soup = BeautifulSoup(response.text, 'html.parser')
54
  title = soup.title.text if soup.title else ""
55
  if ticker not in title:
56
  st.error(f"Page for {ticker} not found or access denied.")
57
  return []
58
-
59
  news_table = soup.find(id='news-table')
60
  if news_table is None:
61
  st.error(f"News table not found for {ticker}. The website structure might have changed.")
62
  return []
63
-
64
  news = []
65
- for row in news_table.findAll('tr')[:50]: # Fetch up to 50 articles
66
  a_tag = row.find('a')
67
  if a_tag:
68
  title = a_tag.get_text()
@@ -81,28 +85,45 @@ def analyze_sentiment(text):
81
  st.error(f"Sentiment analysis failed: {e}")
82
  return "Unknown"
83
 
84
- # ----------- Streamlit UI -----------
85
- st.title("📊 Stock News Sentiment Analysis")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  st.markdown("""
87
  This tool parses stock tickers and analyzes the sentiment of related news articles.
88
-
89
- 💡 *Example input:* `META, NVDA, AAPL, NTES, NCTY`
90
-
91
  **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.
92
  """)
93
 
94
- # Input field for stock tickers
95
- tickers_input = st.text_input("Enter stock tickers separated by commas:", "META, NVDA, AAPL, NTES, NCTY")
96
 
97
- # Parse and display cleaned tickers in real-time
98
- if tickers_input:
99
  tickers = [ticker.strip().upper() for ticker in tickers_input.split(",") if ticker.strip()]
 
 
 
 
 
100
  cleaned_input = ", ".join(tickers)
101
- st.markdown(f"🔎 **Parsed Tickers:** `{cleaned_input}`")
102
  else:
103
  tickers = []
104
 
105
- # Button to trigger sentiment analysis
106
  if st.button("Get News and Sentiment"):
107
  if not tickers:
108
  st.warning("Please enter at least one stock ticker.")
@@ -112,36 +133,34 @@ if st.button("Get News and Sentiment"):
112
  for idx, ticker in enumerate(tickers):
113
  st.subheader(f"Analyzing {ticker}...")
114
  news_list = fetch_news(ticker)
115
-
116
  if news_list:
117
- # Analyze sentiment for all news articles (up to 50)
118
  sentiments = []
119
  for news in news_list:
120
  sentiment = analyze_sentiment(news['title'])
121
  sentiments.append(sentiment)
122
-
123
- # Determine overall sentiment based on majority
124
  positive_count = sentiments.count("Positive")
125
  negative_count = sentiments.count("Negative")
126
  total = len(sentiments)
127
  positive_ratio = positive_count / total if total else 0
128
  negative_ratio = negative_count / total if total else 0
 
129
  if positive_ratio >= 0.4:
130
- overall_sentiment = "Positive"
131
- else negative_ratio >= 0.6:
132
- overall_sentiment = "Negative"
133
-
134
- # Display top 3 news articles with sentiment
 
135
  st.write(f"**Top 3 News Articles for {ticker}**")
136
  for i, news in enumerate(news_list[:3], 1):
137
  sentiment = sentiments[i-1]
138
  st.markdown(f"{i}. [{news['title']}]({news['link']}) - **{sentiment}**")
139
-
140
- # Display overall sentiment
141
  st.write(f"**Overall Sentiment for {ticker}: {overall_sentiment}**")
142
  else:
143
  st.write(f"No news available for {ticker}.")
144
-
145
- # Update progress bar
146
  progress_bar.progress((idx + 1) / total_stocks)
147
- time.sleep(0.1) # Simulate processing time
 
2
  import requests
3
  from bs4 import BeautifulSoup
4
  from transformers import pipeline
5
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification, AutoModelForTokenClassification
6
  import time
7
 
8
  # ----------- Page Layout & Custom Styling -----------
 
29
  """, unsafe_allow_html=True)
30
 
31
  # ----------- Model Setup -----------
32
+ sentiment_model_id = "LinkLinkWu/Boss_Stock_News_Analysis"
33
+ sentiment_tokenizer = AutoTokenizer.from_pretrained(sentiment_model_id)
34
+ sentiment_model = AutoModelForSequenceClassification.from_pretrained(sentiment_model_id)
35
+ sentiment_pipeline = pipeline("sentiment-analysis", model=sentiment_model, tokenizer=sentiment_tokenizer)
36
 
37
+ ner_tokenizer = AutoTokenizer.from_pretrained("dslim/bert-base-NER")
38
+ ner_model = AutoModelForTokenClassification.from_pretrained("dslim/bert-base-NER")
39
+ ner_pipeline = pipeline("ner", model=ner_model, tokenizer=ner_tokenizer, grouped_entities=True)
40
+
41
+ # ----------- Functions -----------
42
  def fetch_news(ticker):
43
  try:
44
  url = f"https://finviz.com/quote.ashx?t={ticker}"
45
  headers = {
46
+ 'User-Agent': 'Mozilla/5.0',
47
+ 'Accept': 'text/html',
48
  'Accept-Language': 'en-US,en;q=0.5',
49
  'Referer': 'https://finviz.com/',
50
  'Connection': 'keep-alive',
 
53
  if response.status_code != 200:
54
  st.error(f"Failed to fetch page for {ticker}: Status code {response.status_code}")
55
  return []
56
+
57
  soup = BeautifulSoup(response.text, 'html.parser')
58
  title = soup.title.text if soup.title else ""
59
  if ticker not in title:
60
  st.error(f"Page for {ticker} not found or access denied.")
61
  return []
62
+
63
  news_table = soup.find(id='news-table')
64
  if news_table is None:
65
  st.error(f"News table not found for {ticker}. The website structure might have changed.")
66
  return []
67
+
68
  news = []
69
+ for row in news_table.findAll('tr')[:50]:
70
  a_tag = row.find('a')
71
  if a_tag:
72
  title = a_tag.get_text()
 
85
  st.error(f"Sentiment analysis failed: {e}")
86
  return "Unknown"
87
 
88
+ def extract_org_entities(text):
89
+ try:
90
+ entities = ner_pipeline(text)
91
+ org_entities = []
92
+ for ent in entities:
93
+ if ent["entity_group"] == "ORG":
94
+ clean_word = ent["word"].replace("##", "").strip()
95
+ if clean_word.upper() not in org_entities:
96
+ org_entities.append(clean_word.upper())
97
+ if len(org_entities) >= 5:
98
+ break
99
+ return org_entities
100
+ except Exception as e:
101
+ st.error(f"NER entity extraction failed: {e}")
102
+ return []
103
+
104
+ # ----------- UI -----------
105
+ st.title("\U0001F4CA Stock News Sentiment Analysis")
106
  st.markdown("""
107
  This tool parses stock tickers and analyzes the sentiment of related news articles.
108
+ \U0001F4A1 *Example input:* `META, NVDA, AAPL, NTES, NCTY`
 
 
109
  **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.
110
  """)
111
 
112
+ input_mode = st.radio("Choose input method:", ("Text (auto detect)", "Manual tickers"))
 
113
 
114
+ if input_mode == "Manual tickers":
115
+ tickers_input = st.text_input("Enter stock tickers separated by commas:", "META, NVDA, AAPL")
116
  tickers = [ticker.strip().upper() for ticker in tickers_input.split(",") if ticker.strip()]
117
+ else:
118
+ free_text = st.text_area("Enter text mentioning companies:", height=100)
119
+ tickers = extract_org_entities(free_text)
120
+
121
+ if tickers:
122
  cleaned_input = ", ".join(tickers)
123
+ st.markdown(f"\U0001F50E **Parsed Tickers:** `{cleaned_input}`")
124
  else:
125
  tickers = []
126
 
 
127
  if st.button("Get News and Sentiment"):
128
  if not tickers:
129
  st.warning("Please enter at least one stock ticker.")
 
133
  for idx, ticker in enumerate(tickers):
134
  st.subheader(f"Analyzing {ticker}...")
135
  news_list = fetch_news(ticker)
136
+
137
  if news_list:
 
138
  sentiments = []
139
  for news in news_list:
140
  sentiment = analyze_sentiment(news['title'])
141
  sentiments.append(sentiment)
142
+
 
143
  positive_count = sentiments.count("Positive")
144
  negative_count = sentiments.count("Negative")
145
  total = len(sentiments)
146
  positive_ratio = positive_count / total if total else 0
147
  negative_ratio = negative_count / total if total else 0
148
+
149
  if positive_ratio >= 0.4:
150
+ overall_sentiment = "Positive"
151
+ elif negative_ratio >= 0.6:
152
+ overall_sentiment = "Negative"
153
+ else:
154
+ overall_sentiment = "Neutral"
155
+
156
  st.write(f"**Top 3 News Articles for {ticker}**")
157
  for i, news in enumerate(news_list[:3], 1):
158
  sentiment = sentiments[i-1]
159
  st.markdown(f"{i}. [{news['title']}]({news['link']}) - **{sentiment}**")
160
+
 
161
  st.write(f"**Overall Sentiment for {ticker}: {overall_sentiment}**")
162
  else:
163
  st.write(f"No news available for {ticker}.")
164
+
 
165
  progress_bar.progress((idx + 1) / total_stocks)
166
+ time.sleep(0.1)