Ritvik commited on
Commit
c615548
Β·
1 Parent(s): bd5601f

Updated app

Browse files
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
.idea/.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
.idea/Finance_Stock_Prediction_v1.iml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
6
+ </content>
7
+ <orderEntry type="jdk" jdkName="Python 3.12 (Finance_Stock_Prediction_v1)" jdkType="Python SDK" />
8
+ <orderEntry type="sourceFolder" forTests="false" />
9
+ </component>
10
+ </module>
.idea/dataSources.xml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
4
+ <data-source source="LOCAL" name="sentiment_data" uuid="797dab22-bf41-4c84-8d62-7031ba070b53">
5
+ <driver-ref>sqlite.xerial</driver-ref>
6
+ <synchronize>true</synchronize>
7
+ <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
8
+ <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/sentiment_data.db</jdbc-url>
9
+ <working-dir>$ProjectFileDir$</working-dir>
10
+ </data-source>
11
+ </component>
12
+ </project>
.idea/inspectionProfiles/profiles_settings.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
.idea/misc.xml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.9 (pythonProject)" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Finance_Stock_Prediction_v1)" project-jdk-type="Python SDK" />
7
+ </project>
.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/Finance_Stock_Prediction_v1.iml" filepath="$PROJECT_DIR$/.idea/Finance_Stock_Prediction_v1.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
App.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ from bs4 import BeautifulSoup
4
+ import pandas as pd
5
+ import plotly.express as px
6
+ import datetime
7
+ import time
8
+ import nltk
9
+ from nltk.sentiment.vader import SentimentIntensityAnalyzer
10
+ from textblob import TextBlob
11
+ import ssl
12
+ import certifi
13
+ import sqlite3
14
+
15
+ # Configure SSL to use certifi certificates
16
+ ssl._create_default_https_context = lambda: ssl.create_default_context(cafile=certifi.where())
17
+
18
+ # Download VADER lexicon
19
+ try:
20
+ nltk.download('vader_lexicon')
21
+ print("βœ… VADER lexicon downloaded successfully!")
22
+ except Exception as e:
23
+ print(f"❌ Error downloading VADER lexicon: {str(e)}")
24
+ raise
25
+
26
+ # Initialize VADER sentiment analyzer
27
+ sia = SentimentIntensityAnalyzer()
28
+
29
+ FINVIZ_URL = 'https://finviz.com/quote.ashx?t='
30
+ session = requests.Session()
31
+ session.headers.update({
32
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
33
+ })
34
+
35
+
36
+ def get_news(ticker):
37
+ """ Fetch stock news from FinViz with error handling """
38
+ try:
39
+ url = FINVIZ_URL + ticker
40
+ for attempt in range(3):
41
+ response = session.get(url)
42
+ if response.status_code == 200:
43
+ break
44
+ time.sleep(5)
45
+
46
+ if response.status_code != 200:
47
+ return None, f"❌ Error: Received status code {response.status_code}"
48
+
49
+ html = BeautifulSoup(response.text, "html.parser")
50
+ news_table = html.find('table', class_='fullview-news-outer')
51
+
52
+ if not news_table:
53
+ return None, "❌ News table not found!"
54
+
55
+ return news_table, None
56
+ except Exception as e:
57
+ return None, f"❌ Error fetching stock news: {str(e)}"
58
+
59
+
60
+ def parse_news(news_table):
61
+ """ Extracts and parses stock news headlines from FinViz """
62
+ parsed_news = []
63
+ today_string = datetime.datetime.today().strftime('%Y-%m-%d')
64
+
65
+ for row in news_table.find_all('tr'):
66
+ try:
67
+ news_container = row.find('div', class_='news-link-container')
68
+ link = news_container.find('a', class_='tab-link-news') if news_container else row.find('a')
69
+
70
+ if not link or not link.get_text().strip():
71
+ continue
72
+ text = link.get_text().strip()
73
+
74
+ date_td = row.find('td', align='right')
75
+ date_scrape = date_td.text.strip().split() if date_td and date_td.text.strip() else []
76
+ if len(date_scrape) == 1:
77
+ date, time_ = today_string, date_scrape[0]
78
+ else:
79
+ date, time_ = datetime.datetime.strptime(date_scrape[0], '%b-%d-%y').strftime('%Y-%m-%d'), date_scrape[
80
+ 1]
81
+
82
+ time_ = datetime.datetime.strptime(time_, '%I:%M%p').strftime('%H:%M')
83
+ parsed_news.append([date, time_, text])
84
+ except Exception:
85
+ continue
86
+
87
+ df = pd.DataFrame(parsed_news, columns=['date', 'time', 'headline'])
88
+ df['datetime'] = pd.to_datetime(df['date'] + ' ' + df['time'], format="%Y-%m-%d %H:%M", errors='coerce')
89
+ return df.dropna(subset=['datetime'])
90
+
91
+
92
+ def analyze_sentiment(text):
93
+ """ Combines VADER and TextBlob for better sentiment accuracy """
94
+ vader_score = sia.polarity_scores(text)['compound']
95
+ textblob_score = TextBlob(text).sentiment.polarity # -1 to 1 range
96
+ return (vader_score + textblob_score) / 2 # Averaging both
97
+
98
+
99
+ def score_news(df):
100
+ """ Applies sentiment analysis using both VADER and TextBlob """
101
+ df['sentiment_score'] = df['headline'].apply(analyze_sentiment)
102
+ return df[['datetime', 'headline', 'sentiment_score']]
103
+
104
+
105
+ def save_to_db(df, ticker):
106
+ """ Stores sentiment analysis results in SQLite for historical tracking """
107
+ conn = sqlite3.connect("sentiment_data.db")
108
+ df.to_sql(f"{ticker}_news", conn, if_exists="append", index=False)
109
+ conn.close()
110
+
111
+
112
+ def plot_sentiment(df, ticker, interval):
113
+ """ Generates sentiment trend plots with correct filtering """
114
+ if df.empty:
115
+ return None
116
+
117
+ df['datetime'] = pd.to_datetime(df['datetime'])
118
+ df = df.set_index('datetime')
119
+
120
+ # βœ… Resample only within the available range
121
+ df = df.resample(interval).mean(numeric_only=True).dropna()
122
+
123
+ # βœ… Use rolling average to smooth the graph
124
+ df['rolling_avg'] = df['sentiment_score'].rolling(5, min_periods=1).mean()
125
+
126
+ fig = px.line(df, x=df.index, y=['sentiment_score', 'rolling_avg'],
127
+ labels={"value": "Sentiment Score"},
128
+ title=f"{ticker} {interval.capitalize()} Sentiment Trends")
129
+ return fig
130
+
131
+
132
+ def analyze_stock_sentiment(ticker, days):
133
+ """ Fetches news, analyzes sentiment, and filters by user-selected date range. """
134
+ if not ticker:
135
+ return "❌ Please enter a stock ticker!", None, None, None
136
+
137
+ print(f"πŸ“… Selected Days: {days}") # Debugging
138
+
139
+ news_table, error = get_news(ticker.upper())
140
+ if error:
141
+ return error, None, None, None
142
+
143
+ df_news = parse_news(news_table)
144
+
145
+ # βœ… Convert `days` to an integer and filter news based on the correct time range
146
+ days = int(days)
147
+ today = datetime.datetime.today()
148
+ start_date = today - datetime.timedelta(days=days)
149
+
150
+ print(f"πŸ” Filtering News from {start_date} to {today}") # Debugging
151
+
152
+ df_news['datetime'] = pd.to_datetime(df_news['datetime'])
153
+ df_news = df_news[df_news['datetime'] >= start_date]
154
+
155
+ if df_news.empty:
156
+ return f"⚠️ No news found for {ticker.upper()} in the last {days} days.", None, None, None
157
+
158
+ df_scored = score_news(df_news).sort_values(by="datetime", ascending=False)
159
+
160
+ print(f"πŸ“Š Filtered News Count: {len(df_scored)}") # Debugging
161
+
162
+ save_to_db(df_scored, ticker.upper())
163
+
164
+ fig_hourly = plot_sentiment(df_scored, ticker, interval='h')
165
+ fig_daily = plot_sentiment(df_scored, ticker, interval='D')
166
+
167
+ return f"βœ… Analysis for {ticker.upper()} (Last {days} Days) Complete!", df_scored, fig_hourly, fig_daily
168
+
169
+
170
+ # Gradio Interface
171
+ with gr.Blocks(title="πŸ“ˆ Stock News Sentiment Analyzer") as iface:
172
+ with gr.Row():
173
+ gr.Markdown("## πŸ“ˆStock News Sentiment Analyzer")
174
+ gr.Markdown("Analyze stock news sentiment using VADER and TextBlob.")
175
+
176
+ ticker_dropdown = gr.Dropdown(choices=["AAPL", "TSLA", "AMZN", "MSFT"], label="Stock Ticker")
177
+ days_slider = gr.Slider(minimum=1, maximum=30, step=1, label="Days of News History", value=7, interactive=True)
178
+ status = gr.Textbox(label="Status", interactive=False)
179
+
180
+ with gr.Row():
181
+ submit_btn = gr.Button("Analyze", variant="primary")
182
+ clear_btn = gr.Button("Clear", variant="secondary")
183
+
184
+ table = gr.Dataframe(label="Sentiment Analysis", interactive=False)
185
+ hourly_plot = gr.Plot(label="Hourly Sentiment Scores")
186
+ daily_plot = gr.Plot(label="Daily Sentiment Scores")
187
+
188
+ submit_btn.click(fn=analyze_stock_sentiment, inputs=[ticker_dropdown, days_slider],
189
+ outputs=[status, table, hourly_plot, daily_plot])
190
+ clear_btn.click(fn=lambda: ("", None, None, None), inputs=None, outputs=[status, table, hourly_plot, daily_plot])
191
+
192
+ iface.launch(share=True)
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ requests
3
+ beautifulsoup4
4
+ pandas
5
+ plotly
6
+ nltk
7
+ textblob
8
+ certifi
9
+ sqlite3
sentiment_data.db ADDED
Binary file (303 kB). View file