QuantumLearner commited on
Commit
9375608
·
verified ·
1 Parent(s): d86df1b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +318 -0
app.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import yfinance as yf
3
+ import pandas as pd
4
+ import numpy as np
5
+ from pykalman import KalmanFilter
6
+ import plotly.graph_objs as go
7
+ import networkx as nx
8
+
9
+ # Streamlit app setup
10
+ st.set_page_config(layout="wide")
11
+
12
+ st.title("Herding Behaviour Analysis in Cryptocurrency Markets")
13
+
14
+ st.markdown(
15
+ """
16
+ This app analyzes herding behavior in cryptocurrency markets by examining price movements and correlations.
17
+ You can specify the cryptocurrency pairs, time period, and other parameters for the analysis.
18
+ The app includes various analyses such as price reindexing, Kalman Filter estimation of a common factor,
19
+ CSSD and CSAD calculations, rolling CSSD and CSAD, and network visualization of correlations.
20
+ """
21
+ )
22
+
23
+ st.sidebar.header("How to use")
24
+ st.sidebar.write(
25
+ """
26
+ 1. Select the cryptocurrency pairs.
27
+ 2. Choose the time period.
28
+ 3. Set additional parameters for the analyses.
29
+ 4. Click 'Run Analysis' to see the results.
30
+ """
31
+ )
32
+
33
+ # User inputs in the sidebar
34
+ tickers = st.sidebar.text_area("Asset Symbol (Crypto-Pair or Stock Ticker) (comma-separated)", 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(",")
35
+ start_date = st.sidebar.date_input("Start Date", value=pd.to_datetime("2020-01-01"))
36
+ end_date = st.sidebar.date_input("End Date", value=pd.to_datetime("2025-01-01"))
37
+ market_index = st.sidebar.text_input("Market Index Ticker", value="BTC-USD")
38
+ run_button = st.sidebar.button("Run Analysis")
39
+
40
+ if run_button:
41
+ # Include market index in the tickers list if not already present
42
+ if market_index not in tickers:
43
+ tickers.append(market_index)
44
+
45
+ # Fetching cryptocurrency data
46
+ data = yf.download(tickers, start=start_date, end=end_date)['Close']
47
+
48
+ # Clean the data by filling or dropping NaN and infinite values
49
+ data = data.fillna(method='ffill').dropna()
50
+ data = data.replace([np.inf, -np.inf], np.nan).dropna()
51
+
52
+ # Reindexing prices to start at 0
53
+ st.markdown("#### Cryptocurrency Prices Reindexed to Start at 0")
54
+ st.markdown("This analysis reindexes cryptocurrency prices to start at 0, making it easier to compare their relative movements over time.")
55
+
56
+ data_reindexed = data.apply(lambda x: x / x.iloc[0])
57
+ fig = go.Figure()
58
+
59
+ for ticker in tickers:
60
+ fig.add_trace(go.Scatter(x=data_reindexed.index, y=data_reindexed[ticker], mode='lines', name=ticker))
61
+
62
+ fig.update_layout(
63
+ title="Cryptocurrency Prices Reindexed to Start at 0",
64
+ xaxis_title="Date",
65
+ yaxis_title="Reindexed Price",
66
+ template="plotly_dark"
67
+ )
68
+ st.plotly_chart(fig, use_container_width=True)
69
+
70
+ # Calculating daily returns
71
+ returns = data.pct_change().dropna()
72
+
73
+ # Ensure no inf or NaN values in returns
74
+ returns = returns.replace([np.inf, -np.inf], np.nan).dropna()
75
+
76
+ # Kalman Filter: Estimating a common factor
77
+ st.markdown("#### Kalman Filter: Estimated Common Factor and Cryptocurrency Returns")
78
+ st.markdown("This analysis uses the Kalman Filter to estimate a common factor influencing all cryptocurrency returns. It compares individual cryptocurrency returns to the estimated common factor.")
79
+
80
+ st.markdown("The Kalman Filter operates based on the following state-space model:")
81
+
82
+ st.latex(r"""
83
+ \text{State Equation:} \quad \mathbf{x}_t = \mathbf{A} \mathbf{x}_{t-1} + \mathbf{w}_t
84
+ """)
85
+
86
+ st.latex(r"""
87
+ \text{Observation Equation:} \quad \mathbf{y}_t = \mathbf{H} \mathbf{x}_t + \mathbf{v}_t
88
+ """)
89
+
90
+ st.markdown("Where:")
91
+ st.markdown(r"""
92
+ \begin{itemize}
93
+ \item \(\mathbf{x}_t\) is the state vector (the common factor we are estimating).
94
+ \item \(\mathbf{A}\) is the state transition matrix (set to the identity matrix \(\mathbf{I}\)).
95
+ \item \(\mathbf{w}_t\) is the process noise (with covariance \(\mathbf{Q}\)).
96
+ \item \(\mathbf{y}_t\) is the observation vector (cryptocurrency returns).
97
+ \item \(\mathbf{H}\) is the observation matrix (set to a vector of ones).
98
+ \item \(\mathbf{v}_t\) is the observation noise (with covariance \(\mathbf{R}\)).
99
+ \end{itemize}
100
+ """)
101
+
102
+ st.markdown("The Kalman Filter recursively estimates the state vector \(\mathbf{x}_t\) using the observed cryptocurrency returns \(\mathbf{y}_t\). The estimated common factor is then compared to individual cryptocurrency returns.")
103
+
104
+ st.markdown("""
105
+ The steps to derive the common factor are:
106
+ 1. **Initialization:** Start with an initial estimate of the state vector \(\mathbf{x}_0\) and the initial covariance.
107
+ 2. **Prediction:** Use the state equation to predict the state vector at the next time step.
108
+ 3. **Update:** Use the observation equation and the actual observed returns to update the estimate of the state vector.
109
+
110
+ This process repeats for each time step, producing an estimated common factor that influences all cryptocurrency returns.
111
+ """)
112
+
113
+ st.markdown("""
114
+ **How to Interpret the Results:**
115
+
116
+ - **Estimated Common Factor:** This represents the underlying factor that influences all the cryptocurrency returns. If the common factor is high, it indicates that most cryptocurrencies are experiencing high returns. Conversely, if the common factor is low, it indicates that most cryptocurrencies are experiencing low returns.
117
+ - **Individual Cryptocurrency Returns vs. Common Factor:** By comparing the individual cryptocurrency returns to the estimated common factor, you can identify which cryptocurrencies are moving with the market trend and which are moving independently.
118
+ - **Deviation from the Common Factor:** Cryptocurrencies that deviate significantly from the common factor may be influenced by specific news or events, whereas cryptocurrencies that closely follow the common factor are more influenced by market-wide factors.
119
+
120
+ By observing these comparisons, you can gain insights into the behavior of individual cryptocurrencies relative to the market and identify potential outliers or trend-followers.
121
+ """)
122
+
123
+ observations = returns.values
124
+ initial_state_mean = np.zeros(1)
125
+ kf = KalmanFilter(
126
+ transition_matrices=np.eye(1),
127
+ observation_matrices=np.ones((len(tickers), 1)),
128
+ initial_state_mean=initial_state_mean,
129
+ observation_covariance=np.eye(len(tickers)),
130
+ transition_covariance=np.eye(1)
131
+ )
132
+
133
+ state_means, state_covariances = kf.em(observations).filter(observations)
134
+ fig = go.Figure()
135
+
136
+ for i, ticker in enumerate(tickers):
137
+ fig.add_trace(go.Scatter(x=returns.index, y=observations[:, i], mode='lines', name=f'{ticker} Returns'))
138
+
139
+ fig.add_trace(go.Scatter(x=returns.index, y=state_means[:, 0], mode='lines', name='Estimated Common Factor', line=dict(color='red', width=4)))
140
+ fig.update_layout(
141
+ title='Kalman Filter: Estimated Common Factor and Cryptocurrency Returns',
142
+ xaxis_title='Date',
143
+ yaxis_title='Returns',
144
+ template='plotly_dark'
145
+ )
146
+ st.plotly_chart(fig, use_container_width=True)
147
+
148
+ # CSSD and CSAD calculations
149
+ st.markdown("#### CSSD and CSAD Calculations")
150
+ st.markdown("This analysis calculates the Cross-Sectional Standard Deviation (CSSD) and Cross-Sectional Absolute Deviation (CSAD) of cryptocurrency returns. These metrics help to identify herding behavior in the market.")
151
+
152
+ st.markdown("The formulas for CSSD and CSAD are as follows:")
153
+
154
+ st.markdown("**CSSD (Cross-Sectional Standard Deviation):**")
155
+ st.latex(r"\text{CSSD}_t = \sqrt{\frac{\sum_{i=1}^{N} (R_{i,t} - \overline{R}_t)^2}{N - 1}}")
156
+
157
+ st.markdown("""
158
+ Where:
159
+ - \(R_{i,t}\) is the return of cryptocurrency \(i\) at time \(t\).
160
+ - \(\overline{R}_t\) is the average return of all cryptocurrencies at time \(t\).
161
+ - \(N\) is the number of cryptocurrencies.
162
+ """)
163
+
164
+ st.markdown("**CSAD (Cross-Sectional Absolute Deviation):**")
165
+ st.latex(r"\text{CSAD}_t = \frac{\sum_{i=1}^{N} |R_{i,t} - \overline{R}_t|}{N}")
166
+
167
+ st.markdown("""
168
+ Where:
169
+ - \(R_{i,t}\) is the return of cryptocurrency \(i\) at time \(t\).
170
+ - \(\overline{R}_t\) is the average return of all cryptocurrencies at time \(t\).
171
+ - \(N\) is the number of cryptocurrencies.
172
+ """)
173
+
174
+ st.markdown("These metrics help to identify herding behavior by measuring the dispersion of individual cryptocurrency returns around the market return.")
175
+
176
+ st.markdown("""
177
+ **How to Interpret the Results:**
178
+
179
+ - **CSSD (Cross-Sectional Standard Deviation):** A higher CSSD indicates greater dispersion of individual cryptocurrency returns around the market return, suggesting less herding behavior. Conversely, a lower CSSD indicates that cryptocurrency returns are more closely clustered around the market return, suggesting more herding behavior.
180
+ - **CSAD (Cross-Sectional Absolute Deviation):** A higher CSAD also indicates greater dispersion of individual cryptocurrency returns around the market return, suggesting less herding behavior. A lower CSAD suggests more herding behavior as cryptocurrency returns are more closely clustered around the market return.
181
+
182
+ By observing the trends in CSSD and CSAD over time, you can identify periods of increased or decreased herding behavior in the market.
183
+ """)
184
+
185
+ market_return = returns[market_index]
186
+ returns = returns.drop(columns=[market_index])
187
+
188
+ def calculate_cssd(returns, market_return):
189
+ cssd = np.sqrt(((returns - market_return[:, None]) ** 2).sum(axis=1) / (returns.shape[1] - 1))
190
+ return cssd
191
+
192
+ def calculate_csad(returns, market_return):
193
+ csad = (np.abs(returns - market_return[:, None]).sum(axis=1)) / returns.shape[1]
194
+ return csad
195
+
196
+ cssd = calculate_cssd(returns.values, market_return.values)
197
+ csad = calculate_csad(returns.values, market_return.values)
198
+
199
+ # Rolling CSSD and CSAD calculations
200
+ window_size = 30
201
+
202
+ def rolling_csad(stock_returns, market_returns, window):
203
+ csad_values = []
204
+ for i in range(len(stock_returns) - window + 1):
205
+ window_data = stock_returns.iloc[i:i+window]
206
+ window_market = market_returns[i:i+window].mean()
207
+ N = len(window_data.columns)
208
+ csad = (1 / N) * np.sum(np.abs(window_data.sub(window_market, axis=0)).mean(axis=1))
209
+ csad_values.append(csad)
210
+ return pd.Series(csad_values, index=stock_returns.index[window-1:])
211
+
212
+ def rolling_cssd(stock_returns, market_returns, window):
213
+ cssd_values = []
214
+ for i in range(len(stock_returns) - window + 1):
215
+ window_data = stock_returns.iloc[i:i+window]
216
+ window_market = market_returns[i:i+window].mean()
217
+ N = len(window_data.columns)
218
+ cssd = np.sqrt((1 / (N - 1)) * np.sum(np.square(window_data.sub(window_market, axis=0)).mean(axis=1)))
219
+ cssd_values.append(cssd)
220
+ return pd.Series(cssd_values, index=stock_returns.index[window-1:])
221
+
222
+ rolling_csad_values = rolling_csad(returns, market_return, window_size)
223
+ rolling_cssd_values = rolling_cssd(returns, market_return, window_size)
224
+
225
+ fig = go.Figure()
226
+ fig.add_trace(go.Scatter(x=returns.index, y=cssd, mode='lines', name='CSSD', line=dict(color='blue')))
227
+ fig.add_trace(go.Scatter(x=returns.index, y=csad, mode='lines', name='CSAD', line=dict(color='red')))
228
+ 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')))
229
+ 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')))
230
+ fig.update_layout(
231
+ title='CSSD, CSAD, Rolling CSAD, and Rolling CSSD over Time',
232
+ xaxis_title='Date',
233
+ yaxis_title='Value',
234
+ template='plotly_dark'
235
+ )
236
+ st.plotly_chart(fig, use_container_width=True)
237
+
238
+ # Network visualization of cryptocurrency correlations
239
+ st.markdown("#### Network Visualization of Cryptocurrency Correlations")
240
+ st.markdown("This analysis visualizes the correlations between cryptocurrencies as a network. Cryptocurrencies are connected by edges if their correlation exceeds a threshold.")
241
+ st.markdown("""
242
+ **How to Interpret the Results:**
243
+
244
+ - **Nodes:** Each node represents a cryptocurrency. The position of the nodes is determined by a spring layout algorithm, which places highly connected nodes closer together.
245
+ - **Edges:** An edge (or line) between two nodes indicates that the correlation between the two cryptocurrencies exceeds the specified threshold (0.5 in this case).
246
+ - **Edge Thickness:** The thickness of the edge represents the strength of the correlation. Thicker edges indicate higher correlations.
247
+ - **Cluster Formation:** Groups of nodes that are densely connected to each other represent clusters of cryptocurrencies that move together. This can indicate sector-specific movements or broader market trends.
248
+ - **Isolated Nodes:** Nodes that are not connected to others suggest that those cryptocurrencies do not have strong correlations with the rest of the market within the given threshold.
249
+
250
+ By examining this network, you can identify groups of cryptocurrencies that tend to move together, which may reflect sector-specific behavior or broader market dynamics. This can provide insights into the structure of the market and potential areas of risk or opportunity.
251
+ """)
252
+
253
+ years = data.index.year.unique()
254
+
255
+ def plot_network_for_year(data_for_year, year):
256
+ corr_matrix = data_for_year.corr()
257
+ G = nx.Graph()
258
+
259
+ for ticker in tickers:
260
+ G.add_node(ticker)
261
+
262
+ threshold = 0.5
263
+ for i in range(len(tickers)):
264
+ for j in range(i+1, len(tickers)):
265
+ if abs(corr_matrix.iloc[i, j]) > threshold:
266
+ G.add_edge(tickers[i], tickers[j], weight=corr_matrix.iloc[i, j])
267
+
268
+ pos = nx.spring_layout(G)
269
+ edge_trace = []
270
+
271
+ for edge in G.edges(data=True):
272
+ x0, y0 = pos[edge[0]]
273
+ x1, y1 = pos[edge[1]]
274
+ trace = go.Scatter(
275
+ x=[x0, x1, None],
276
+ y=[y0, y1, None],
277
+ line=dict(width=2, color='blue'),
278
+ hoverinfo='none',
279
+ mode='lines'
280
+ )
281
+ edge_trace.append(trace)
282
+
283
+ node_trace = go.Scatter(
284
+ x=[pos[node][0] for node in G.nodes()],
285
+ y=[pos[node][1] for node in G.nodes()],
286
+ text=[node for node in G.nodes()],
287
+ mode='markers+text',
288
+ textposition='top center',
289
+ hoverinfo='text',
290
+ marker=dict(
291
+ size=10,
292
+ color='red',
293
+ )
294
+ )
295
+
296
+ layout = go.Layout(
297
+ title=f'Cryptocurrency Correlation Network for {year}',
298
+ showlegend=False,
299
+ hovermode='closest',
300
+ margin=dict(b=20, l=5, r=5, t=40),
301
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
302
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
303
+ )
304
+
305
+ fig = go.Figure(data=edge_trace + [node_trace], layout=layout)
306
+ st.plotly_chart(fig, use_container_width=True)
307
+
308
+ for year in years:
309
+ data_for_year = data[data.index.year == year]
310
+ plot_network_for_year(data_for_year, year)
311
+
312
+ hide_streamlit_style = """
313
+ <style>
314
+ #MainMenu {visibility: hidden;}
315
+ footer {visibility: hidden;}
316
+ </style>
317
+ """
318
+ st.markdown(hide_streamlit_style, unsafe_allow_html=True)