chrisvnz commited on
Commit
79de53b
·
1 Parent(s): 9b098af

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -40
app.py CHANGED
@@ -1,21 +1,58 @@
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
6
  import plotly.express as px
7
- import json # for graph plotting in website
8
- # NLTK VADER for sentiment analysis
9
  import nltk
10
  nltk.downloader.download('vader_lexicon')
11
  from nltk.sentiment.vader import SentimentIntensityAnalyzer
12
-
13
- import subprocess
14
  from datetime import datetime
15
- import os
16
 
17
- st.set_page_config(page_title = "Stock News Sentiment Analyzer", layout = "wide")
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  def get_news(ticker):
21
  url = finviz_url + ticker
@@ -103,43 +140,103 @@ def plot_daily_sentiment(parsed_and_scored_news, ticker):
103
  fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score', title = ticker + ' Daily Sentiment Scores')
104
  return fig # instead of using fig.show(), we return fig and turn it into a graphjson object for displaying in web page later
105
 
106
- # for extracting data from finviz
107
- finviz_url = 'https://finviz.com/quote.ashx?t='
108
-
109
 
110
  st.header("Stock News Sentiment Analyzer")
111
 
112
  ticker = st.text_input('Enter Stock Ticker', '').upper()
113
 
114
- df = pd.DataFrame({'datetime': datetime.now(), 'ticker': ticker}, index = [0])
115
-
116
-
117
- try:
118
- st.subheader("Hourly and Daily Sentiment of {} Stock".format(ticker))
119
- news_table = get_news(ticker)
120
- parsed_news_df = parse_news(news_table)
121
- print(parsed_news_df)
122
- parsed_and_scored_news = score_news(parsed_news_df)
123
- fig_hourly = plot_hourly_sentiment(parsed_and_scored_news, ticker)
124
- fig_daily = plot_daily_sentiment(parsed_and_scored_news, ticker)
125
-
126
- st.plotly_chart(fig_hourly)
127
- st.plotly_chart(fig_daily)
128
-
129
- description = """
130
- The above chart averages the sentiment scores of {} stock hourly and daily.
131
- The table below gives each of the most recent headlines of the stock and the negative, neutral, positive and an aggregated sentiment score.
132
- The news headlines are obtained from the FinViz website.
133
- Sentiments are given by the nltk.sentiment.vader Python library.
134
- Adapted from https://github.com/damianboh/stock_sentiment_streamlit
135
- """.format(ticker)
136
-
137
- st.write(description)
138
- st.table(parsed_and_scored_news)
139
-
140
- except Exception as e:
141
- print(str(e))
142
- st.write("Enter a stock ticker, e.g. 'AAPL' above and hit Enter.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  hide_streamlit_style = """
145
  <style>
@@ -150,4 +247,5 @@ footer {visibility: hidden;}
150
  st.markdown(hide_streamlit_style, unsafe_allow_html=True)
151
 
152
 
153
- # streamlit run "c:/Dropbox/BIMWERX/_work/YouTube/elephant/230711 - Stock Sentiment/src/stock_sentiment_streamlit/app.py"
 
 
1
  import streamlit as st
2
+ import yfinance as yf
3
+ import matplotlib.pyplot as plt
4
+ import pandas as pd
5
+ import numpy as np
6
  from urllib.request import urlopen, Request
7
  from bs4 import BeautifulSoup
 
8
  import plotly
9
  import plotly.express as px
 
 
10
  import nltk
11
  nltk.downloader.download('vader_lexicon')
12
  from nltk.sentiment.vader import SentimentIntensityAnalyzer
 
 
13
  from datetime import datetime
14
+ from sklearn.preprocessing import MinMaxScaler
15
 
16
+ finviz_url = 'https://finviz.com/quote.ashx?t='
17
 
18
+ sector_etf_mapping = {
19
+ 'Consumer Durables': 'XLY',
20
+ 'Consumer Discretionary': 'XLY',
21
+ 'Consumer Staples': 'XLP',
22
+ 'Energy': 'XLE',
23
+ 'Financials': 'XLF',
24
+ 'Health Care': 'XLV',
25
+ 'Industrials': 'XLI',
26
+ 'Materials': 'XLB',
27
+ 'Real Estate': 'XLRE',
28
+ 'Technology': 'XLK',
29
+ 'Utilities': 'XLU',
30
+ 'Communication Services': 'XLC'
31
+ }
32
+
33
+ # Function to get the trend of a given stock or ETF
34
+ def get_trend(ticker, period='1mo'):
35
+ stock = yf.Ticker(ticker)
36
+ hist = stock.history(period=period)
37
+ return hist['Close']
38
+
39
+ # Function to normalize a pandas series
40
+ def normalize(series):
41
+ if len(series.unique()) > 1: # Check if the series has more than one unique value
42
+ scaler = MinMaxScaler(feature_range=(0, 1))
43
+ scaled_values = scaler.fit_transform(series.values.reshape(-1,1))
44
+ return pd.Series(scaled_values.flatten(), index=series.index)
45
+ else:
46
+ return series # If the series has only one unique value, return it as is
47
+
48
+
49
+ # Function to get the trend of a given sector or industry
50
+ def get_trend_from_csv(df, column, value, period='1mo'):
51
+ # Filter the dataframe by the given sector or industry
52
+ df_filtered = df[df[column] == value]
53
+ # Calculate the mean of the closing prices of all stocks in the sector or industry
54
+ trend = df_filtered.resample(period).mean()
55
+ return trend
56
 
57
  def get_news(ticker):
58
  url = finviz_url + ticker
 
140
  fig = px.bar(mean_scores, x=mean_scores.index, y='sentiment_score', title = ticker + ' Daily Sentiment Scores')
141
  return fig # instead of using fig.show(), we return fig and turn it into a graphjson object for displaying in web page later
142
 
143
+ # Streamlit app
144
+ st.set_page_config(page_title = "Stock Trend and News Sentiment Analyzer", layout = "wide")
 
145
 
146
  st.header("Stock News Sentiment Analyzer")
147
 
148
  ticker = st.text_input('Enter Stock Ticker', '').upper()
149
 
150
+ uploaded_file = st.file_uploader("Choose a CSV file", type="csv")
151
+ if uploaded_file is not None:
152
+ data = pd.read_csv(uploaded_file)
153
+
154
+ try:
155
+ # Display company name, last close, and sector from CSV
156
+ company_name = data.loc[data['Ticker'] == ticker, 'Description'].values[0]
157
+ last_close = data.loc[data['Ticker'] == ticker, 'Price'].values[0]
158
+ csv_sector = data.loc[data['Ticker'] == ticker, 'Sector'].values[0]
159
+ st.write(f"Company: {company_name} ({csv_sector})")
160
+ st.write(f"Last Close: {last_close}")
161
+
162
+
163
+ # Define a list of all possible sectors and their corresponding ETFs
164
+ all_sectors = ['XLY', 'XLP', 'XLE', 'XLF', 'XLV', 'XLI', 'XLB', 'XLRE', 'XLK', 'XLU', 'XLC']
165
+ sector_mapping = {
166
+ 'XLY': 'Consumer Discretionary',
167
+ 'XLP': 'Consumer Staples',
168
+ 'XLE': 'Energy',
169
+ 'XLF': 'Financials',
170
+ 'XLV': 'Health Care',
171
+ 'XLI': 'Industrials',
172
+ 'XLB': 'Materials',
173
+ 'XLRE': 'Real Estate',
174
+ 'XLK': 'Technology',
175
+ 'XLU': 'Utilities',
176
+ 'XLC': 'Communication Services'
177
+ }
178
+
179
+ # Get sector from CSV
180
+ sector = data.loc[data['Ticker'] == ticker, 'Sector'].values[0]
181
+
182
+ # If the sector is not found in the list of all sectors, set it to '--Not Found--'
183
+ if sector not in sector_mapping.values():
184
+ sector = '--Not Found--'
185
+ else:
186
+ # Convert the sector to its corresponding ETF
187
+ sector = list(sector_mapping.keys())[list(sector_mapping.values()).index(sector)]
188
+
189
+ # Display a dropdown menu for the sector with the current sector selected by default
190
+ sector = st.selectbox('Sector', options=all_sectors, index=all_sectors.index(sector) if sector in all_sectors else 0)
191
+
192
+ # Display the sector mapping table
193
+ st.table(pd.DataFrame(list(sector_mapping.items()), columns=['ETF', 'Sector']))
194
+
195
+ if st.button('Submit'):
196
+ # Get trends
197
+ stock_trend = get_trend(ticker)
198
+ sector_trend = get_trend(sector)
199
+
200
+ # Normalize trends
201
+ scaler = MinMaxScaler()
202
+ stock_trend = pd.DataFrame(scaler.fit_transform(stock_trend.values.reshape(-1,1)), index=stock_trend.index, columns=['Close'])
203
+ sector_trend = pd.DataFrame(scaler.fit_transform(sector_trend.values.reshape(-1,1)), index=sector_trend.index, columns=['Close'])
204
+
205
+ # Plot trends
206
+ plt.figure(figsize=(12,6))
207
+ plt.plot(stock_trend.index, stock_trend.values, label=ticker)
208
+ plt.plot(sector_trend.index, sector_trend.values, label=sector)
209
+ plt.legend()
210
+ plt.grid(True)
211
+ plt.title('Stock and Sector Trend over the past 30 days')
212
+ st.pyplot(plt)
213
+
214
+ st.subheader("Hourly and Daily Sentiment of {} Stock".format(ticker))
215
+ news_table = get_news(ticker)
216
+ parsed_news_df = parse_news(news_table)
217
+ parsed_and_scored_news = score_news(parsed_news_df)
218
+ fig_hourly = plot_hourly_sentiment(parsed_and_scored_news, ticker)
219
+ fig_daily = plot_daily_sentiment(parsed_and_scored_news, ticker)
220
+
221
+ st.plotly_chart(fig_hourly)
222
+ st.plotly_chart(fig_daily)
223
+
224
+ description = """
225
+ The above chart averages the sentiment scores of {} stock hourly and daily.
226
+ The table below gives each of the most recent headlines of the stock and the negative, neutral, positive and an aggregated sentiment score.
227
+ The news headlines are obtained from the FinViz website.
228
+ Sentiments are given by the nltk.sentiment.vader Python library.
229
+ Adapted from https://github.com/damianboh/stock_sentiment_streamlit
230
+ """.format(ticker)
231
+
232
+ st.write(description)
233
+ st.table(parsed_and_scored_news)
234
+
235
+ except Exception as e:
236
+ print(str(e))
237
+ st.write("Enter a stock ticker, e.g. 'AAPL' above and hit Enter.")
238
+ else:
239
+ st.write("Please upload a CSV file.")
240
 
241
  hide_streamlit_style = """
242
  <style>
 
247
  st.markdown(hide_streamlit_style, unsafe_allow_html=True)
248
 
249
 
250
+
251
+ # streamlit run "c:/Dropbox/BIMWERX/_work/YouTube/elephant/230701-10 - ML Predict/src/trend_sentiment.py"