QuantumLearner commited on
Commit
787c3fa
·
verified ·
1 Parent(s): b678e2f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -189
app.py CHANGED
@@ -27,15 +27,20 @@ with st.sidebar.expander("How to Use", expanded=False):
27
  # Sidebar: Assets and Dates (open by default)
28
  with st.sidebar.expander("Assets and Dates", expanded=True):
29
  tickers = st.text_area("Asset Symbols (Crypto-Pair or Stock Ticker) (comma-separated)",
30
- value="BTC-USD,ETH-USD,BNB-USD,ADA-USD,SOL-USD,DOT-USD,DOGE-USD,AVAX-USD,MATIC-USD,LTC-USD,LUNA1-USD,LINK-USD,ALGO-USD,ATOM-USD,FTT-USD,TRX-USD,ETC-USD,FIL-USD,XMR-USD,XLM-USD").split(",")
31
- start_date = st.date_input("Start Date", value=pd.to_datetime("2020-01-01"))
32
- end_date = st.date_input("End Date", value=pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)))
 
 
 
33
 
34
  # Sidebar: Market Index and Correlation (open by default)
35
  with st.sidebar.expander("Market Index and Correlation", expanded=True):
36
- market_index = st.text_input("Market Index Ticker", value="BTC-USD")
 
37
  correlation_threshold = st.slider("Correlation Threshold (for Network Analysis)",
38
- min_value=0.0, max_value=1.0, value=0.75, step=0.05)
 
39
 
40
  # Run Analysis button
41
  run_button = st.sidebar.button("Run Analysis")
@@ -52,199 +57,205 @@ if run_button:
52
  data = data.fillna(method='ffill').dropna()
53
  data = data.replace([np.inf, -np.inf], np.nan).dropna()
54
 
55
- st.markdown("### Asset Prices Reindexed to Start at 0")
56
- st.markdown("Reindexed asset prices to compare their relative movements over time.")
57
-
58
- data_reindexed = data.apply(lambda x: x / x.iloc[0])
59
- fig = go.Figure()
60
 
61
- for ticker in tickers:
62
- fig.add_trace(go.Scatter(x=data_reindexed.index, y=data_reindexed[ticker], mode='lines', name=ticker))
63
 
64
- fig.update_layout(
65
- title="Asset Prices Reindexed to Start at 0",
66
- xaxis_title="Date",
67
- yaxis_title="Reindexed Price",
68
- template="plotly_dark"
69
- )
70
- st.plotly_chart(fig, use_container_width=True)
71
-
72
- # Calculating daily returns
73
- returns = data.pct_change().dropna()
74
-
75
- # Ensure no inf or NaN values in returns
76
- returns = returns.replace([np.inf, -np.inf], np.nan).dropna()
77
-
78
- st.markdown("### Kalman Filter: Estimated Common Factor and Asset Returns")
79
- st.markdown("Using the Kalman Filter to estimate a common factor influencing all asset returns.")
80
-
81
- # Methodology in expander
82
- with st.expander("Kalman Filter Methodology", expanded=False):
83
- st.markdown("The Kalman Filter operates based on the following state-space model:")
84
- st.latex(r"""
85
- \text{State Equation:} \quad \mathbf{x}_t = \mathbf{A} \mathbf{x}_{t-1} + \mathbf{w}_t
86
- """)
87
- st.latex(r"""
88
- \text{Observation Equation:} \quad \mathbf{y}_t = \mathbf{H} \mathbf{x}_t + \mathbf{v}_t
89
- """)
90
- st.markdown("""
91
- Where:
92
- - \(xt\) is the state vector (the common factor we are estimating).
93
- - \(A\) is the state transition matrix (set to the identity matrix \(I\)).
94
- - \(wt\) is the process noise (with covariance \(Q\)).
95
- - \(yt\) is the observation vector (asset returns).
96
- - \(H\) is the observation matrix (set to a vector of ones).
97
- - \(vt\) is the observation noise (with covariance \(R\)).
98
- """)
99
-
100
- observations = returns.values
101
- initial_state_mean = np.zeros(1)
102
- kf = KalmanFilter(
103
- transition_matrices=np.eye(1),
104
- observation_matrices=np.ones((len(tickers), 1)),
105
- initial_state_mean=initial_state_mean,
106
- observation_covariance=np.eye(len(tickers)),
107
- transition_covariance=np.eye(1)
108
- )
109
-
110
- state_means, state_covariances = kf.em(observations).filter(observations)
111
- fig = go.Figure()
112
-
113
- for i, ticker in enumerate(tickers):
114
- fig.add_trace(go.Scatter(x=returns.index, y=observations[:, i], mode='lines', name=f'{ticker} Returns'))
115
 
116
- fig.add_trace(go.Scatter(x=returns.index, y=state_means[:, 0], mode='lines', name='Estimated Common Factor', line=dict(color='red', width=4)))
117
- fig.update_layout(
118
- title='Kalman Filter: Estimated Common Factor and Asset Returns',
119
- xaxis_title='Date',
120
- yaxis_title='Returns',
121
- template='plotly_dark'
122
- )
123
- st.plotly_chart(fig, use_container_width=True)
124
-
125
- st.markdown("### CSSD and CSAD Calculations")
126
- st.markdown("Calculating the Cross-Sectional Standard Deviation (CSSD) and Cross-Sectional Absolute Deviation (CSAD) of asset returns.")
127
-
128
- # Methodology in expander
129
- with st.expander("CSSD and CSAD Methodology", expanded=False):
130
- st.markdown("The formulas for CSSD and CSAD are as follows:")
131
- st.markdown("**CSSD (Cross-Sectional Standard Deviation):**")
132
- st.latex(r"\text{CSSD}_t = \sqrt{\frac{\sum_{i=1}^{N} (R_{i,t} - \overline{R}_t)^2}{N - 1}}")
133
- st.markdown("**CSAD (Cross-Sectional Absolute Deviation):**")
134
- st.latex(r"\text{CSAD}_t = \frac{\sum_{i=1}^{N} |R_{i,t} - \overline{R}_t|}{N}")
135
-
136
- market_return = returns[market_index]
137
- returns = returns.drop(columns=[market_index])
138
-
139
- def calculate_cssd(returns, market_return):
140
- cssd = np.sqrt(((returns - market_return[:, None]) ** 2).sum(axis=1) / (returns.shape[1] - 1))
141
- return cssd
142
-
143
- def calculate_csad(returns, market_return):
144
- csad = (np.abs(returns - market_return[:, None]).sum(axis=1)) / returns.shape[1]
145
- return csad
146
-
147
- cssd = calculate_cssd(returns.values, market_return.values)
148
- csad = calculate_csad(returns.values, market_return.values)
149
-
150
- window_size = 30
151
-
152
- def rolling_csad(stock_returns, market_returns, window):
153
- csad_values = []
154
- for i in range(len(stock_returns) - window + 1):
155
- window_data = stock_returns.iloc[i:i+window]
156
- window_market = market_returns[i:i+window].mean()
157
- N = len(window_data.columns)
158
- csad = (1 / N) * np.sum(np.abs(window_data.sub(window_market, axis=0)).mean(axis=1))
159
- csad_values.append(csad)
160
- return pd.Series(csad_values, index=stock_returns.index[window-1:])
161
-
162
- def rolling_cssd(stock_returns, market_returns, window):
163
- cssd_values = []
164
- for i in range(len(stock_returns) - window + 1):
165
- window_data = stock_returns.iloc[i:i+window]
166
- window_market = market_returns[i:i+window].mean()
167
- N = len(window_data.columns)
168
- cssd = np.sqrt((1 / (N - 1)) * np.sum(np.square(window_data.sub(window_market, axis=0)).mean(axis=1)))
169
- cssd_values.append(cssd)
170
- return pd.Series(cssd_values, index=stock_returns.index[window-1:])
171
-
172
- rolling_csad_values = rolling_csad(returns, market_return, window_size)
173
- rolling_cssd_values = rolling_cssd(returns, market_return, window_size)
174
-
175
- fig = go.Figure()
176
- fig.add_trace(go.Scatter(x=returns.index, y=cssd, mode='lines', name='CSSD', line=dict(color='blue')))
177
- fig.add_trace(go.Scatter(x=returns.index, y=csad, mode='lines', name='CSAD', line=dict(color='red')))
178
- fig.add_trace(go.Scatter(x=rolling_csad_values.index, y=rolling_csad_values, mode='lines', name='Rolling CSAD (30 days)', line=dict(color='orange')))
179
- fig.add_trace(go.Scatter(x=rolling_cssd_values.index, y=rolling_cssd_values, mode='lines', name='Rolling CSSD (30 days)', line=dict(color='green')))
180
- fig.update_layout(
181
- title='CSSD, CSAD, Rolling CSAD, and Rolling CSSD over Time',
182
- xaxis_title='Date',
183
- yaxis_title='Value',
184
- template='plotly_dark'
185
- )
186
- st.plotly_chart(fig, use_container_width=True)
187
 
188
- st.markdown("### Network Visualization of Asset Correlations")
189
- st.markdown("Visualizing the correlations between assets as a network. Assets are connected by edges if their correlation exceeds a threshold.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
- years = data.index.year.unique()
 
192
 
193
- def plot_network_for_year(data_for_year, year, threshold):
194
- corr_matrix = data_for_year.corr()
195
- G = nx.Graph()
196
 
197
- for ticker in tickers:
198
- G.add_node(ticker)
199
-
200
- for i in range(len(tickers)):
201
- for j in range(i+1, len(tickers)):
202
- if abs(corr_matrix.iloc[i, j]) > threshold:
203
- G.add_edge(tickers[i], tickers[j], weight=corr_matrix.iloc[i, j])
204
-
205
- pos = nx.spring_layout(G)
206
- edge_trace = []
207
-
208
- for edge in G.edges(data=True):
209
- x0, y0 = pos[edge[0]]
210
- x1, y1 = pos[edge[1]]
211
- trace = go.Scatter(
212
- x=[x0, x1, None],
213
- y=[y0, y1, None],
214
- line=dict(width=2, color='blue'),
215
- hoverinfo='none',
216
- mode='lines'
217
  )
218
- edge_trace.append(trace)
219
-
220
- node_trace = go.Scatter(
221
- x=[pos[node][0] for node in G.nodes()],
222
- y=[pos[node][1] for node in G.nodes()],
223
- text=[node for node in G.nodes()],
224
- mode='markers+text',
225
- textposition='top center',
226
- hoverinfo='text',
227
- marker=dict(
228
- size=10,
229
- color='red',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  )
231
- )
232
-
233
- layout = go.Layout(
234
- title=f'Asset Correlation Network for {year}',
235
- showlegend=False,
236
- hovermode='closest',
237
- margin=dict(b=20, l=5, r=5, t=40),
238
- xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
239
- yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
240
- )
241
-
242
- fig = go.Figure(data=edge_trace + [node_trace], layout=layout)
243
- st.plotly_chart(fig, use_container_width=True)
244
-
245
- for year in years:
246
- data_for_year = data[data.index.year == year]
247
- plot_network_for_year(data_for_year, year, correlation_threshold)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  hide_streamlit_style = """
250
  <style>
 
27
  # Sidebar: Assets and Dates (open by default)
28
  with st.sidebar.expander("Assets and Dates", expanded=True):
29
  tickers = st.text_area("Asset Symbols (Crypto-Pair or Stock Ticker) (comma-separated)",
30
+ value="BTC-USD,ETH-USD,BNB-USD,ADA-USD,SOL-USD,DOT-USD,DOGE-USD,AVAX-USD,MATIC-USD,LTC-USD,LUNA1-USD,LINK-USD,ALGO-USD,ATOM-USD,FTT-USD,TRX-USD,ETC-USD,FIL-USD,XMR-USD,XLM-USD",
31
+ help="Enter the ticker symbols for the assets you want to analyze, separated by commas. E.g., 'BTC-USD, ETH-USD'.").split(",")
32
+ start_date = st.date_input("Start Date", value=pd.to_datetime("2020-01-01"),
33
+ help="Select the start date for the analysis period.")
34
+ end_date = st.date_input("End Date", value=pd.to_datetime(pd.Timestamp.now().date() + pd.Timedelta(days=1)),
35
+ help="Select the end date for the analysis period.")
36
 
37
  # Sidebar: Market Index and Correlation (open by default)
38
  with st.sidebar.expander("Market Index and Correlation", expanded=True):
39
+ market_index = st.text_input("Market Index Ticker", value="BTC-USD",
40
+ help="Enter the ticker symbol for the market index, e.g., 'BTC-USD'.")
41
  correlation_threshold = st.slider("Correlation Threshold (for Network Analysis)",
42
+ min_value=0.0, max_value=1.0, value=0.75, step=0.05,
43
+ help="Set the threshold for correlation in the network analysis.")
44
 
45
  # Run Analysis button
46
  run_button = st.sidebar.button("Run Analysis")
 
57
  data = data.fillna(method='ffill').dropna()
58
  data = data.replace([np.inf, -np.inf], np.nan).dropna()
59
 
60
+ if data.empty:
61
+ st.warning("No data available for the given assets and date range.")
62
+ else:
63
+ st.markdown("### Asset Prices Reindexed to Start at 0")
64
+ st.markdown("Reindexed asset prices to compare their relative movements over time.")
65
 
66
+ data_reindexed = data.apply(lambda x: x / x.iloc[0])
67
+ fig = go.Figure()
68
 
69
+ for ticker in tickers:
70
+ fig.add_trace(go.Scatter(x=data_reindexed.index, y=data_reindexed[ticker], mode='lines', name=ticker))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ fig.update_layout(
73
+ title="Asset Prices Reindexed to Start at 0",
74
+ xaxis_title="Date",
75
+ yaxis_title="Reindexed Price",
76
+ template="plotly_dark"
77
+ )
78
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ # Calculating daily returns
81
+ returns = data.pct_change().dropna()
82
+
83
+ # Ensure no inf or NaN values in returns
84
+ returns = returns.replace([np.inf, -np.inf], np.nan).dropna()
85
+
86
+ if returns.empty or len(returns) <= 1:
87
+ st.warning("Not enough data to perform further analysis.")
88
+ else:
89
+ st.markdown("### Kalman Filter: Estimated Common Factor and Asset Returns")
90
+ st.markdown("Using the Kalman Filter to estimate a common factor influencing all asset returns.")
91
+
92
+ # Methodology in expander
93
+ with st.expander("Kalman Filter Methodology", expanded=False):
94
+ st.markdown("The Kalman Filter operates based on the following state-space model:")
95
+ st.latex(r"""
96
+ \text{State Equation:} \quad \mathbf{x}_t = \mathbf{A} \mathbf{x}_{t-1} + \mathbf{w}_t
97
+ """)
98
+ st.latex(r"""
99
+ \text{Observation Equation:} \quad \mathbf{y}_t = \mathbf{H} \mathbf{x}_t + \mathbf{v}_t
100
+ """)
101
+ st.markdown("""
102
+ Where:
103
+ - \(xt\) is the state vector (the common factor we are estimating).
104
+ - \(A\) is the state transition matrix (set to the identity matrix \(I\)).
105
+ - \(wt\) is the process noise (with covariance \(Q\)).
106
+ - \(yt\) is the observation vector (asset returns).
107
+ - \(H\) is the observation matrix (set to a vector of ones).
108
+ - \(vt\) is the observation noise (with covariance \(R\)).
109
+ """)
110
+
111
+ observations = returns.values
112
+ initial_state_mean = np.zeros(1)
113
+ kf = KalmanFilter(
114
+ transition_matrices=np.eye(1),
115
+ observation_matrices=np.ones((len(tickers), 1)),
116
+ initial_state_mean=initial_state_mean,
117
+ observation_covariance=np.eye(len(tickers)),
118
+ transition_covariance=np.eye(1)
119
+ )
120
 
121
+ state_means, state_covariances = kf.em(observations).filter(observations)
122
+ fig = go.Figure()
123
 
124
+ for i, ticker in enumerate(tickers):
125
+ fig.add_trace(go.Scatter(x=returns.index, y=observations[:, i], mode='lines', name=f'{ticker} Returns'))
 
126
 
127
+ fig.add_trace(go.Scatter(x=returns.index, y=state_means[:, 0], mode='lines', name='Estimated Common Factor', line=dict(color='red', width=4)))
128
+ fig.update_layout(
129
+ title='Kalman Filter: Estimated Common Factor and Asset Returns',
130
+ xaxis_title='Date',
131
+ yaxis_title='Returns',
132
+ template='plotly_dark'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  )
134
+ st.plotly_chart(fig, use_container_width=True)
135
+
136
+ st.markdown("### CSSD and CSAD Calculations")
137
+ st.markdown("Calculating the Cross-Sectional Standard Deviation (CSSD) and Cross-Sectional Absolute Deviation (CSAD) of asset returns.")
138
+
139
+ # Methodology in expander
140
+ with st.expander("CSSD and CSAD Methodology", expanded=False):
141
+ st.markdown("The formulas for CSSD and CSAD are as follows:")
142
+ st.markdown("**CSSD (Cross-Sectional Standard Deviation):**")
143
+ st.latex(r"\text{CSSD}_t = \sqrt{\frac{\sum_{i=1}^{N} (R_{i,t} - \overline{R}_t)^2}{N - 1}}")
144
+ st.markdown("**CSAD (Cross-Sectional Absolute Deviation):**")
145
+ st.latex(r"\text{CSAD}_t = \frac{\sum_{i=1}^{N} |R_{i,t} - \overline{R}_t|}{N}")
146
+
147
+ market_return = returns[market_index]
148
+ returns = returns.drop(columns=[market_index])
149
+
150
+ def calculate_cssd(returns, market_return):
151
+ cssd = np.sqrt(((returns - market_return[:, None]) ** 2).sum(axis=1) / (returns.shape[1] - 1))
152
+ return cssd
153
+
154
+ def calculate_csad(returns, market_return):
155
+ csad = (np.abs(returns - market_return[:, None]).sum(axis=1)) / returns.shape[1]
156
+ return csad
157
+
158
+ cssd = calculate_cssd(returns.values, market_return.values)
159
+ csad = calculate_csad(returns.values, market_return.values)
160
+
161
+ window_size = 30
162
+
163
+ def rolling_csad(stock_returns, market_returns, window):
164
+ csad_values = []
165
+ for i in range(len(stock_returns) - window + 1):
166
+ window_data = stock_returns.iloc[i:i+window]
167
+ window_market = market_returns[i:i+window].mean()
168
+ N = len(window_data.columns)
169
+ csad = (1 / N) * np.sum(np.abs(window_data.sub(window_market, axis=0)).mean(axis=1))
170
+ csad_values.append(csad)
171
+ return pd.Series(csad_values, index=stock_returns.index[window-1:])
172
+
173
+ def rolling_cssd(stock_returns, market_returns, window):
174
+ cssd_values = []
175
+ for i in range(len(stock_returns) - window + 1):
176
+ window_data = stock_returns.iloc[i:i+window]
177
+ window_market = market_returns[i:i+window].mean()
178
+ N = len(window_data.columns)
179
+ cssd = np.sqrt((1 / (N - 1)) * np.sum(np.square(window_data.sub(window_market, axis=0)).mean(axis=1)))
180
+ cssd_values.append(cssd)
181
+ return pd.Series(cssd_values, index=stock_returns.index[window-1:])
182
+
183
+ rolling_csad_values = rolling_csad(returns, market_return, window_size)
184
+ rolling_cssd_values = rolling_cssd(returns, market_return, window_size)
185
+
186
+ fig = go.Figure()
187
+ fig.add_trace(go.Scatter(x=returns.index, y=cssd, mode='lines', name='CSSD', line=dict(color='blue')))
188
+ fig.add_trace(go.Scatter(x=returns.index, y=csad, mode='lines', name='CSAD', line=dict(color='red')))
189
+ fig.add_trace(go.Scatter(x=rolling_csad_values.index, y=rolling_csad_values, mode='lines', name='Rolling CSAD (30 days)', line=dict(color='orange')))
190
+ fig.add_trace(go.Scatter(x=rolling_cssd_values.index, y=rolling_cssd_values, mode='lines', name='Rolling CSSD (30 days)', line=dict(color='green')))
191
+ fig.update_layout(
192
+ title='CSSD, CSAD, Rolling CSAD, and Rolling CSSD over Time',
193
+ xaxis_title='Date',
194
+ yaxis_title='Value',
195
+ template='plotly_dark'
196
  )
197
+ st.plotly_chart(fig, use_container_width=True)
198
+
199
+ st.markdown("### Network Visualization of Asset Correlations")
200
+ st.markdown("Visualizing the correlations between assets as a network.")
201
+
202
+ years = data.index.year.unique()
203
+
204
+ def plot_network_for_year(data_for_year, year, threshold):
205
+ corr_matrix = data_for_year.corr()
206
+ G = nx.Graph()
207
+
208
+ for ticker in tickers:
209
+ G.add_node(ticker)
210
+
211
+ for i in range(len(tickers)):
212
+ for j in range(i+1, len(tickers)):
213
+ if abs(corr_matrix.iloc[i, j]) > threshold:
214
+ G.add_edge(tickers[i], tickers[j], weight=corr_matrix.iloc[i, j])
215
+
216
+ pos = nx.spring_layout(G)
217
+ edge_trace = []
218
+
219
+ for edge in G.edges(data=True):
220
+ x0, y0 = pos[edge[0]]
221
+ x1, y1 = pos[edge[1]]
222
+ trace = go.Scatter(
223
+ x=[x0, x1, None],
224
+ y=[y0, y1, None],
225
+ line=dict(width=2, color='blue'),
226
+ hoverinfo='none',
227
+ mode='lines'
228
+ )
229
+ edge_trace.append(trace)
230
+
231
+ node_trace = go.Scatter(
232
+ x=[pos[node][0] for node in G.nodes()],
233
+ y=[pos[node][1] for node in G.nodes()],
234
+ text=[node for node in G.nodes()],
235
+ mode='markers+text',
236
+ textposition='top center',
237
+ hoverinfo='text',
238
+ marker=dict(
239
+ size=10,
240
+ color='red',
241
+ )
242
+ )
243
+
244
+ layout = go.Layout(
245
+ title=f'Asset Correlation Network for {year}',
246
+ showlegend=False,
247
+ hovermode='closest',
248
+ margin=dict(b=20, l=5, r=5, t=40),
249
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
250
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
251
+ )
252
+
253
+ fig = go.Figure(data=edge_trace + [node_trace], layout=layout)
254
+ st.plotly_chart(fig, use_container_width=True)
255
+
256
+ for year in years:
257
+ data_for_year = data[data.index.year == year]
258
+ plot_network_for_year(data_for_year, year, correlation_threshold)
259
 
260
  hide_streamlit_style = """
261
  <style>