Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -4,14 +4,19 @@ from bs4 import BeautifulSoup
|
|
4 |
import pandas as pd
|
5 |
import plotly.express as px
|
6 |
from dateutil import parser
|
7 |
-
import nltk
|
8 |
-
nltk.downloader.download('vader_lexicon')
|
9 |
-
from nltk.sentiment.vader import SentimentIntensityAnalyzer
|
10 |
import datetime
|
11 |
import requests
|
|
|
12 |
|
13 |
st.set_page_config(page_title="Stock News Sentiment Analyzer", layout="wide")
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
def verify_link(url, timeout=10, retries=3):
|
16 |
for _ in range(retries):
|
17 |
try:
|
@@ -64,19 +69,40 @@ def parse_news(news_table):
|
|
64 |
return parsed_news_df
|
65 |
|
66 |
def score_news(parsed_news_df):
|
67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
-
scores = parsed_news_df['headline'].apply(vader.polarity_scores).tolist()
|
70 |
-
scores_df = pd.DataFrame(scores)
|
71 |
-
parsed_and_scored_news = parsed_news_df.join(scores_df, rsuffix='_right')
|
72 |
-
parsed_and_scored_news = parsed_and_scored_news.set_index('datetime')
|
73 |
-
parsed_and_scored_news = parsed_and_scored_news.rename(columns={"compound": "sentiment_score"})
|
74 |
-
|
75 |
return parsed_and_scored_news
|
76 |
|
77 |
def plot_hourly_sentiment(parsed_and_scored_news, ticker):
|
78 |
-
|
79 |
-
mean_scores = numeric_cols.resample('h').mean()
|
80 |
|
81 |
fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score',
|
82 |
title=f'{ticker} Hourly Sentiment Scores',
|
@@ -93,8 +119,7 @@ def plot_hourly_sentiment(parsed_and_scored_news, ticker):
|
|
93 |
return fig
|
94 |
|
95 |
def plot_daily_sentiment(parsed_and_scored_news, ticker):
|
96 |
-
|
97 |
-
mean_scores = numeric_cols.resample('D').mean()
|
98 |
|
99 |
fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score',
|
100 |
title=f'{ticker} Daily Sentiment Scores',
|
@@ -113,14 +138,14 @@ def plot_daily_sentiment(parsed_and_scored_news, ticker):
|
|
113 |
def get_recommendation(sentiment_scores):
|
114 |
avg_sentiment = sentiment_scores['sentiment_score'].mean()
|
115 |
|
116 |
-
if avg_sentiment >= 0.
|
117 |
return f"Positive sentiment (Score: {avg_sentiment:.2f}). The recent news suggests a favorable outlook for this stock. Consider buying or holding if you already own it."
|
118 |
-
elif avg_sentiment <= -0.
|
119 |
return f"Negative sentiment (Score: {avg_sentiment:.2f}). The recent news suggests caution. Consider selling or avoiding this stock for now."
|
120 |
else:
|
121 |
return f"Neutral sentiment (Score: {avg_sentiment:.2f}). The recent news doesn't show a strong bias. Consider holding if you own the stock, or watch for more definitive trends before making a decision."
|
122 |
|
123 |
-
st.header("Stock News Sentiment Analyzer")
|
124 |
|
125 |
ticker = st.text_input('Enter Stock Ticker', '').upper()
|
126 |
|
@@ -145,9 +170,9 @@ try:
|
|
145 |
|
146 |
description = f"""
|
147 |
The above charts average the sentiment scores of {ticker} stock hourly and daily.
|
148 |
-
The table below
|
149 |
The news headlines are obtained from the FinViz website.
|
150 |
-
Sentiments are
|
151 |
Links have been verified for validity.
|
152 |
"""
|
153 |
|
@@ -158,7 +183,8 @@ try:
|
|
158 |
axis=1
|
159 |
)
|
160 |
|
161 |
-
|
|
|
162 |
|
163 |
except Exception as e:
|
164 |
print(str(e))
|
|
|
4 |
import pandas as pd
|
5 |
import plotly.express as px
|
6 |
from dateutil import parser
|
|
|
|
|
|
|
7 |
import datetime
|
8 |
import requests
|
9 |
+
from transformers import pipeline
|
10 |
|
11 |
st.set_page_config(page_title="Stock News Sentiment Analyzer", layout="wide")
|
12 |
|
13 |
+
# Initialize FinBERT pipeline
|
14 |
+
@st.cache_resource
|
15 |
+
def load_model():
|
16 |
+
return pipeline("text-classification", model="ProsusAI/finbert")
|
17 |
+
|
18 |
+
finbert = load_model()
|
19 |
+
|
20 |
def verify_link(url, timeout=10, retries=3):
|
21 |
for _ in range(retries):
|
22 |
try:
|
|
|
69 |
return parsed_news_df
|
70 |
|
71 |
def score_news(parsed_news_df):
|
72 |
+
# Get FinBERT predictions
|
73 |
+
predictions = finbert(parsed_news_df['headline'].tolist())
|
74 |
+
|
75 |
+
# Convert predictions to sentiment scores
|
76 |
+
sentiment_scores = []
|
77 |
+
for pred in predictions:
|
78 |
+
label = pred['label']
|
79 |
+
score = pred['score']
|
80 |
+
|
81 |
+
# Convert to -1 to 1 scale
|
82 |
+
if label == 'positive':
|
83 |
+
sentiment_score = score
|
84 |
+
elif label == 'negative':
|
85 |
+
sentiment_score = -score
|
86 |
+
else: # neutral
|
87 |
+
sentiment_score = 0
|
88 |
+
|
89 |
+
sentiment_scores.append({
|
90 |
+
'sentiment_score': sentiment_score,
|
91 |
+
'label': label,
|
92 |
+
'confidence': score
|
93 |
+
})
|
94 |
+
|
95 |
+
# Convert to DataFrame
|
96 |
+
scores_df = pd.DataFrame(sentiment_scores)
|
97 |
+
|
98 |
+
# Join with original news DataFrame
|
99 |
+
parsed_and_scored_news = parsed_news_df.join(scores_df)
|
100 |
+
parsed_and_scored_news = parsed_and_scored_news.set_index('datetime')
|
101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
return parsed_and_scored_news
|
103 |
|
104 |
def plot_hourly_sentiment(parsed_and_scored_news, ticker):
|
105 |
+
mean_scores = parsed_and_scored_news['sentiment_score'].resample('h').mean()
|
|
|
106 |
|
107 |
fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score',
|
108 |
title=f'{ticker} Hourly Sentiment Scores',
|
|
|
119 |
return fig
|
120 |
|
121 |
def plot_daily_sentiment(parsed_and_scored_news, ticker):
|
122 |
+
mean_scores = parsed_and_scored_news['sentiment_score'].resample('D').mean()
|
|
|
123 |
|
124 |
fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score',
|
125 |
title=f'{ticker} Daily Sentiment Scores',
|
|
|
138 |
def get_recommendation(sentiment_scores):
|
139 |
avg_sentiment = sentiment_scores['sentiment_score'].mean()
|
140 |
|
141 |
+
if avg_sentiment >= 0.3:
|
142 |
return f"Positive sentiment (Score: {avg_sentiment:.2f}). The recent news suggests a favorable outlook for this stock. Consider buying or holding if you already own it."
|
143 |
+
elif avg_sentiment <= -0.3:
|
144 |
return f"Negative sentiment (Score: {avg_sentiment:.2f}). The recent news suggests caution. Consider selling or avoiding this stock for now."
|
145 |
else:
|
146 |
return f"Neutral sentiment (Score: {avg_sentiment:.2f}). The recent news doesn't show a strong bias. Consider holding if you own the stock, or watch for more definitive trends before making a decision."
|
147 |
|
148 |
+
st.header("Stock News Sentiment Analyzer (FinBERT)")
|
149 |
|
150 |
ticker = st.text_input('Enter Stock Ticker', '').upper()
|
151 |
|
|
|
170 |
|
171 |
description = f"""
|
172 |
The above charts average the sentiment scores of {ticker} stock hourly and daily.
|
173 |
+
The table below shows recent headlines with their sentiment scores and classifications.
|
174 |
The news headlines are obtained from the FinViz website.
|
175 |
+
Sentiments are analyzed using the ProsusAI/finbert model, which is specifically trained for financial text.
|
176 |
Links have been verified for validity.
|
177 |
"""
|
178 |
|
|
|
183 |
axis=1
|
184 |
)
|
185 |
|
186 |
+
display_df = parsed_and_scored_news.drop(columns=['is_valid'])
|
187 |
+
st.write(display_df.to_html(escape=False), unsafe_allow_html=True)
|
188 |
|
189 |
except Exception as e:
|
190 |
print(str(e))
|