Spaces:
Sleeping
Sleeping
File size: 4,996 Bytes
092ae8c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
import streamlit as st
from urllib.request import urlopen, Request
from bs4 import BeautifulSoup
import pandas as pd
import plotly.express as px
import json
import nltk
import datetime
from nltk.sentiment.vader import SentimentIntensityAnalyzer
# Ensure nltk dependencies are downloaded
nltk.download('vader_lexicon')
# Page Config
st.set_page_config(page_title="StockSim News Sentiment Analyzer", layout="wide")
# Custom CSS for Glassmorphism & Styling
st.markdown("""
<style>
body {
font-family: 'Arial', sans-serif;
}
.glass {
background: rgba(255, 255, 255, 0.1);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
padding: 20px;
}
.button {
background: linear-gradient(135deg, #ff7eb3, #ff758c);
color: black;
padding: 10px;
border-radius: 10px;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
.button:hover {
background: linear-gradient(135deg, #ff758c, #ff7eb3);
transform: scale(1.05);
}
h1 {
color: black;
text-align: center;
}
</style>
""", unsafe_allow_html=True)
# Header
st.markdown('<h1>π StockSim News Sentiment Analyzer π</h1>', unsafe_allow_html=True)
# Finviz URL
finviz_url = 'https://finviz.com/quote.ashx?t='
def get_news(ticker):
try:
url = finviz_url + ticker
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
response = urlopen(req)
html = BeautifulSoup(response, 'html.parser')
news_table = html.find(id='news-table')
return news_table
except Exception as e:
st.error(f"Error fetching news: {e}")
return None
def parse_news(news_table):
parsed_news = []
today_string = datetime.datetime.today().strftime('%Y-%m-%d')
if news_table:
for x in news_table.findAll('tr'):
try:
text = x.a.get_text() if x.a else ""
date_scrape = x.td.text.split() if x.td else []
date = today_string if len(date_scrape) == 1 else date_scrape[0]
time = date_scrape[-1] if date_scrape else "00:00"
parsed_news.append([date, time, text])
except Exception as e:
st.warning(f"Skipping a row due to error: {e}")
parsed_news_df = pd.DataFrame(parsed_news, columns=['date', 'time', 'headline'])
parsed_news_df['datetime'] = pd.to_datetime(parsed_news_df['date'] + ' ' + parsed_news_df['time'], errors='coerce')
parsed_news_df.dropna(inplace=True)
return parsed_news_df
def score_news(parsed_news_df):
vader = SentimentIntensityAnalyzer()
scores = parsed_news_df['headline'].apply(lambda x: vader.polarity_scores(x) if isinstance(x, str) else {'compound': 0}).tolist()
scores_df = pd.DataFrame(scores)
parsed_and_scored_news = parsed_news_df.join(scores_df).set_index('datetime')
parsed_and_scored_news = parsed_and_scored_news.rename(columns={"compound": "sentiment_score"})
return parsed_and_scored_news
def plot_sentiment(parsed_and_scored_news, ticker, freq):
if not parsed_and_scored_news.empty:
mean_scores = parsed_and_scored_news.resample(freq).mean()
fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score',
title=f'{ticker} {freq} Sentiment Scores')
return fig
else:
st.warning("No sentiment data available for plotting.")
return None
# UI Elements
ticker = st.text_input('π Enter Stock Ticker', '').upper()
if ticker:
try:
st.markdown('<div class="glass">', unsafe_allow_html=True)
st.subheader(f"π Sentiment Analysis for {ticker} Stock")
news_table = get_news(ticker)
if news_table:
parsed_news_df = parse_news(news_table)
parsed_and_scored_news = score_news(parsed_news_df)
fig_hourly = plot_sentiment(parsed_and_scored_news, ticker, 'H')
fig_daily = plot_sentiment(parsed_and_scored_news, ticker, 'D')
if fig_hourly:
st.plotly_chart(fig_hourly)
if fig_daily:
st.plotly_chart(fig_daily)
st.markdown("""
The above charts show **hourly and daily sentiment scores** for the stock.
News headlines are obtained from **FinViz** and analyzed using **NLTK Vader**.
""")
st.table(parsed_and_scored_news[['headline', 'sentiment_score']])
else:
st.warning("No news data available for this ticker.")
st.markdown('</div>', unsafe_allow_html=True)
except Exception as e:
st.error(f"β οΈ An unexpected error occurred: {str(e)}")
else:
st.info("βΉοΈ Enter a valid stock ticker (e.g., AAPL) and hit Enter.") |