LinkLinkWu commited on
Commit
0228682
·
verified ·
1 Parent(s): 0f5dc9c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -0
app.py CHANGED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
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 -----------
9
+ st.set_page_config(page_title="Stock News Sentiment Analysis", layout="centered")
10
+
11
+ st.markdown("""
12
+ <style>
13
+ .main { background-color: #f9fbfc; }
14
+ .stTextInput>div>div>input, .stTextArea textarea {
15
+ font-size: 16px;
16
+ padding: 0.5rem;
17
+ }
18
+ .stButton>button {
19
+ background-color: #4CAF50;
20
+ color: white;
21
+ font-size: 16px;
22
+ padding: 0.5rem 1rem;
23
+ border-radius: 8px;
24
+ }
25
+ .stButton>button:hover {
26
+ background-color: #45a049;
27
+ }
28
+ </style>
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',
51
+ }
52
+ response = requests.get(url, headers=headers)
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()
73
+ link = a_tag['href']
74
+ news.append({'title': title, 'link': link})
75
+ return news
76
+ except Exception as e:
77
+ st.error(f"Failed to fetch news for {ticker}: {e}")
78
+ return []
79
+
80
+ def analyze_sentiment(text):
81
+ try:
82
+ result = sentiment_pipeline(text)[0]
83
+ return "Positive" if result['label'] == 'POSITIVE' else "Negative"
84
+ except Exception as e:
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 analyzes the sentiment of news articles related to companies you mention in text.
108
+ \U0001F4A1 *Try input like:* `I want to check Apple, Tesla, and Microsoft.`
109
+ **Note:** If news fetching fails, it may be due to changes in the Finviz website or access restrictions.
110
+ """)
111
+
112
+ free_text = st.text_area("Enter text mentioning companies:", height=100)
113
+ tickers = extract_org_entities(free_text)
114
+
115
+ if tickers:
116
+ cleaned_input = ", ".join(tickers)
117
+ st.markdown(f"\U0001F50E **Identified Tickers:** `{cleaned_input}`")
118
+ else:
119
+ tickers = []
120
+
121
+ if st.button("Get News and Sentiment"):
122
+ if not tickers:
123
+ st.warning("Please mention at least one recognizable company.")
124
+ else:
125
+ progress_bar = st.progress(0)
126
+ total_stocks = len(tickers)
127
+ for idx, ticker in enumerate(tickers):
128
+ st.subheader(f"Analyzing {ticker}...")
129
+ news_list = fetch_news(ticker)
130
+
131
+ if news_list:
132
+ sentiments = []
133
+ for news in news_list:
134
+ sentiment = analyze_sentiment(news['title'])
135
+ sentiments.append(sentiment)
136
+
137
+ positive_count = sentiments.count("Positive")
138
+ negative_count = sentiments.count("Negative")
139
+ total = len(sentiments)
140
+ positive_ratio = positive_count / total if total else 0
141
+ negative_ratio = negative_count / total if total else 0
142
+
143
+ if positive_ratio >= 0.4:
144
+ overall_sentiment = "Positive"
145
+ elif negative_ratio >= 0.6:
146
+ overall_sentiment = "Negative"
147
+ else:
148
+ overall_sentiment = "Neutral"
149
+
150
+ st.write(f"**Top 3 News Articles for {ticker}**")
151
+ for i, news in enumerate(news_list[:3], 1):
152
+ sentiment = sentiments[i-1]
153
+ st.markdown(f"{i}. [{news['title']}]({news['link']}) - **{sentiment}**")
154
+
155
+ if overall_sentiment != "Undecided":
156
+ st.write(f"**Overall Sentiment for {ticker}: {overall_sentiment}**")
157
+ else:
158
+ st.write("**No clear sentiment (does not meet threshold conditions).**")
159
+ else:
160
+ st.write(f"No news available for {ticker}.")
161
+
162
+ progress_bar.progress((idx + 1) / total_stocks)
163
+ time.sleep(0.1)