Spaces:
Build error
Build error
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))
|