Spaces:
Running
Running
truevis
commited on
Commit
•
182c84d
1
Parent(s):
b077ed8
Update app.py
Browse files
app.py
CHANGED
@@ -1,4 +1,203 @@
|
|
1 |
import streamlit as st
|
|
|
|
|
|
|
|
|
2 |
|
3 |
-
|
4 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
import yfinance as yf
|
3 |
+
import pandas as pd
|
4 |
+
import matplotlib.pyplot as plt
|
5 |
+
import streamlit.components.v1 as components
|
6 |
|
7 |
+
# Set the page to wide mode and specify a title
|
8 |
+
st.set_page_config(layout="wide", page_title="Stock Insight Explorer", page_icon=":chart_with_upwards_trend:")
|
9 |
+
|
10 |
+
# Display the application title in the app itself
|
11 |
+
st.title('Stock Insight Explorer: Visual Analytics & Dividend Overview')
|
12 |
+
# Input field for stock symbol
|
13 |
+
stock_symbol = st.text_input('Enter Stock Symbol', value='JEPI')
|
14 |
+
|
15 |
+
# Fetch stock data
|
16 |
+
stock_data = yf.Ticker(stock_symbol)
|
17 |
+
|
18 |
+
# Get historical data for the past year
|
19 |
+
end_date = pd.Timestamp.today()
|
20 |
+
start_date = end_date - pd.DateOffset(years=1)
|
21 |
+
historical_data = stock_data.history(start=start_date, end=end_date)
|
22 |
+
|
23 |
+
# Display company name and current stock price in bold
|
24 |
+
company_name = stock_data.info['longName']
|
25 |
+
current_price = historical_data['Close'].iloc[-1]
|
26 |
+
# st.markdown(f"**Company Name:** {company_name}")
|
27 |
+
# https://finance.yahoo.com/quote/JEPQ?.tsrc=fin-srch
|
28 |
+
company_url = f"https://finance.yahoo.com/quote/{stock_symbol}?.tsrc=fin-srch"
|
29 |
+
|
30 |
+
# Use HTML anchor tag to create a hyperlink
|
31 |
+
st.markdown(f"**Company Name:** <a href='{company_url}' target='_blank'>{company_name}</a>", unsafe_allow_html=True)
|
32 |
+
|
33 |
+
st.markdown(f"**Current Price:** ${current_price:.2f}")
|
34 |
+
|
35 |
+
# Creating 3 columns for the charts
|
36 |
+
col1, col2, col3, col4 = st.columns(4)
|
37 |
+
|
38 |
+
|
39 |
+
# Closing prices chart
|
40 |
+
with col1:
|
41 |
+
st.subheader('Closing Prices Over The Past Year')
|
42 |
+
st.line_chart(historical_data['Close'])
|
43 |
+
|
44 |
+
# Volume of stock trades chart
|
45 |
+
with col2:
|
46 |
+
st.subheader('Volume of Stock Trades Over The Past Year')
|
47 |
+
st.bar_chart(historical_data['Volume'])
|
48 |
+
|
49 |
+
# Calculate 20 day and 50 day moving averages
|
50 |
+
historical_data['20 Day MA'] = historical_data['Close'].rolling(window=20).mean()
|
51 |
+
historical_data['50 Day MA'] = historical_data['Close'].rolling(window=50).mean()
|
52 |
+
|
53 |
+
# Line chart for closing price, 20 day MA, and 50 day MA
|
54 |
+
with col3:
|
55 |
+
st.subheader('Closing Price and Moving Averages')
|
56 |
+
plt.figure(figsize=(10, 5))
|
57 |
+
plt.plot(historical_data.index, historical_data['Close'], label='Close Price')
|
58 |
+
plt.plot(historical_data.index, historical_data['20 Day MA'], label='20 Day MA')
|
59 |
+
plt.plot(historical_data.index, historical_data['50 Day MA'], label='50 Day MA')
|
60 |
+
plt.legend()
|
61 |
+
plt.xlabel('Date')
|
62 |
+
plt.ylabel('Price')
|
63 |
+
plt.title('Closing Price, 20 Day MA, and 50 Day MA')
|
64 |
+
st.pyplot(plt)
|
65 |
+
|
66 |
+
# Get the dividend data
|
67 |
+
# Assuming stock_data is your Ticker object from yfinance
|
68 |
+
dividends = stock_data.dividends
|
69 |
+
|
70 |
+
# Check if there are any dividends
|
71 |
+
if not dividends.empty:
|
72 |
+
last_dividend_value = dividends.iloc[-1] # Get the last dividend value from the dividends history
|
73 |
+
else:
|
74 |
+
# Fallback to the lastDividendValue from the .info attribute if no historical dividends are found
|
75 |
+
last_dividend_value = stock_data.info.get('lastDividendValue', 0)
|
76 |
+
|
77 |
+
with col4:
|
78 |
+
st.subheader('Dividends')
|
79 |
+
|
80 |
+
markdown_output = "" # Initialize an empty string to accumulate messages
|
81 |
+
|
82 |
+
# Ensure dividends.index is in the correct format
|
83 |
+
dividends.index = pd.to_datetime(dividends.index).date
|
84 |
+
|
85 |
+
if not dividends.empty:
|
86 |
+
plt.figure(figsize=(10, 5))
|
87 |
+
dividends.plot(kind='bar')
|
88 |
+
plt.title(f'Dividends of {stock_symbol} Over Time')
|
89 |
+
plt.ylabel('Dividend Amount')
|
90 |
+
plt.xlabel('Date')
|
91 |
+
plt.xticks(rotation=45)
|
92 |
+
plt.tight_layout()
|
93 |
+
st.pyplot(plt)
|
94 |
+
|
95 |
+
# Calculate total annual dividends
|
96 |
+
today = pd.to_datetime("today")
|
97 |
+
last_year_dividends = dividends.loc[dividends.index > (today - pd.DateOffset(years=1)).date()]
|
98 |
+
total_annual_dividends = last_year_dividends.sum()
|
99 |
+
|
100 |
+
if current_price and current_price > 0:
|
101 |
+
annual_dividend_yield = (total_annual_dividends / current_price) * 100
|
102 |
+
markdown_output += f"**Annual Dividend Yield:** {annual_dividend_yield:.2f}%.\n"
|
103 |
+
else:
|
104 |
+
markdown_output += "Current price not available. Cannot calculate annual dividend yield.\n"
|
105 |
+
else:
|
106 |
+
markdown_output += "This stock does not have any dividend data available.\n"
|
107 |
+
total_annual_dividends = 0 # Define it as 0 if there are no dividends
|
108 |
+
|
109 |
+
if current_price and current_price > 0 and last_dividend_value:
|
110 |
+
dividend_yield = (last_dividend_value / current_price) * 100
|
111 |
+
markdown_output += f"**Last Dividend Yield:** {dividend_yield:.2f}%, ${last_dividend_value:.2f} per share."
|
112 |
+
else:
|
113 |
+
markdown_output += "Cannot calculate last dividend yield."
|
114 |
+
|
115 |
+
# Display all collected information in one markdown output
|
116 |
+
|
117 |
+
st.markdown(markdown_output, unsafe_allow_html=True)
|
118 |
+
|
119 |
+
|
120 |
+
|
121 |
+
|
122 |
+
|
123 |
+
# components.iframe("https://duckduckgo.com/?q=ZIM&va=i&t=hd&iar=news&df=w&ia=news")
|
124 |
+
|
125 |
+
# components.iframe("https://news.google.com/rss/search?q=ZIM&hl=en-US&gl=US&ceid=US%3Aen", height=800, scrolling=True)
|
126 |
+
# components.iframe("https://truevis.com", height=800, scrolling=True)
|
127 |
+
|
128 |
+
|
129 |
+
from unicodedata import normalize
|
130 |
+
from bs4 import BeautifulSoup
|
131 |
+
from feedparser import parse as parse_feed
|
132 |
+
|
133 |
+
def flatten_unicode_keys(entry_properties):
|
134 |
+
"""Ensures passing unicode keywords to **kwargs."""
|
135 |
+
new_properties = {}
|
136 |
+
for key, value in entry_properties.items():
|
137 |
+
key_str = key.decode('utf-8') if isinstance(key, bytes) else key
|
138 |
+
new_key = normalize('NFKD', key_str).encode('ascii', 'ignore').decode('ascii') # Decode the bytes object to string
|
139 |
+
new_properties[new_key] = value
|
140 |
+
return new_properties
|
141 |
+
|
142 |
+
TEMPLATE = """
|
143 |
+
<h2 class='title'>{title}</h2>
|
144 |
+
<a class='link' href='{link}'>{title}</a>
|
145 |
+
<span class='description'>{description}</span>
|
146 |
+
"""
|
147 |
+
|
148 |
+
def entry_to_html(entry_id, **kwargs):
|
149 |
+
new_kwargs = flatten_unicode_keys(kwargs)
|
150 |
+
new_kwargs.setdefault('title', 'No title')
|
151 |
+
new_kwargs.setdefault('link', 'No link')
|
152 |
+
new_kwargs.setdefault('description', '')
|
153 |
+
accordion_item = f"""
|
154 |
+
<div class="card">
|
155 |
+
<div class="card-header" id="heading{entry_id}">
|
156 |
+
<h5 class="mb-0">
|
157 |
+
<button class="btn btn-link" data-toggle="collapse" data-target="#collapse{entry_id}" aria-expanded="true" aria-controls="collapse{entry_id}">
|
158 |
+
{new_kwargs['title']}
|
159 |
+
</button>
|
160 |
+
</h5>
|
161 |
+
</div>
|
162 |
+
<div id="collapse{entry_id}" class="collapse {'show' if entry_id == 0 else ''}" aria-labelledby="heading{entry_id}" data-parent="#accordion">
|
163 |
+
<div class="card-body">
|
164 |
+
<a href='{new_kwargs['link']}' target='_blank'>{new_kwargs['title']}</a>
|
165 |
+
<p>{new_kwargs['description']}</p>
|
166 |
+
</div>
|
167 |
+
</div>
|
168 |
+
</div>
|
169 |
+
"""
|
170 |
+
return accordion_item
|
171 |
+
|
172 |
+
def convert_feed_to_accordion(url):
|
173 |
+
feed = parse_feed(url)
|
174 |
+
accordion_html = '<div id="accordion">'
|
175 |
+
for i, entry in enumerate(feed.entries):
|
176 |
+
accordion_html += entry_to_html(i, **entry)
|
177 |
+
accordion_html += '</div>'
|
178 |
+
return accordion_html
|
179 |
+
|
180 |
+
# Bootstrap CSS and JS must be included in the output HTML to style the accordion correctly
|
181 |
+
bootstrap_includes = """
|
182 |
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
|
183 |
+
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
184 |
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
185 |
+
"""
|
186 |
+
|
187 |
+
# Fetch and display the accordion
|
188 |
+
url = f"https://news.google.com/rss/search?q={stock_symbol}&hl=en-US&gl=US&ceid=US%3Aen"
|
189 |
+
# print(url)
|
190 |
+
feed_accordion_html = bootstrap_includes + convert_feed_to_accordion(url)
|
191 |
+
st.markdown(f"Latest news on **{stock_symbol}**")
|
192 |
+
components.html(feed_accordion_html, height=800, scrolling=True)
|
193 |
+
|
194 |
+
# def convert_feed_to_raw_data(url):
|
195 |
+
# feed = parse_feed(url)
|
196 |
+
# raw_data = [entry for entry in feed.entries]
|
197 |
+
# return raw_data
|
198 |
+
|
199 |
+
# Fetch and display the raw RSS data
|
200 |
+
# url = f"https://news.google.com/rss/search?q={stock_symbol}&hl=en-US&gl=US&ceid=US%3Aen"
|
201 |
+
# raw_data = convert_feed_to_raw_data(url)
|
202 |
+
# print(raw_data)
|
203 |
+
# st.write(raw_data)
|