JaganathC commited on
Commit
092ae8c
Β·
verified Β·
1 Parent(s): 374811c

Create app.py

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