Dispersione Trasversale dei Rendimenti
Sapere quanto sono diversi tra loro i rendimenti \(r_{1,t},r_{2,t},\dots,r_{n,t}\) di \(n\) diversi asset osservati in uno stesso istante \(t\) dà una misura di quanto i titoli si muovono in modo omogeneo o eterogeneo.
La deviazione standard di questi rendimenti, o dispersione cross-section, è
\[σ_{cs} = \sqrt{\frac{1}{n}\sum_{i=1}^n(r_{i,t}-\overline{r_{i,t}})^2}.\]
import yfinance as yf
import pandas as pd
import numpy as np
def gini_index(series):
values = series.dropna().values # Rimuovi NaN
# Se ci sono valori negativi traslo per rendere non negativi
if np.any(values < 0):
values = values - np.min(values) + 1e-10
sorted_values = np.sort(values) # Ordina in modo crescente
n = len(sorted_values)
sum_values = np.sum(sorted_values) # Somma totale
if sum_values == 0:
return 0.0 # Gini = 0 se tutti valori sono zero
#formula con indici 1-based (più stabile numericamente)
indices = np.arange(1, n + 1) # ← CHIAVE: parte da 1, non da 0
weighted_sum = np.sum(indices * sorted_values)
return (2 * weighted_sum) / (n * sum_values) - (n + 1) / n
def cross_section_dispersion(tickers, date):
"""
Calcola la dispersione dei rendimenti per la lista
dei tickers nella data di interesse date
"""
target_date = pd.to_datetime(date)
data = yf.download(tickers, start=target_date - pd.Timedelta(days=10), end=target_date)
valid_returns_df = data['Close'].pct_change().dropna()
returns = valid_returns_df.iloc[-1]
# Calcola indici di dispersione using the obtained returns
stats = {
'data': valid_returns_df.index[-1].strftime('%Y-%m-%d'),
'num_titoli': len(returns),
'media_rendimenti': returns.mean(),
'mediana_rendimenti': returns.median(),
'deviazione_standard': returns.std(),
'range': returns.max() - returns.min(),
'IQR': returns.quantile(0.75) - returns.quantile(0.25),
'skew': returns.skew(),
'kurtosis': returns.kurtosis(),
'gini': gini_index(returns)
}
return stats
# Esempio con un paniere di titoli
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META', 'TSLA', 'NVDA', 'JPM', 'V', 'JNJ']
stats = cross_section_dispersion(tickers, '2026-05-17')
print()
for key, value in stats.items():
if isinstance(value, (float, np.number)):
print(f"{key}: {value:.4f}")
else:
print(f"{key}: {value}")
/tmp/ipykernel_16836/2676860743.py:37: FutureWarning: YF.download() has changed argument auto_adjust default to True
data = yf.download(tickers, start=target_date - pd.Timedelta(days=10), end=target_date)
[*********************100%***********************] 10 of 10 completed
data: 2026-05-15
num_titoli: 10
media_rendimenti: -0.0098
mediana_rendimenti: -0.0088
deviazione_standard: 0.0235
range: 0.0780
IQR: 0.0196
skew: -0.1786
kurtosis: 0.1463
gini: 0.3242
Quando la dispersione cross-section è:
- molto bassa (< 0.5%), indica panico generalizzato, in cui tutti vendono tutto (crollo) oppure tutti comprano (rally euforico);
- bassa, il mercato è guidato da fattori comuni (macro, tassi, sentiment generale);
- alta, i fattori specifici (settoriali, aziendali) dominano;
- molto alta (> 1.5%), il mercato è selettivo, con pochi "vincenti" e molti "perdenti", un buon momento per la diversificazione.
Potremmo considerare anche una dispersione ponderata che come pesi utilizzi ad esempio la capitalizzazione di mercato normalizzata \(w_i:\) \[\sqrt{\sum_{i=1}^nw_i(r_{i,t}-\overline{w_ir_{i,t}})^2}.\] È considerata lo standard nelle analisi della dispersione di mercato (es. Goldman Sachs, Morgan Stanley), nei modelli di cross‑sectional risk, nelle strategie long/short e statistical arbitrage, nelle misure di diversificazione effettiva, nella decomposizione del rischio di portafoglio.
Nel seguito mettiamo a confronto la dispersione cross-section non ponderata e quella ponderata di un portafoglio titoli.
def cross_section_dispersion(returns, weights=None):
returns = np.asarray(returns)
returns = returns[~np.isnan(returns)]
if weights is not None: # Dispersione ponderata (es. per capitalizzazione)
mean_return = np.sum(weights * returns)
dispersion = np.sqrt(np.sum(weights * (returns - mean_return)**2))
else: # Dispersione semplice (tutti gli asset hanno lo stesso peso)
mean_return = np.mean(returns)
dispersion = np.std(returns, ddof=0) # deviazione standard della popolazione
return dispersion
def daily_dispersion(tickers, start, end, w=False):
"""
dispersione giornaliera.
"""
data = yf.download(tickers, start=start, end=end)
returns = data['Close'].pct_change().dropna()
if len(returns) == 0:
return np.nan
if w: # Dispersione ponderata per capitalizzazione
market_caps = []
for ticker in tickers:
try:
azione = yf.Ticker(ticker)
# Il metodo .info restituisce un dizionario con molti dati
info_azione = azione.info
# La capitalizzazione di mercato è nella chiave 'marketCap'
market_cap = info_azione.get('marketCap')
if market_cap:
# yfinance restituisce il valore come intero (numero di dollari)
market_caps.append(market_cap)
else:
market_caps.append(None)
except Exception as e:
print(f"Si è verificato un errore per {ticker}: {e}")
return None
weights = np.asarray(market_cap)
weights = weights / np.sum( weights) # normalizza
else:
weights = None
return returns.apply(cross_section_dispersion, axis=1, args=(weights,))
# Esempio con un paniere di titoli
tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META', 'TSLA', 'NVDA', 'JPM', 'V', 'JNJ']
start, end = '2025-01-01', '2026-05-27'
dd = pd.DataFrame()
dd["non pesata"] = daily_dispersion(tickers, start, end)
dd["pesata"] = daily_dispersion(tickers, start, end, True)
dd.plot()
Gli index provider (S&P, MSCI, FTSE) non pubblicano ufficialmente una misura di dispersione cross‑section dei rendimenti dei componenti ma esistono fonti professionali che la calcolano e la pubblicano regolarmente come strumenti professionali, non gratuiti:
- Goldman Sachs (US Equity Dispersion Index),
- Morgan Stanley (Cross‑Sectional Volatility Monitor),
- JPMorgan (Dispersion & Correlation Indicators),
- CBOE (indice di dispersione su opzioni: CBOE S&P 500 Dispersion Index),
- Axioma / Qontigo (risk model con dispersione fattoriale),
- Bloomberg (funzioni DISP, CRP, HRA).
Il rapporto dispersione/volatilità è un indicatore utile: se è maggiore di 1, le differenze tra titoli sono più ampie della volatilità del mercato; se è minore di 1, il mercato si muove come un blocco unico.
La dispersione cross-section di un portafoglio ha correlazione
- positiva moderata con gli indici della volatilità attesa, in particolare in periodi di paura la dispersione tende ad aumentare,
- negativa quasi perfetta con la correlazione media del portafoglio, con alta dispersione c'è bassa correlazione,
- positiva con il volume di scambi, in particolare maggiore disaccordo tra investitori più numerosi sono gli scambi.
Gli indicatori di market breadth forniscono:
- conferma del trend quando l'indice sale e il breadth sale, un trend forte sostenuto da ampia partecipazione;
- segnale di inversione quando l'indice sale ma il breadth scende (divergenza), il movimento è fragile e segnala un possibile crollo imminente o esaurimento del trend.
I principali indicatori di market breadth sono:
- Advance/Decline Line (ADL), indicatore cumulativo che somma la differenza tra titoli in rialzo e in calo; ADL in calo mentre l'indice sale indica debolezza;
- McClellan Oscillator, calcola la differenza tra medie mobili esponenziali di avanzamenti e declini; valori sopra +100 indicano ipercomprato, sotto -100 ipervenduto.
- Arms Index (TRIN), confronta il rapporto titoli avanzanti/declinanti con il rapporto volumi; un TRIN < 0.75 è rialzista, > 1.25 è ribassista;
- Nuovi Massimi/Minimi (52 settimane), alto rapporto nuovi massimi su nuovi minimi conferma forza di mercato; un rapporto inverso segnala debolezza;
- Percentuale sopra la Media Mobile, monitora quante azioni di un indice scambiano sopra la loro media mobile a 50 giorni; se >80% è segnale forte, se <20% è segnale debole.
- conferma incrociata, di non usare mai un singolo indicatore da solo ma aspettare che 3 indicatori si allineino o che i segnali si confermino su 2-3 sessioni di trading;
- analisi settoriale, di applicare questi indicatori a singoli settori (es. Tecnologia) per identificare forze e debolezze specifiche all'interno del mercato;
- limitazioni, poiché il breadth può divergere dai prezzi per lunghi periodi durante trend forti guidati da poche azioni (es. mega-cap); non fornisce segnali di trade diretti, ma di contesto e rischio.
- Cross-Sectional Dispersion and Expected Returns, by Thanos Verousisa and Nikolaos Voukelatos