QuantumLearner commited on
Commit
2017ea2
·
verified ·
1 Parent(s): b4cf3fc

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -0
app.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import yfinance as yf
3
+ import numpy as np
4
+ from ripser import Rips
5
+ import persim
6
+ import plotly.graph_objs as go
7
+ import warnings
8
+
9
+ # Function to fetch stock data
10
+ def fetch_data(ticker_name, start_date, end_date):
11
+ raw_data = yf.download(ticker_name, start=start_date, end=end_date)
12
+ adjusted_close = raw_data['Adj Close'].dropna()
13
+ prices = adjusted_close.values
14
+ log_returns = np.log(prices[1:] / prices[:-1])
15
+ return adjusted_close, log_returns
16
+
17
+ # Function to compute Wasserstein distances
18
+ def compute_wasserstein_distances(log_returns, window_size, rips):
19
+ n = len(log_returns) - (2 * window_size) + 1
20
+ distances = np.full((n, 1), np.nan)
21
+
22
+ for i in range(n):
23
+ segment1 = log_returns[i:i+window_size].reshape(-1, 1)
24
+ segment2 = log_returns[i+window_size:i+(2*window_size)].reshape(-1, 1)
25
+
26
+ if segment1.shape[0] != window_size or segment2.shape[0] != window_size:
27
+ continue
28
+
29
+ dgm1 = rips.fit_transform(segment1)
30
+ dgm2 = rips.fit_transform(segment2)
31
+ distance = persim.wasserstein(dgm1[0], dgm2[0], matching=False)
32
+ distances[i] = distance
33
+
34
+ return distances
35
+
36
+ # Streamlit app
37
+ def main():
38
+ st.set_page_config(layout="wide")
39
+
40
+ st.title('Stock Analysis Using Topological Data Analysis')
41
+ st.write("""
42
+ This application analyzes stock data using Wasserstein distances to detect changes in price dynamics over time.
43
+ Wasserstein distances, derived from persistence diagrams in Topological Data Analysis (TDA), help identify significant shifts in stock price behaviors.
44
+ """)
45
+
46
+ st.sidebar.title('Parameters')
47
+
48
+ # Input fields for user
49
+ ticker_name = st.sidebar.text_input('Enter Ticker Symbol', '^GSPC')
50
+ start_date_string = st.sidebar.text_input('Start Date (YYYY-MM-DD)', '2020-01-01')
51
+ end_date_string = st.sidebar.text_input('End Date (YYYY-MM-DD)', '2025-01-01')
52
+ window_size = st.sidebar.slider('Window Size', min_value=5, max_value=50, value=20)
53
+ threshold = st.sidebar.slider('Alert Threshold', min_value=0.05, max_value=0.2, value=0.075, step=0.005)
54
+
55
+ st.sidebar.write("""
56
+ **How to use:**
57
+ 1. Enter the stock ticker symbol (e.g., `^GSPC` for S&P 500).
58
+ 2. Specify the start and end dates for the analysis period.
59
+ 3. Adjust the window size for the sliding window analysis.
60
+ 4. Set the alert threshold for detecting significant changes.
61
+ 5. Click 'Run Analysis' to start.
62
+ """)
63
+
64
+ if st.sidebar.button('Run Analysis'):
65
+ st.write(f"Analyzing {ticker_name} from {start_date_string} to {end_date_string} with window size {window_size} and threshold {threshold}")
66
+
67
+ # Fetch data
68
+ prices, log_returns = fetch_data(ticker_name, start_date_string, end_date_string)
69
+ rips = Rips(maxdim=2)
70
+ wasserstein_dists = compute_wasserstein_distances(log_returns, window_size, rips)
71
+
72
+ # Plotting with Plotly
73
+ dates = prices.index[window_size:-window_size]
74
+ valid_indices = ~np.isnan(wasserstein_dists.flatten())
75
+ valid_dates = dates[valid_indices]
76
+ valid_distances = wasserstein_dists[valid_indices].flatten()
77
+
78
+ alert_indices = [i for i, d in enumerate(valid_distances) if d > threshold]
79
+ alert_dates = [valid_dates[i] for i in alert_indices]
80
+ alert_values = [prices.iloc[i + window_size] for i in alert_indices]
81
+
82
+ # Plot price and alerts
83
+ fig = go.Figure()
84
+ fig.add_trace(go.Scatter(x=valid_dates, y=prices.iloc[window_size:-window_size], mode='lines', name='Price'))
85
+ fig.add_trace(go.Scatter(x=alert_dates, y=alert_values, mode='markers', name='Alert', marker=dict(color='red', size=8)))
86
+ fig.update_layout(title=f'{ticker_name} Prices Over Time', xaxis_title='Date', yaxis_title='Price')
87
+ st.plotly_chart(fig, use_container_width=True)
88
+
89
+ # Plot Wasserstein distances
90
+ fig = go.Figure()
91
+ fig.add_trace(go.Scatter(x=valid_dates, y=valid_distances, mode='lines', name='Wasserstein Distance', line=dict(color='blue', width=2)))
92
+ fig.add_hline(y=threshold, line_dash='dash', line_color='red', annotation_text=f'Threshold: {threshold}', annotation_position='bottom right')
93
+ fig.update_layout(title='Wasserstein Distances Over Time', xaxis_title='Date', yaxis_title='Wasserstein Distance')
94
+ st.plotly_chart(fig, use_container_width=True)
95
+
96
+ # Interpretation of results
97
+ st.subheader("Interpretation of Results")
98
+
99
+ st.write("""
100
+ **Wasserstein Distance Analysis:**
101
+ The Wasserstein distance measures the difference between two distributions. In this context, it quantifies changes in the log returns of stock prices over time.
102
+ A high Wasserstein distance indicates a significant change in the price dynamics, which might suggest a market event or shift in investor sentiment.
103
+ """)
104
+
105
+ st.latex(r'''
106
+ W(P, Q) = \inf_{\gamma \in \Pi(P, Q)} \mathbb{E}_{(x,y) \sim \gamma} [d(x, y)]
107
+ ''')
108
+
109
+ st.write("""
110
+ - Where \( W(P, Q) \) is the Wasserstein distance between distributions \( P \) and \( Q \).
111
+ - \( d(x, y) \) is the distance between points \( x \) and \( y \).
112
+ - \( \gamma \) is a joint distribution with marginals \( P \) and \( Q \).
113
+
114
+ **Alert Threshold:**
115
+ The alert threshold is set to identify significant changes in the Wasserstein distances. Alerts are triggered when the distance exceeds the threshold.
116
+ """)
117
+ st.write(f"Threshold: {threshold}")
118
+
119
+ st.write("""
120
+ **Plot Interpretation:**
121
+ - The first plot shows the stock price over time with alerts marked in red.
122
+ - 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.
123
+ """)
124
+
125
+ # Main function call
126
+ if __name__ == "__main__":
127
+ warnings.filterwarnings('ignore')
128
+ main()
129
+
130
+ hide_streamlit_style = """
131
+ <style>
132
+ #MainMenu {visibility: hidden;}
133
+ footer {visibility: hidden;}
134
+ </style>
135
+ """
136
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)