Spaces:
Sleeping
Sleeping
import streamlit as st | |
import yfinance as yf | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
import streamlit.components.v1 as components | |
# Set the page to wide mode and specify a title | |
st.set_page_config(layout="wide", page_title="Stock Insight Explorer", page_icon=":chart_with_upwards_trend:") | |
# Display the application title in the app itself | |
st.title('Stock Insight Explorer: Visual Analytics & Dividend Overview') | |
# Input field for stock symbol | |
stock_symbol = st.text_input('Enter Stock Symbol', value='JEPI') | |
# Fetch stock data | |
stock_data = yf.Ticker(stock_symbol) | |
# Get historical data for the past year | |
end_date = pd.Timestamp.today() | |
start_date = end_date - pd.DateOffset(years=1) | |
historical_data = stock_data.history(start=start_date, end=end_date) | |
# Display company name and current stock price in bold | |
company_name = stock_data.info['longName'] | |
current_price = historical_data['Close'].iloc[-1] | |
# st.markdown(f"**Company Name:** {company_name}") | |
# https://finance.yahoo.com/quote/JEPQ?.tsrc=fin-srch | |
company_url = f"https://finance.yahoo.com/quote/{stock_symbol}?.tsrc=fin-srch" | |
# Use HTML anchor tag to create a hyperlink | |
st.markdown(f"**Company Name:** <a href='{company_url}' target='_blank'>{company_name}</a>", unsafe_allow_html=True) | |
st.markdown(f"**Current Price:** ${current_price:.2f}") | |
# Creating 3 columns for the charts | |
col1, col2, col3, col4 = st.columns(4) | |
# Closing prices chart | |
with col1: | |
st.subheader('Closing Prices Over The Past Year') | |
st.line_chart(historical_data['Close']) | |
# Volume of stock trades chart | |
with col2: | |
st.subheader('Volume of Stock Trades Over The Past Year') | |
st.bar_chart(historical_data['Volume']) | |
# Calculate 20 day and 50 day moving averages | |
historical_data['20 Day MA'] = historical_data['Close'].rolling(window=20).mean() | |
historical_data['50 Day MA'] = historical_data['Close'].rolling(window=50).mean() | |
# Line chart for closing price, 20 day MA, and 50 day MA | |
with col3: | |
st.subheader('Closing Price and Moving Averages') | |
plt.figure(figsize=(10, 5)) | |
plt.plot(historical_data.index, historical_data['Close'], label='Close Price') | |
plt.plot(historical_data.index, historical_data['20 Day MA'], label='20 Day MA') | |
plt.plot(historical_data.index, historical_data['50 Day MA'], label='50 Day MA') | |
plt.legend() | |
plt.xlabel('Date') | |
plt.ylabel('Price') | |
plt.title('Closing Price, 20 Day MA, and 50 Day MA') | |
st.pyplot(plt) | |
# Get the dividend data | |
# Assuming stock_data is your Ticker object from yfinance | |
dividends = stock_data.dividends | |
# Check if there are any dividends | |
if not dividends.empty: | |
last_dividend_value = dividends.iloc[-1] # Get the last dividend value from the dividends history | |
else: | |
# Fallback to the lastDividendValue from the .info attribute if no historical dividends are found | |
last_dividend_value = stock_data.info.get('lastDividendValue', 0) | |
with col4: | |
st.subheader('Dividends') | |
markdown_output = "" # Initialize an empty string to accumulate messages | |
# Ensure dividends.index is in the correct format | |
dividends.index = pd.to_datetime(dividends.index).date | |
if not dividends.empty: | |
plt.figure(figsize=(10, 5)) | |
dividends.plot(kind='bar') | |
plt.title(f'Dividends of {stock_symbol} Over Time') | |
plt.ylabel('Dividend Amount') | |
plt.xlabel('Date') | |
plt.xticks(rotation=45) | |
plt.tight_layout() | |
st.pyplot(plt) | |
# Calculate total annual dividends | |
today = pd.to_datetime("today") | |
last_year_dividends = dividends.loc[dividends.index > (today - pd.DateOffset(years=1)).date()] | |
total_annual_dividends = last_year_dividends.sum() | |
if current_price and current_price > 0: | |
annual_dividend_yield = (total_annual_dividends / current_price) * 100 | |
markdown_output += f"**Annual Dividend Yield:** {annual_dividend_yield:.2f}%.\n" | |
else: | |
markdown_output += "Current price not available. Cannot calculate annual dividend yield.\n" | |
else: | |
markdown_output += "This stock does not have any dividend data available.\n" | |
total_annual_dividends = 0 # Define it as 0 if there are no dividends | |
if current_price and current_price > 0 and last_dividend_value: | |
dividend_yield = (last_dividend_value / current_price) * 100 | |
markdown_output += f"**Last Dividend Yield:** {dividend_yield:.2f}%, ${last_dividend_value:.2f} per share." | |
else: | |
markdown_output += "Cannot calculate last dividend yield." | |
# Display all collected information in one markdown output | |
st.markdown(markdown_output, unsafe_allow_html=True) | |
# components.iframe("https://duckduckgo.com/?q=ZIM&va=i&t=hd&iar=news&df=w&ia=news") | |
# components.iframe("https://news.google.com/rss/search?q=ZIM&hl=en-US&gl=US&ceid=US%3Aen", height=800, scrolling=True) | |
# components.iframe("https://truevis.com", height=800, scrolling=True) | |
from unicodedata import normalize | |
from bs4 import BeautifulSoup | |
from feedparser import parse as parse_feed | |
def flatten_unicode_keys(entry_properties): | |
"""Ensures passing unicode keywords to **kwargs.""" | |
new_properties = {} | |
for key, value in entry_properties.items(): | |
key_str = key.decode('utf-8') if isinstance(key, bytes) else key | |
new_key = normalize('NFKD', key_str).encode('ascii', 'ignore').decode('ascii') # Decode the bytes object to string | |
new_properties[new_key] = value | |
return new_properties | |
TEMPLATE = """ | |
<h2 class='title'>{title}</h2> | |
<a class='link' href='{link}'>{title}</a> | |
<span class='description'>{description}</span> | |
""" | |
def entry_to_html(entry_id, **kwargs): | |
new_kwargs = flatten_unicode_keys(kwargs) | |
new_kwargs.setdefault('title', 'No title') | |
new_kwargs.setdefault('link', 'No link') | |
new_kwargs.setdefault('description', '') | |
accordion_item = f""" | |
<div class="card"> | |
<div class="card-header" id="heading{entry_id}"> | |
<h5 class="mb-0"> | |
<button class="btn btn-link" data-toggle="collapse" data-target="#collapse{entry_id}" aria-expanded="true" aria-controls="collapse{entry_id}"> | |
{new_kwargs['title']} | |
</button> | |
</h5> | |
</div> | |
<div id="collapse{entry_id}" class="collapse {'show' if entry_id == 0 else ''}" aria-labelledby="heading{entry_id}" data-parent="#accordion"> | |
<div class="card-body"> | |
<a href='{new_kwargs['link']}' target='_blank'>{new_kwargs['title']}</a> | |
<p>{new_kwargs['description']}</p> | |
</div> | |
</div> | |
</div> | |
""" | |
return accordion_item | |
def convert_feed_to_accordion(url): | |
feed = parse_feed(url) | |
accordion_html = '<div id="accordion">' | |
for i, entry in enumerate(feed.entries): | |
accordion_html += entry_to_html(i, **entry) | |
accordion_html += '</div>' | |
return accordion_html | |
# Bootstrap CSS and JS must be included in the output HTML to style the accordion correctly | |
bootstrap_includes = """ | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> | |
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> | |
""" | |
# Fetch and display the accordion | |
url = f"https://news.google.com/rss/search?q={stock_symbol}&hl=en-US&gl=US&ceid=US%3Aen" | |
# print(url) | |
feed_accordion_html = bootstrap_includes + convert_feed_to_accordion(url) | |
st.markdown(f"Latest news on **{stock_symbol}**") | |
components.html(feed_accordion_html, height=800, scrolling=True) | |
# def convert_feed_to_raw_data(url): | |
# feed = parse_feed(url) | |
# raw_data = [entry for entry in feed.entries] | |
# return raw_data | |
# Fetch and display the raw RSS data | |
# url = f"https://news.google.com/rss/search?q={stock_symbol}&hl=en-US&gl=US&ceid=US%3Aen" | |
# raw_data = convert_feed_to_raw_data(url) | |
# print(raw_data) | |
# st.write(raw_data) |