Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -9,33 +9,35 @@ import networkx as nx
|
|
| 9 |
# Streamlit app setup
|
| 10 |
st.set_page_config(layout="wide")
|
| 11 |
|
| 12 |
-
st.title("Herding Behaviour Analysis in
|
| 13 |
-
|
| 14 |
-
st.markdown(
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
)
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
| 39 |
run_button = st.sidebar.button("Run Analysis")
|
| 40 |
|
| 41 |
if run_button:
|
|
@@ -50,9 +52,8 @@ if run_button:
|
|
| 50 |
data = data.fillna(method='ffill').dropna()
|
| 51 |
data = data.replace([np.inf, -np.inf], np.nan).dropna()
|
| 52 |
|
| 53 |
-
#
|
| 54 |
-
st.markdown("
|
| 55 |
-
st.markdown("This analysis reindexes asset prices to start at 0, making it easier to compare their relative movements over time.")
|
| 56 |
|
| 57 |
data_reindexed = data.apply(lambda x: x / x.iloc[0])
|
| 58 |
fig = go.Figure()
|
|
@@ -74,48 +75,27 @@ if run_button:
|
|
| 74 |
# Ensure no inf or NaN values in returns
|
| 75 |
returns = returns.replace([np.inf, -np.inf], np.nan).dropna()
|
| 76 |
|
| 77 |
-
# Kalman Filter:
|
| 78 |
-
st.markdown("
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
st.
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
- \(vt\) is the observation noise (with covariance \(R\)).
|
| 99 |
-
""")
|
| 100 |
-
|
| 101 |
-
st.markdown("The Kalman Filter recursively estimates the state vector \(xt\) using the observed asset returns \( yt \). The estimated common factor is then compared to individual asset returns.")
|
| 102 |
-
|
| 103 |
-
st.markdown("""
|
| 104 |
-
The steps to derive the common factor are:
|
| 105 |
-
1. **Initialization:** Start with an initial estimate of the state vector \(\mathbf{x}_0\) and the initial covariance.
|
| 106 |
-
2. **Prediction:** Use the state equation to predict the state vector at the next time step.
|
| 107 |
-
3. **Update:** Use the observation equation and the actual observed returns to update the estimate of the state vector.
|
| 108 |
-
|
| 109 |
-
This process repeats for each time step, producing an estimated common factor that influences all asset returns.
|
| 110 |
-
""")
|
| 111 |
-
|
| 112 |
-
st.markdown("""
|
| 113 |
-
**How to Interpret the Results:**
|
| 114 |
-
|
| 115 |
-
- **Estimated Common Factor:** This represents the underlying factor that influences all the asset returns. If the common factor is high, it indicates that most assets are experiencing high returns. Conversely, if the common factor is low, it indicates that most asset are experiencing low returns.
|
| 116 |
-
- **Individual Asset Returns vs. Common Factor:** By comparing the individual asset returns to the estimated common factor, you can identify which asset are moving with the market trend and which are moving independently.
|
| 117 |
-
- **Deviation from the Common Factor:** Assets that deviate significantly from the common factor may be influenced by specific news or events, whereas assets that closely follow the common factor are more influenced by market-wide factors.
|
| 118 |
-
""")
|
| 119 |
|
| 120 |
observations = returns.values
|
| 121 |
initial_state_mean = np.zeros(1)
|
|
@@ -142,32 +122,16 @@ if run_button:
|
|
| 142 |
)
|
| 143 |
st.plotly_chart(fig, use_container_width=True)
|
| 144 |
|
| 145 |
-
# CSSD and CSAD
|
| 146 |
-
st.markdown("
|
| 147 |
-
st.markdown("This analysis calculates the Cross-Sectional Standard Deviation (CSSD) and Cross-Sectional Absolute Deviation (CSAD) of Asset returns.")
|
| 148 |
-
|
| 149 |
-
st.markdown("The formulas for CSSD and CSAD are as follows:")
|
| 150 |
-
|
| 151 |
-
st.markdown("**CSSD (Cross-Sectional Standard Deviation):**")
|
| 152 |
-
st.latex(r"\text{CSSD}_t = \sqrt{\frac{\sum_{i=1}^{N} (R_{i,t} - \overline{R}_t)^2}{N - 1}}")
|
| 153 |
|
| 154 |
-
|
| 155 |
-
st.
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
- N is the number of Assets.
|
| 162 |
-
""")
|
| 163 |
-
|
| 164 |
-
st.markdown("These metrics help to identify the dispersion of individual asset returns around the market return.")
|
| 165 |
-
|
| 166 |
-
st.markdown("""
|
| 167 |
-
**How to Interpret the Results:**
|
| 168 |
-
|
| 169 |
-
- **CSSD (Cross-Sectional Standard Deviation) and CSAD (Cross-Sectional Absolute Deviation):** Higher values indicate greater dispersion of asset returns around the market return, suggesting less herding behavior. Lower values indicate more clustering, suggesting more herding behavior.
|
| 170 |
-
""")
|
| 171 |
|
| 172 |
market_return = returns[market_index]
|
| 173 |
returns = returns.drop(columns=[market_index])
|
|
@@ -183,7 +147,6 @@ if run_button:
|
|
| 183 |
cssd = calculate_cssd(returns.values, market_return.values)
|
| 184 |
csad = calculate_csad(returns.values, market_return.values)
|
| 185 |
|
| 186 |
-
# Rolling CSSD and CSAD calculations
|
| 187 |
window_size = 30
|
| 188 |
|
| 189 |
def rolling_csad(stock_returns, market_returns, window):
|
|
@@ -222,21 +185,9 @@ if run_button:
|
|
| 222 |
)
|
| 223 |
st.plotly_chart(fig, use_container_width=True)
|
| 224 |
|
| 225 |
-
# Network
|
| 226 |
-
st.markdown("
|
| 227 |
-
st.markdown("This analysis visualizes the correlations between Assets as a network. Assets are connected by edges if their correlation exceeds a threshold.")
|
| 228 |
-
st.markdown("""
|
| 229 |
-
**How to Interpret the Results:**
|
| 230 |
|
| 231 |
-
- **Nodes:** Each node represents a Asset. The position of the nodes is determined by a spring layout algorithm, which places highly connected nodes closer together.
|
| 232 |
-
- **Edges:** An edge (or line) between two nodes indicates that the correlation between the two Assets exceeds the specified threshold (0.5 in this case).
|
| 233 |
-
- **Edge Thickness:** The thickness of the edge represents the strength of the correlation. Thicker edges indicate higher correlations.
|
| 234 |
-
- **Cluster Formation:** Groups of nodes that are densely connected to each other represent clusters of Assets that move together. This can indicate sector-specific movements or broader market trends.
|
| 235 |
-
- **Isolated Nodes:** Nodes that are not connected to others suggest that those Assets do not have strong correlations with the rest of the market within the given threshold.
|
| 236 |
-
|
| 237 |
-
By examining this network, you can identify groups of Assets 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.
|
| 238 |
-
""")
|
| 239 |
-
|
| 240 |
years = data.index.year.unique()
|
| 241 |
|
| 242 |
def plot_network_for_year(data_for_year, year, threshold):
|
|
@@ -246,7 +197,6 @@ if run_button:
|
|
| 246 |
for ticker in tickers:
|
| 247 |
G.add_node(ticker)
|
| 248 |
|
| 249 |
-
#threshold = 0.5
|
| 250 |
for i in range(len(tickers)):
|
| 251 |
for j in range(i+1, len(tickers)):
|
| 252 |
if abs(corr_matrix.iloc[i, j]) > threshold:
|
|
@@ -302,4 +252,4 @@ hide_streamlit_style = """
|
|
| 302 |
footer {visibility: hidden;}
|
| 303 |
</style>
|
| 304 |
"""
|
| 305 |
-
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
|
|
|
|
| 9 |
# Streamlit app setup
|
| 10 |
st.set_page_config(layout="wide")
|
| 11 |
|
| 12 |
+
st.title("Herding Behaviour Analysis in Financial Markets")
|
| 13 |
+
|
| 14 |
+
st.markdown("This app analyzes herding behavior in financial markets by examining price movements and correlations.")
|
| 15 |
+
|
| 16 |
+
# Sidebar: How to Use (closed by default)
|
| 17 |
+
with st.sidebar.expander("How to Use", expanded=False):
|
| 18 |
+
st.write(
|
| 19 |
+
"""
|
| 20 |
+
1. Select the stock ticker or crypto pairs.
|
| 21 |
+
2. Choose the time period.
|
| 22 |
+
3. Set additional parameters for the analyses.
|
| 23 |
+
4. Click 'Run Analysis' to see the results.
|
| 24 |
+
"""
|
| 25 |
+
)
|
| 26 |
+
|
| 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")
|
| 42 |
|
| 43 |
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()
|
|
|
|
| 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)
|
|
|
|
| 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])
|
|
|
|
| 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):
|
|
|
|
| 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):
|
|
|
|
| 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:
|
|
|
|
| 252 |
footer {visibility: hidden;}
|
| 253 |
</style>
|
| 254 |
"""
|
| 255 |
+
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
|