4 Methods for Quantifying the Collective Moves in Stocks
An intriguing phenomenon, deeply rooted in behavioral finance, is the concept of ‘herding behavior’. This is where individual investors mimic the actions of the larger crowd, sometimes even against their own information or judgment. But why does this matter? Because, at its core, herding can amplify market volatility, create price bubbles, and even contribute to financial crises.
Moreover, it challenges the traditional financial theory that markets are always efficient, as it can lead to asset prices deviating from their fundamental values. While the idea of herding is not new, quantifying it in a structured and meaningful way has always been challenging. We’ll attempt to do that in this article with easily accesible data in Python.
We will also navigate through several methodologies designed to quantify herding behavior in the stock market. By blending theoretical concepts with hands-on Python implementations, we aim to provide readers with a practical toolkit to discern and measure herding patterns in financial data.
2. Python Implementations
2.1 Herding Behavior Index (HBI)
The Herding Behavior Index (HBI) is a quantitative measure designed to capture the degree to which individual stock returns deviate from the market average. Essentially, if a stock is behaving “in herd” with the market, its returns would move closely in line with the market’s returns. However, significant deviations indicate the stock is moving against the herd.
Given the returns R of a stock and the returns of a market index, the Rolling HBI over a window of size w at time t can be expressed as:
Equation. 1: Formula representation of the Rolling Herd Behavior Index (HBI): Quantifying the deviation of individual stock returns from the market.
The HBI gauges the relative deviation of the stock from the market. When the HBI is higher, it suggests that the stock is deviating from the market trend, indicating potential non-herding behavior. Conversely, a lower HBI indicates the stock is moving more in line with the market, suggesting potential herding behavior.
In the context of market analysis, observing how HBI evolves over time can shed light on changing investor sentiments and market dynamics. A rising HBI may imply decreasing confidence in market trends, while a stable or declining HBI might suggest sustained confidence or increasing herding behavior.
To implement HBI on a rolling basis in Python you can use the code below:
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Download ASML.AS and ^AEX data
asml = yf.download("ASML.AS", start='2010-01-01', end='2023-12-29')
aex = yf.download('^AEX', start='2010-01-01', end='2023-12-29')
# Calculate daily returns
asml_returns = asml['Close'].pct_change().dropna()
aex_returns = aex['Close'].pct_change().dropna()
# Define the rolling window size
window_size = 30
# Calculate the rolling HBI
rolling_hbi = (np.abs(asml_returns - aex_returns).rolling(window_size).mean() /
np.abs(aex_returns).rolling(window_size).mean())
# Plot ASML.AS stock price and rolling HBI
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(30, 6), sharex=True)
ax1.plot(asml['Close'], label='ASML.AS Stock Price', color='blue')
ax1.set_title('ASML.AS Stock Price')
ax1.legend()
ax2.plot(rolling_hbi, label='Rolling HBI (30 days)', color='orange')
ax2.set_title('Rolling Herd Behavior Index (HBI) for ASML.AS')
ax2.legend()
plt.tight_layout()
plt.show()
Figure. 1: Comparative Analysis of ASML.AS Stock Price and its Rolling Herd Behavior Index (HBI) from 2010 to 2023: The HBI measures the extent to which ASML.AS’s daily returns deviate from the AEX market’s average returns over a rolling 30-day window. Lower HBI values suggest that ASML.AS’s returns are more closely aligned with the market, potentially indicative of stronger herding behavior. Higher HBI values indicate less conformity with the market’s movements.
It’s crucial to understand that while the HBI can highlight potential herding periods, it does not, on its own, suggest causation. Other macroeconomic and market-specific factors should be considered alongside the HBI to form a comprehensive view.
2.2 Cross-Sectional Absolute Deviation (CSAD)
While HBI focuses on the deviation of an individual stock’s return from the market average, CSAD offers a perspective on herding behavior by examining the dispersion of returns across a portfolio of stocks. The logic behind CSAD is intuitive: during periods of herding, the returns of individual stocks tend to converge towards the market return, resulting in a low CSAD value. In contrast, during periods of non-herding or independent decision-making, the returns are more dispersed, leading to a higher CSAD.
For a given window of size w and a set of stocks, CSAD at time t can be represented as:
Equation. 2: Formula for the Cross-Sectional Absolute Deviation (CSAD): Capturing the average divergence of individual stocks from the market trend.
Where:
- Ri,t is the return of stock i at time t.
- Rˉt is the average return of the market at time t.
- N is the total number of stocks.
The formula calculates the average absolute deviation of each stock’s return from the market average return. The term N/(N-1) is a correction factor used when analyzing samples rather than an entire population.
From an interpretation standpoint:
- Low CSAD: Indicates that individual stocks are moving more in tandem with the market average. This could suggest herding behavior among the investors, where they are collectively bullish or bearish, making stock returns converge.
- High CSAD: Signifies greater dispersion in the stock returns compared to the market average. This might point towards independent decision-making by investors and lesser herding.
To Implement the CSAD on a rolling basis, the following Python can be used:
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Extended list of tech stocks along with S&P 500
tickers = ["AAPL", "AMZN", "GOOGL", "MSFT", "META", "NVDA", "NFLX", "ADBE", "CRM",
"INTC", "AVGO", "QCOM", "TXN", "CSCO", "ORCL", "^GSPC"]
data = yf.download(tickers, start='2000-01-01', end='2023-12-29')['Close']
# Calculate daily returns
returns = data.pct_change().dropna()
# Define rolling window size
window_size = 30
def rolling_csad(stock_returns, market_returns, window):
csad_values = []
for i in range(len(stock_returns) - window + 1):
window_data = stock_returns.iloc[i:i+window]
window_market = market_returns[i:i+window].mean()
N = len(window_data.columns)
csad = (N / (N - 1)) * np.sum(np.abs(window_data.sub(window_market, axis=0)).mean(axis=1))
csad_values.append(csad)
return pd.Series(csad_values, index=stock_returns.index[window-1:])
# Calculate rolling CSAD for selected stocks compared to S&P 500
rolling_csad_values = rolling_csad(returns[tickers[:-1]], returns["^GSPC"], window_size)
# Align the data dates with rolling_csad_values dates
aligned_data = data.loc[rolling_csad_values.index]
# Re-index stocks to start from 0
aligned_data_zeroed = aligned_data.sub(aligned_data.iloc[0])
# Plotting
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(14, 15))
# Plot S&P 500 price
ax1.plot(aligned_data["^GSPC"], label='S&P 500 Price', color='green')
ax1.set_title('S&P 500 Price')
ax1.legend()
# Plot S&P 500 returns
ax2.plot(returns.loc[rolling_csad_values.index, "^GSPC"], label='S&P 500 Daily Returns', color='blue')
ax2.set_title('S&P 500 Daily Returns')
ax2.legend()
# Plot re-indexed stocks
for ticker in tickers[:-1]: # we exclude ^GSPC
ax3.plot(aligned_data_zeroed[ticker], label=ticker)
ax3.set_title('Individual Stocks Re-indexed to Start from 0')
ax3.legend(loc='center left', bbox_to_anchor=(1, 0.5))
# Plot Rolling CSAD
ax4.plot(rolling_csad_values, label='Rolling CSAD (30 days)', color='orange')
ax4.set_title('Rolling CSAD for Selected Stocks vs. S&P 500')
ax4.legend()
plt.tight_layout()
plt.show()
More articles worth reading:
Top 36 Moving Average Methods For Stock Prices in Python
Newsletter