Space14 / app.py
QuantumLearner's picture
Update app.py
306e37a verified
import streamlit as st
import yfinance as yf
import numpy as np
from ripser import Rips
import persim
import plotly.graph_objs as go
import warnings
import pandas as pd
# Function to fetch stock or crypto data
def fetch_data(ticker_name, start_date, end_date):
raw_data = yf.download(ticker_name, start=start_date, end=end_date)
adjusted_close = raw_data['Adj Close'].dropna()
prices = adjusted_close.values
log_returns = np.log(prices[1:] / prices[:-1])
return adjusted_close, log_returns
# Function to compute Wasserstein distances
def compute_wasserstein_distances(log_returns, window_size, rips):
n = len(log_returns) - (2 * window_size) + 1
distances = np.full((n, 1), np.nan)
for i in range(n):
segment1 = log_returns[i:i+window_size].reshape(-1, 1)
segment2 = log_returns[i+window_size:i+(2*window_size)].reshape(-1, 1)
if segment1.shape[0] != window_size or segment2.shape[0] != window_size:
continue
dgm1 = rips.fit_transform(segment1)
dgm2 = rips.fit_transform(segment2)
distance = persim.wasserstein(dgm1[0], dgm2[0], matching=False)
distances[i] = distance
return distances
# Streamlit app configuration
st.set_page_config(layout="wide")
st.title("Market Crash Analysis with Topology")
st.write("""
This application analyzes asset price data using Wasserstein distances to detect changes in price dynamics over time.
Wasserstein distances, derived from persistence diagrams in Topological Data Analysis (TDA), help identify significant shifts in asset price behaviors for both stocks and cryptocurrencies.
""")
with st.expander("Wasserstein Distance Methodology", expanded=False):
# Explanation of the Wasserstein Distance method
st.subheader("Wasserstein Distance Methodology")
st.write("""
The Wasserstein distance is a measure from optimal transport theory, used here to compare distributions of log returns in different time windows.
A high Wasserstein distance indicates a significant change in the price dynamics, which might suggest a market event or shift in investor sentiment.
""")
st.latex(r'''
W(P, Q) = \inf_{\gamma \in \Pi(P, Q)} \mathbb{E}_{(x,y) \sim \gamma} [d(x, y)]
''')
st.write("""
- Where \( W(P, Q) \) is the Wasserstein distance between distributions \( P \) and \( Q \).
- \( d(x, y) \) is the distance between points \( x \) and \( y \).
- \( \gamma \) is a joint distribution with marginals \( P \) and \( Q \).
""")
# Interpretation of results
st.subheader("Interpretation of Results")
st.write("""
**Wasserstein Distance Analysis:**
The Wasserstein distance quantifies changes in the log returns of asset prices over time.
A high distance indicates a significant shift in price dynamics, potentially due to a market event or a change in investor behavior.
""")
st.sidebar.title("""
Input Parameters
""")
#st.write(f"Threshold: {threshold}")
# Sidebar for "How to Use" instructions inside an expander, closed by default
with st.sidebar.expander("How to Use", expanded=False):
st.write("""
**How to use this app:**
1. Enter the stock or crypto ticker symbol (e.g., `^GSPC` for S&P 500 or `BTC-USD` for Bitcoin).
2. Specify the start and end dates for the analysis period.
3. Adjust the window size for the sliding window analysis.
4. Set the alert threshold for detecting significant changes in price dynamics.
5. Click 'Run Analysis' to start.
""")
# Input parameters inside an expander, open by default
with st.sidebar.expander("Input Parameters", expanded=True):
ticker_name = st.text_input('Enter Stock or Crypto Symbol (e.g., AAPL or BTC-USD)', '^GSPC', help="Enter the ticker symbol for the stock or cryptocurrency you want to analyze.")
start_date_string = st.date_input('Start Date', pd.to_datetime('2020-01-01'), help="Select the start date for the data range.")
end_date_string = st.date_input('End Date', pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)), help="Select the end date for the data range.")
# Parameters for the selected method inside an expander, open by default
with st.sidebar.expander("Parameters", expanded=True):
window_size = st.slider('Window Size', min_value=5, max_value=50, value=20, help="Set the window size for the sliding window analysis.")
threshold = st.slider('Alert Threshold', min_value=0.02, max_value=0.2, value=0.075, step=0.005, help="Set the threshold for detecting significant changes in price dynamics.")
# Run Analysis button in the sidebar
if st.sidebar.button('Run Analysis'):
st.write(f"Analyzing {ticker_name} from {start_date_string} to {end_date_string} with window size {window_size} and threshold {threshold}")
# Fetch data
prices, log_returns = fetch_data(ticker_name, start_date_string, end_date_string)
rips = Rips(maxdim=2)
wasserstein_dists = compute_wasserstein_distances(log_returns, window_size, rips)
# Plotting with Plotly
dates = prices.index[window_size:-window_size]
valid_indices = ~np.isnan(wasserstein_dists.flatten())
valid_dates = dates[valid_indices]
valid_distances = wasserstein_dists[valid_indices].flatten()
alert_indices = [i for i, d in enumerate(valid_distances) if d > threshold]
alert_dates = [valid_dates[i] for i in alert_indices]
alert_values = [prices.iloc[i + window_size] for i in alert_indices]
# Plot price and alerts
fig = go.Figure()
fig.add_trace(go.Scatter(x=valid_dates, y=prices.iloc[window_size:-window_size], mode='lines', name='Price'))
fig.add_trace(go.Scatter(x=alert_dates, y=alert_values, mode='markers', name='Alert', marker=dict(color='red', size=8)))
fig.update_layout(title=f'{ticker_name} Prices Over Time', xaxis_title='Date', yaxis_title='Price')
st.plotly_chart(fig, use_container_width=True)
# Plot Wasserstein distances
fig = go.Figure()
fig.add_trace(go.Scatter(x=valid_dates, y=valid_distances, mode='lines', name='Wasserstein Distance', line=dict(color='blue', width=2)))
fig.add_hline(y=threshold, line_dash='dash', line_color='red', annotation_text=f'Threshold: {threshold}', annotation_position='bottom right')
fig.update_layout(title='Wasserstein Distances Over Time', xaxis_title='Date', yaxis_title='Wasserstein Distance')
st.plotly_chart(fig, use_container_width=True)
st.write("""
**Plot Interpretation:**
- The first plot shows the asset price over time with alerts marked in red.
- The second plot displays the Wasserstein distances over time, with the threshold indicated by a dashed red line. Peaks above this line represent significant changes in price dynamics.
""")
st.markdown(
"""
<style>
/* Adjust the width of the sidebar */
[data-testid="stSidebar"] {
width: 500px; /* Change this value to set the width you want */
}
</style>
""",
unsafe_allow_html=True
)
# Hide the default Streamlit menu and footer
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)