Spaces:
Running
Running
Akshayram1
commited on
Commit
•
a7d0af8
1
Parent(s):
1d4fc4e
Update app.py
Browse files
app.py
CHANGED
@@ -10,8 +10,8 @@ from transformers import BertTokenizer, BertForSequenceClassification, pipeline
|
|
10 |
|
11 |
st.set_page_config(page_title="Stock News Sentiment Analyzer", layout="wide")
|
12 |
|
13 |
-
# Initialize FinBERT (yiyanghkust/finbert-tone) pipeline
|
14 |
-
@st.cache_resource
|
15 |
def load_model():
|
16 |
model = BertForSequenceClassification.from_pretrained('yiyanghkust/finbert-tone', num_labels=3)
|
17 |
tokenizer = BertTokenizer.from_pretrained('yiyanghkust/finbert-tone')
|
@@ -30,13 +30,17 @@ def verify_link(url, timeout=10, retries=3):
|
|
30 |
return False
|
31 |
|
32 |
def get_news(ticker):
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
40 |
|
41 |
def parse_news(news_table):
|
42 |
parsed_news = []
|
@@ -71,34 +75,35 @@ def parse_news(news_table):
|
|
71 |
return parsed_news_df
|
72 |
|
73 |
def score_news(parsed_news_df):
|
74 |
-
#
|
75 |
-
|
76 |
-
|
77 |
-
# Convert predictions to sentiment scores
|
78 |
sentiment_scores = []
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
82 |
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
elif label == 'LABEL_2':
|
87 |
-
sentiment_score = -score
|
88 |
-
else: # neutral
|
89 |
-
sentiment_score = 0
|
90 |
|
91 |
-
|
92 |
-
'
|
93 |
-
|
94 |
-
'
|
95 |
-
|
96 |
-
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
scores_df = pd.DataFrame(sentiment_scores)
|
99 |
-
|
100 |
-
# Join with original news DataFrame
|
101 |
-
parsed_and_scored_news = parsed_news_df.join(scores_df)
|
102 |
parsed_and_scored_news = parsed_and_scored_news.set_index('datetime')
|
103 |
|
104 |
return parsed_and_scored_news
|
@@ -154,43 +159,47 @@ ticker = st.text_input('Enter Stock Ticker', '').upper()
|
|
154 |
try:
|
155 |
st.subheader(f"Sentiment Analysis and Recommendation for {ticker} Stock")
|
156 |
news_table = get_news(ticker)
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
# Display a disclaimer
|
165 |
-
st.warning("Disclaimer: This recommendation is based solely on recent news sentiment and should not be considered as financial advice. Always do your own research and consult with a qualified financial advisor before making investment decisions.")
|
166 |
-
|
167 |
-
fig_hourly = plot_hourly_sentiment(parsed_and_scored_news, ticker)
|
168 |
-
fig_daily = plot_daily_sentiment(parsed_and_scored_news, ticker)
|
169 |
-
|
170 |
-
st.plotly_chart(fig_hourly)
|
171 |
-
st.plotly_chart(fig_daily)
|
172 |
-
|
173 |
-
description = f"""
|
174 |
-
The above charts average the sentiment scores of {ticker} stock hourly and daily.
|
175 |
-
The table below shows recent headlines with their sentiment scores and classifications.
|
176 |
-
The news headlines are obtained from the FinViz website.
|
177 |
-
Sentiments are analyzed using the yiyanghkust/finbert-tone model, which is specifically trained for financial text.
|
178 |
-
Links have been verified for validity.
|
179 |
-
"""
|
180 |
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
|
191 |
except Exception as e:
|
192 |
print(str(e))
|
193 |
-
st.write("Enter a correct stock ticker, e.g. 'AAPL' above and hit Enter.")
|
194 |
|
195 |
hide_streamlit_style = """
|
196 |
<style>
|
|
|
10 |
|
11 |
st.set_page_config(page_title="Stock News Sentiment Analyzer", layout="wide")
|
12 |
|
13 |
+
# Initialize FinBERT (yiyanghkust/finbert-tone) pipeline only once and cache
|
14 |
+
@st.cache_resource(show_spinner=False)
|
15 |
def load_model():
|
16 |
model = BertForSequenceClassification.from_pretrained('yiyanghkust/finbert-tone', num_labels=3)
|
17 |
tokenizer = BertTokenizer.from_pretrained('yiyanghkust/finbert-tone')
|
|
|
30 |
return False
|
31 |
|
32 |
def get_news(ticker):
|
33 |
+
try:
|
34 |
+
finviz_url = 'https://finviz.com/quote.ashx?t='
|
35 |
+
url = finviz_url + ticker
|
36 |
+
req = Request(url=url, headers={'User-Agent': 'Mozilla/5.0'})
|
37 |
+
response = urlopen(req)
|
38 |
+
html = BeautifulSoup(response, 'html.parser')
|
39 |
+
news_table = html.find(id='news-table')
|
40 |
+
return news_table
|
41 |
+
except Exception as e:
|
42 |
+
st.write("Error fetching news:", str(e))
|
43 |
+
return None
|
44 |
|
45 |
def parse_news(news_table):
|
46 |
parsed_news = []
|
|
|
75 |
return parsed_news_df
|
76 |
|
77 |
def score_news(parsed_news_df):
|
78 |
+
# Send headlines in smaller batches to speed up processing
|
79 |
+
batch_size = 10
|
80 |
+
parsed_news_df = parsed_news_df.reset_index(drop=True)
|
|
|
81 |
sentiment_scores = []
|
82 |
+
|
83 |
+
for i in range(0, len(parsed_news_df), batch_size):
|
84 |
+
batch_headlines = parsed_news_df['headline'][i:i+batch_size].tolist()
|
85 |
+
predictions = finbert(batch_headlines)
|
86 |
|
87 |
+
for pred in predictions:
|
88 |
+
label = pred['label']
|
89 |
+
score = pred['score']
|
|
|
|
|
|
|
|
|
90 |
|
91 |
+
# Map 'LABEL_0' (neutral), 'LABEL_1' (positive), and 'LABEL_2' (negative)
|
92 |
+
if label == 'LABEL_1':
|
93 |
+
sentiment_score = score
|
94 |
+
elif label == 'LABEL_2':
|
95 |
+
sentiment_score = -score
|
96 |
+
else: # neutral
|
97 |
+
sentiment_score = 0
|
98 |
+
|
99 |
+
sentiment_scores.append({
|
100 |
+
'sentiment_score': sentiment_score,
|
101 |
+
'label': label,
|
102 |
+
'confidence': score
|
103 |
+
})
|
104 |
+
|
105 |
scores_df = pd.DataFrame(sentiment_scores)
|
106 |
+
parsed_and_scored_news = pd.concat([parsed_news_df, scores_df], axis=1)
|
|
|
|
|
107 |
parsed_and_scored_news = parsed_and_scored_news.set_index('datetime')
|
108 |
|
109 |
return parsed_and_scored_news
|
|
|
159 |
try:
|
160 |
st.subheader(f"Sentiment Analysis and Recommendation for {ticker} Stock")
|
161 |
news_table = get_news(ticker)
|
162 |
+
if news_table:
|
163 |
+
parsed_news_df = parse_news(news_table)
|
164 |
+
parsed_and_scored_news = score_news(parsed_news_df)
|
165 |
+
|
166 |
+
# Generate and display recommendation
|
167 |
+
recommendation = get_recommendation(parsed_and_scored_news)
|
168 |
+
st.write(recommendation)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
|
170 |
+
# Display a disclaimer
|
171 |
+
st.warning("Disclaimer: This recommendation is based solely on recent news sentiment and should not be considered as financial advice. Always do your own research and consult with a qualified financial advisor before making investment decisions.")
|
172 |
+
|
173 |
+
fig_hourly = plot_hourly_sentiment(parsed_and_scored_news, ticker)
|
174 |
+
fig_daily = plot_daily_sentiment(parsed_and_scored_news, ticker)
|
175 |
+
|
176 |
+
st.plotly_chart(fig_hourly)
|
177 |
+
st.plotly_chart(fig_daily)
|
178 |
+
|
179 |
+
description = f"""
|
180 |
+
The above charts average the sentiment scores of {ticker} stock hourly and daily.
|
181 |
+
The table below shows recent headlines with their sentiment scores and classifications.
|
182 |
+
The news headlines are obtained from the FinViz website.
|
183 |
+
Sentiments are analyzed using the ProsusAI/finbert model, which is specifically trained for financial text.
|
184 |
+
Links have been verified for validity.
|
185 |
+
"""
|
186 |
+
|
187 |
+
st.write(description)
|
188 |
+
|
189 |
+
parsed_and_scored_news['link'] = parsed_and_scored_news.apply(
|
190 |
+
lambda row: f'<a href="{row["link"]}" target="_blank">{"Valid✅" if row["is_valid"] else "Invalid❌"} Link</a>',
|
191 |
+
axis=1
|
192 |
+
)
|
193 |
+
|
194 |
+
display_df = parsed_and_scored_news.drop(columns=['is_valid'])
|
195 |
+
st.write(display_df.to_html(escape=False), unsafe_allow_html=True)
|
196 |
+
|
197 |
+
else:
|
198 |
+
st.write("No news available or invalid ticker symbol.")
|
199 |
|
200 |
except Exception as e:
|
201 |
print(str(e))
|
202 |
+
st.write("Enter a correct stock ticker, e.g. 'AAPL' above and hit Enter.")
|
203 |
|
204 |
hide_streamlit_style = """
|
205 |
<style>
|