DCC-GARCH: correlazioni dinamiche per coppie e rischio di portafoglio
Chiedi alla maggior parte dei desk cripto la correlazione tra BTC ed ETH e ti daranno un solo numero — 0.8, forse 0.75 — calcolato su una finestra che nessuno ricorda di aver scelto. Quel numero e una bugia, o quantomeno una semplificazione pericolosa. La correlazione campionaria e una media su un periodo durante il quale la vera struttura di dipendenza si muoveva di continuo. Nei mercati calmi BTC ed ETH si allontanano abbastanza da rendere attraente una coppia market-neutral. In una cascata di liquidazioni si agganciano l'uno all'altro e a tutto il resto, e la diversificazione che hai pagato evapora esattamente nel momento in cui ne hai bisogno.
Non e un effetto sottile. Prendi un qualsiasi drawdown del 2022 — il collasso di LUNA a maggio, lo smontaggio di 3AC a giugno, l'implosione di FTX a novembre — e vedrai la correlazione media a coppie sui primi 20 token marciare dall'intervallo 0.4-0.6 verso 0.9+ nel giro di pochi giorni. La correlazione non e una costante che ogni tanto viene stimata male; e una serie storica con le proprie dinamiche, il proprio clustering e i propri regimi. Trattarla come uno scalare e l'equivalente multivariato dell'assumere volatilita costante — un errore che abbiamo gia dedicato la Parte 1 di questa serie a smontare per un singolo asset.
Questo articolo e la Parte 3 di una serie sulla volatilita in quattro parti. La Parte 1 ha costruito il GARCH(1,1) univariato con la libreria arch e ha mostrato come la volatilita si aggreghi in cluster e torni verso la media. La Parte 2 ha aggiunto l'asimmetria (GJR-GARCH, EGARCH) e innovazioni Student-t per catturare il leverage effect e le code spesse. Qui passiamo al multivariato: modelliamo l'intera matrice di covarianza condizionale mentre evolve, usando il modello Dynamic Conditional Correlation (DCC) di Engle. Questo ci da due cose che una correlazione scalare non potra mai dare — un hedge ratio dinamico per il pairs trading e una varianza di portafoglio onesta e variabile nel tempo per l'allocazione basata sul rischio. La Parte 4 chiude la serie con un backtest a volatilita obiettivo che lega le previsioni univariate e multivariate in una regola di dimensionamento delle posizioni.
Diamo per scontato che tu abbia letto le Parti 1 e 2, quindi non rideriveremo il GARCH univariato. Se vuoi il comportamento congiunto sulle code — la probabilita che due asset sforino insieme il loro quantile all'1% — quella e una questione di copule, e la trattiamo in Modelli di copula per il rischio congiunto. DCC e copule sono complementari: la copula ti da una struttura di dipendenza di coda statica ma flessibile, mentre il DCC ti da una serie storica trattabile dell'intera matrice di correlazione. Questo articolo riguarda quest'ultima.
Perche la correlazione statica si rompe nel cripto
Prima della macchina, sii preciso su cosa fallisce. Una singola correlazione campionaria su una finestra stima
Questo porta con se tre assunzioni implicite, tutte false per il cripto:
- Stazionarieta della dipendenza. La finestra ha un solo vero . In realta la dipendenza ha regimi — un regime di mercato calmo vicino a 0.5 e un regime di stress vicino a 0.95 — e li fonde in una via di mezzo priva di significato.
- Volatilita marginale costante. La correlazione di Pearson e una covarianza normalizzata. Se e sono a loro volta in movimento (lo sono — e l'intera premessa delle Parti 1 e 2), allora anche una covarianza costante produce una correlazione variabile nel tempo, e viceversa. Non puoi separare le due cose senza un modello di volatilita sottostante.
- Simmetria rispetto alla direzione del mercato. La correlazione sale nei drawdown piu di quanto salga nei rally. Questo e il cugino multivariato del leverage effect. Una finestra mobile non puo esprimerlo senza diventare cosi corta da essere puro rumore.
La correzione a finestra mobile — ricalcolare sugli ultimi 30 o 60 giorni — scambia un problema con un altro. Le finestre corte sono reattive ma rumorose e sono in ritardo rispetto alla rottura effettiva; le finestre lunghe sono stabili ma stantie. Peggio, una matrice di correlazione mobile su asset non e garantita rimanere semidefinita positiva una volta che inizi a ridurla o rattopparla, il che rompe ogni ottimizzatore a valle. Vogliamo un modello che (a) sia guidato da un vero processo di volatilita per asset, (b) produca per costruzione una matrice di correlazione valida a ogni passo e (c) abbia parametri che possiamo stimare per massima verosimiglianza anziche scegliendo una lunghezza di finestra a caso. Quel modello e il DCC-GARCH.
Il problema multivariato: la matrice di covarianza condizionale
Sia il vettore dei rendimenti per asset al tempo , con media condizionale (spesso solo una costante o un piccolo termine AR) e residuo . Assumiamo
dove e la matrice di covarianza condizionale dato l'insieme informativo , e e una qualche distribuzione condizionale (gaussiana o, meglio per il cripto, Student-t multivariata). Tutto nella modellazione della volatilita multivariata e una diversa risposta a una sola domanda: come parametrizzi le dinamiche di affinche resti simmetrica definita positiva a ogni passo senza un'esplosione di parametri?
Due risposte classiche mostrano perche il problema e difficile.
VECH
Il modello VECH (Bollerslev, Engle, Wooldridge 1988) scrive la mezza-vettorizzazione di come funzione lineare dei residui quadrati passati e delle covarianze passate:
dove impila il triangolo inferiore di una matrice simmetrica in un vettore di lunghezza . Questo e massimamente generale — ogni varianza e covarianza dipende da ogni varianza e covarianza passata — e massimamente inutile oltre . Per asset, e sono ciascuna . A sono due matrici , all'incirca 450 parametri, piu vincoli di definitezza positiva che sono penosi persino da esprimere. La superficie di verosimiglianza e una palude.
BEKK
Il modello BEKK (Engle & Kroner 1995) garantisce la definitezza positiva per costruzione usando una forma quadratica:
con triangolare superiore. Poiche ogni termine e una forma quadratica, automaticamente finche . BEKK e piu parsimonioso di VECH ma scala comunque come parametri — le matrici e sono ciascuna. Per stai stimando dell'ordine di 200+ parametri congiuntamente per MLE, su dati cripto giornalieri rumorosi, senza alcuna garanzia che l'ottimizzatore converga a qualcosa di significativo. In pratica il BEKK pieno e confinato a , e anche allora si usano le restrizioni "diagonale" o "scalare" che buttano via gran parte delle dinamiche incrociate.
Questa e la maledizione della dimensionalita per il GARCH multivariato: il numero di parametri cresce quadraticamente, ma la quantita di informazione nei dati no. Esaurisci i gradi di liberta molto prima di esaurire gli asset che ti interessano. Qualsiasi paniere cripto con 10-30 token e completamente fuori portata per VECH o BEKK.
La via d'uscita, dovuta a Engle, e smettere di provare a modellare direttamente e invece fattorizzarlo in pezzi che sappiamo gia stimare a buon mercato.
Il DCC di Engle (2002): la decomposizione in due passi
Il modello Constant Conditional Correlation (CCC) di Bollerslev (1990) fu la prima fattorizzazione parsimoniosa. Scrive
dove e la matrice diagonale delle deviazioni standard condizionali — un GARCH univariato per asset — e e una matrice di correlazione costante. Questa e un'enorme semplificazione: adatti modelli GARCH univariati indipendenti, poi stimi una singola matrice di correlazione campionaria dei residui standardizzati. La definitezza positiva e automatica finche e una matrice di correlazione valida e tutti i .
Il problema del CCC e proprio nel nome — la correlazione e costante, che e esattamente l'assunzione con cui abbiamo aperto questo articolo per rifiutarla. La Dynamic Conditional Correlation di Engle (2002) mantiene la bellissima fattorizzazione del CCC ma lascia respirare la matrice di correlazione:
Ora e variabile nel tempo. Il colpo di genio e che le volatilita e le correlazioni sono stimate in due passi separati, cosicche non affrontiamo mai l'ottimizzazione congiunta completa.
Passo 1: GARCH univariato per asset
Per ogni asset , adatta un modello GARCH univariato esattamente come nelle Parti 1 e 2 — GARCH(1,1), GJR-GARCH o EGARCH con innovazioni Student-t, quello che si adatta meglio a quella serie. Questo fornisce le varianze condizionali e quindi .
Dai modelli adattati estraiamo i residui standardizzati:
Per costruzione ogni ha (approssimativamente) varianza condizionale unitaria. Impilali in un vettore . Questi residui standardizzati sono la materia prima per il passo di correlazione — le loro singole dinamiche di volatilita sono state rimosse, quindi qualunque co-movimento resti e pura dipendenza, non un artefatto di volatilita. (Questa e la stessa logica in stile PIT che l'articolo sulle copule usa prima di adattare le marginali; qui ci fermiamo alla standardizzazione anziche arrivare fino alle uniformi.)
Passo 2: la ricorsione di correlazione DCC
Modelliamo un processo ausiliario , una matrice simmetrica definita positiva , con una ricorsione simile a un GARCH guidata dai prodotti esterni dei residui standardizzati:
dove:
- e la matrice di correlazione incondizionale dei residui standardizzati (questo e il correlation targeting — piu avanti se ne parla),
- governa quanto fortemente lo shock odierno tira la correlazione,
- governa la persistenza — quanto del di ieri viene trasportato in avanti,
- e il vincolo di ritorno verso la media e (con ), direttamente analogo a nel GARCH univariato.
Nota che la struttura e identica a una ricorsione GARCH(1,1) scalare, ma su matrici: un'ancora di lungo periodo , un termine di shock e un termine di persistenza. Poiche e una combinazione convessa di matrici semidefinite positive (, il prodotto esterno di rango 1 e il precedente ), resta definita positiva finche e i pesi sono non negativi. E questo che ci procura matrici di covarianza valide garantite gratis.
e quasi una matrice di correlazione ma non del tutto — la sua diagonale non e esattamente 1. Quindi la normalizziamo:
Elemento per elemento, la correlazione condizionale tra gli asset e e
Questa e una matrice di correlazione a tutti gli effetti — diagonale unitaria, fuori diagonale in , definita positiva — a ogni singolo passo temporale, per costruzione. Riassembla la covarianza condizionale completa:
Quest'ultima forma elemento per elemento e quella che userai di continuo: la covarianza condizionale di due asset e la loro correlazione dinamica moltiplicata per ciascuna delle loro volatilita dinamiche. Ogni ingrediente sul lato destro e variabile nel tempo e proviene da un modello che puoi stimare.
L'intero modello ha solo due parametri di correlazione, e , indipendentemente dal fatto che o . Il lato della volatilita scala linearmente (un GARCH univariato per asset, ciascuno con ~4-5 parametri, tutti adattati indipendentemente e imbarazzantemente in parallelo). E per questo che il DCC scala dove BEKK e VECH non possono: la maledizione della dimensionalita e confinata a , che e targettizzato (inserito come stima campionaria) anziche ottimizzato.
La restrizione scalare e il suo costo
Gli scalari significano che ogni coppia di asset condivide le stesse dinamiche di correlazione — la stessa velocita di aggiustamento e la stessa persistenza. La correlazione BTC-ETH e la correlazione DOGE-SHIB si muovono allo stesso ritmo anche se la loro economia differisce. Questo e il prezzo della trattabilita, ed e di solito un prezzo accettabile. Le generalizzazioni (Generalized DCC con matrici ; il DCC asimmetrico di Cappiello-Engle-Sheppard) lo rilassano al costo di parametri e stabilita di stima. Menzioniamo l'aDCC piu avanti.
La quasi-log-verosimiglianza DCC
Per stimare e ci serve la verosimiglianza. Il risultato chiave di Engle e che la log-verosimiglianza gaussiana si separa in una parte di volatilita e una parte di correlazione, il che e cio che giustifica lo stimatore in due passi. Assumendo , il contributo alla log-verosimiglianza al tempo e
Sostituisci . Allora e , e usando :
Ora suddividilo aggiungendo e sottraendo :
La parte di volatilita dipende solo dai parametri del GARCH univariato (tramite ) — massimizzarla equivale esattamente ad adattare modelli GARCH univariati indipendenti, cosa che abbiamo fatto nel Passo 1. La parte di correlazione dipende da e (tramite ), dati i residui standardizzati del Passo 1. Quindi nel Passo 2 massimizziamo solo
(il termine non dipende da , quindi lo scartiamo). Questa e un'ottimizzazione a due parametri indipendentemente da quanti asset — questo e tutto il punto. Si chiama quasi-verosimiglianza perche lo stimatore in due passi e consistente ma non pienamente efficiente; gli errori standard richiedono una correzione (Engle & Sheppard 2001), ma per la generazione di segnali cio che conta sono le stime puntuali.
Per il cripto, le innovazioni gaussiane sottostimano il rischio di coda. Sostituire la verosimiglianza Student-t multivariata e una modifica plug-in a (sostituisci il kernel gaussiano con la densita multivariata e aggiungi un parametro di gradi di liberta ). Manteniamo la quasi-verosimiglianza gaussiana nello stimatore qui sotto per chiarezza e segnaliamo dove entra — la standardizzazione delle Parti 1-2 ha gia usato innovazioni t sulle marginali, il che cattura gran parte del beneficio di coda.
Implementazione in Python
Un fatto brutale ma importante: la libreria arch non fa GARCH multivariato o DCC. arch e un eccellente motore univariato (ci appoggiamo a esso proprio per quello), ma non c'e alcun dcc_model al suo interno. Le tue opzioni pratiche sono:
- Costruisci il tuo DCC sopra
arch— adatta i modelli univariati conarch, estrai i residui standardizzati, implementa la ricorsione e la quasi-verosimiglianza di correlazione in NumPy/SciPy, e ottimizza i due scalari. E cio che facciamo qui sotto. Sono circa 60 righe e completamente trasparenti. - Il pacchetto
mgarchdi PyPI — un'implementazione DCC-GARCH leggera in puro Python. Comodo per un adattamento veloce, meno flessibile se vuoi marginali GJR o innovazioni t collegate con precisione. rmgarchdi R (Alexios Galanos) — l'implementazione di riferimento.dccspec/dccfitsupportano DCC, aDCC, GARCH-copula, Student-t ed errori standard corretti. Se stai facendo ricerca seria sulla volatilita multivariata,rmgarch(chiamato da Python viarpy2se proprio devi) e il gold standard.
Costruiamo l'opzione 1 perche rende esplicita ogni parte mobile e riutilizza le competenze univariate delle Parti 1-2.
Passo 1: adattare le marginali GARCH univariate con arch
import numpy as np
import pandas as pd
from arch import arch_model
from scipy.optimize import minimize
def fetch_returns(symbols, start="2022-01-01", end="2025-12-31"):
"""
Daily log returns for a list of crypto symbols.
Replace with your data source (ccxt for 24/7 exchange data,
yfinance for a quick sketch).
"""
import yfinance as yf
px = yf.download([f"{s}-USD" for s in symbols],
start=start, end=end)["Close"]
px.columns = symbols
rets = np.log(px / px.shift(1)).dropna()
return rets
symbols = ["BTC", "ETH", "SOL", "BNB"]
returns = fetch_returns(symbols)
def fit_univariate(series, dist="t"):
"""
Fit GJR-GARCH(1,1) with Student-t innovations (Part 2 model).
Returns the fitted result and the *scaled* series it was fit on.
We scale returns by 100 for the optimizer's numerical health,
exactly as in Parts 1 and 2.
"""
scaled = series * 100.0
model = arch_model(scaled, mean="Constant",
vol="GARCH", p=1, o=1, q=1, dist=dist)
res = model.fit(disp="off")
return res
fits = {s: fit_univariate(returns[s]) for s in symbols}
Z = pd.DataFrame({s: fits[s].std_resid for s in symbols}).dropna()
Sigma = pd.DataFrame({s: fits[s].conditional_volatility
for s in symbols}).loc[Z.index]
print(Z.describe())
Un rapido controllo di sanita sui residui standardizzati e importante. Se una colonna ha una deviazione standard lontana da 1, o una pesante autocorrelazione residua nel suo quadrato (Ljung-Box su ), la marginale univariata e mal specificata e il passo DCC ereditera quell'errore. Sistema prima la marginale — a questo serviva la Parte 2.
Passo 2: la ricorsione DCC e la quasi-log-verosimiglianza
def dcc_negloglik(params, Z):
"""
Negative DCC quasi-log-likelihood (Gaussian) in (a, b).
Z : (T, d) array of standardized residuals from Step 1.
Implements:
Q_t = (1-a-b) Qbar + a z_{t-1} z_{t-1}' + b Q_{t-1}
R_t = diag(Q_t)^{-1/2} Q_t diag(Q_t)^{-1/2}
LL = -1/2 sum_t ( log|R_t| + z_t' R_t^{-1} z_t )
with correlation targeting: Qbar = sample corr of Z.
"""
a, b = params
if a <= 0 or b <= 0 or a + b >= 1.0:
return 1e10
Z = np.asarray(Z)
T, d = Z.shape
Qbar = np.cov(Z, rowvar=False, bias=True) # unconditional (targeted)
dinv = np.diag(1.0 / np.sqrt(np.diag(Qbar)))
Qbar = dinv @ Qbar @ dinv
Q = Qbar.copy() # initialize Q_1 at the target
ll = 0.0
for t in range(T):
q_diag = np.sqrt(np.diag(Q))
R = Q / np.outer(q_diag, q_diag)
sign, logdet = np.linalg.slogdet(R)
if sign <= 0:
return 1e10
z = Z[t]
Rinv_z = np.linalg.solve(R, z)
ll += -0.5 * (logdet + z @ Rinv_z)
Q = (1 - a - b) * Qbar + a * np.outer(z, z) + b * Q
return -ll
def fit_dcc(Z):
"""Estimate (a, b) by maximizing the DCC quasi-log-likelihood."""
res = minimize(
dcc_negloglik, x0=[0.03, 0.94], args=(np.asarray(Z),),
method="L-BFGS-B",
bounds=[(1e-6, 0.5), (1e-6, 0.999)],
)
a, b = res.x
print(f"DCC estimates: a = {a:.4f}, b = {b:.4f}, a+b = {a+b:.4f}")
print(f"log-likelihood: {-res.fun:.2f}")
return a, b
a_hat, b_hat = fit_dcc(Z)
Eseguendo questo su un paniere BTC/ETH/SOL/BNB su qualche anno di dati giornalieri si produce un output della forma seguente (i numeri sotto sono illustrativi, non provenienti da uno specifico esperimento datato — eseguilo sui tuoi dati):
DCC estimates: a = 0.0287, b = 0.9401, a+b = 0.9688
log-likelihood: -3812.44
Come leggerlo:
- e piccolo — la matrice di correlazione non sobbalza allo shock di un singolo giorno. Ogni giorno spinge verso il prodotto esterno solo di ~3%.
- e grande — le correlazioni sono altamente persistenti. Una volta che il paniere si accoppia in un evento di stress, resta accoppiato per un po', decadendo lentamente verso . Questo rispecchia l'esperienza vissuta dei drawdown cripto: le correlazioni non tornano indietro di scatto nel momento in cui il prezzo si stabilizza.
- conferma il ritorno verso la media. Il processo di correlazione ha un livello di lungo periodo stazionario () a cui ritorna, con un'emivita di circa giorni. Se mai stimi essenzialmente pari a 1, il processo di correlazione e integrato — non ha alcuna ancora di lungo periodo, di solito un sintomo di una rottura strutturale all'interno del tuo campione che il modello sta assorbendo come persistenza infinita.
La persistenza quasi unitaria e il minuscolo caricamento dello shock sono l'impronta digitale canonica del DCC attraverso le classi di asset, e il cripto non fa eccezione. E anche il motivo per cui una correlazione mobile a 30 giorni e un sostituto cosi scadente: una finestra mobile assume implicitamente e che non corrispondono affatto a questa struttura di decadimento.
Alcune note implementative che risparmiano tempo reale di debug:
- Inizializzazione. Partire da
[0.03, 0.94]riflette la stima cripto tipica: piccolo (le correlazioni reagiscono agli shock ma non violentemente), grande (le correlazioni sono persistenti). Se il tuo ottimizzatore vaga verso il processo di correlazione e integrato — di solito un segno di una rottura strutturale nel campione (un cambio di regime che il modello si sforza di adattare come persistenza). - Convenzione temporale. All'interno del ciclo valutiamo rispetto a e poi aggiorniamo con per il passo successivo. Questo mantiene funzione dell'informazione fino a soltanto — nessun look-ahead. Sbagliare questo off-by-one e il bug DCC piu comune in assoluto, e gonfia silenziosamente l'adattamento in-sample.
- Correlation targeting. Inseriamo come correlazione campionaria anziche stimarla. E cio che rende l'ottimizzazione bidimensionale. Il costo e che usa l'intero campione, quindi in un walk-forward rigoroso devi ristimarla solo sulla finestra di addestramento (vedi sotto).
Passo 3: ricostruire i percorsi di correlazione e covarianza
Una volta fissati , esegui la ricorsione ancora una volta, questa volta memorizzando l'intero percorso di (e ) cosicche le strategie a valle possano usarlo.
def dcc_filter(Z, Sigma, a, b):
"""
Run the DCC recursion with fixed (a,b) and return the full paths:
R_path : (T, d, d) conditional correlation matrices
H_path : (T, d, d) conditional covariance matrices (scaled units)
Sigma : (T, d) conditional volatilities aligned with Z.
"""
Z = np.asarray(Z); Sig = np.asarray(Sigma)
T, d = Z.shape
Qbar = np.cov(Z, rowvar=False, bias=True)
dinv = np.diag(1.0 / np.sqrt(np.diag(Qbar)))
Qbar = dinv @ Qbar @ dinv
Q = Qbar.copy()
R_path = np.empty((T, d, d))
H_path = np.empty((T, d, d))
for t in range(T):
q_diag = np.sqrt(np.diag(Q))
R = Q / np.outer(q_diag, q_diag)
R_path[t] = R
Dt = np.diag(Sig[t]) # D_t = diag(sigma_i,t)
H_path[t] = Dt @ R @ Dt # H_t = D_t R_t D_t
z = Z[t]
Q = (1 - a - b) * Qbar + a * np.outer(z, z) + b * Q
return R_path, H_path
R_path, H_path = dcc_filter(Z, Sigma, a_hat, b_hat)
i, j = symbols.index("BTC"), symbols.index("ETH")
rho_btc_eth = pd.Series(R_path[:, i, j], index=Z.index, name="rho_BTC_ETH")
print(rho_btc_eth.describe())
print("min/max correlation:", rho_btc_eth.min().round(3),
rho_btc_eth.max().round(3))
La serie rho_btc_eth e il frutto dell'intero esercizio: invece di un solo numero, ora hai una correlazione giornaliera che puoi tracciare, sogliare o alimentare in una strategia. Su dati cripto reali la vedrai tipicamente variare da circa 0.5 nei tratti tranquilli a oltre 0.9 durante lo stress — esattamente l'ampiezza che una singola correlazione campionaria media via.
Previsione a un passo in avanti
Per il trading dal vivo ti serve del periodo successivo a partire dall'informazione disponibile adesso. Il lato della volatilita viene dalla previsione a un passo di ciascun modello arch; il lato della correlazione e un altro giro della ricorsione:
def dcc_forecast_next(Z, a, b, fits, symbols):
"""One-step-ahead H_{t+1} using info through the last observation."""
Z = np.asarray(Z); T, d = Z.shape
Qbar = np.cov(Z, rowvar=False, bias=True)
dinv = np.diag(1.0 / np.sqrt(np.diag(Qbar)))
Qbar = dinv @ Qbar @ dinv
Q = Qbar.copy()
for t in range(T):
z = Z[t]
Q = (1 - a - b) * Qbar + a * np.outer(z, z) + b * Q
q_diag = np.sqrt(np.diag(Q))
R_next = Q / np.outer(q_diag, q_diag)
sig_next = np.array([
np.sqrt(fits[s].forecast(horizon=1, reindex=False)
.variance.iloc[-1, 0])
for s in symbols
])
Dt = np.diag(sig_next)
H_next = Dt @ R_next @ Dt
return R_next, H_next, sig_next
R_next, H_next, sig_next = dcc_forecast_next(Z, a_hat, b_hat, fits, symbols)
Ricorda che tutto e in unita scalate (×100) perche abbiamo adattato arch su series * 100. Dividi le volatilita per 100 (e le covarianze per ) per tornare alle unita di rendimento grezzo prima di alimentare una strategia. Tenere dritta la scalatura e tedioso ma una frequente fonte di bug silenziosi.
Applicazione 1: un hedge ratio dinamico per il pairs trading
La classica coppia market-neutral — lunga su un asset, corta su una quantita pesata per il beta di un altro — vive o muore in base all'hedge ratio . Stimalo con OLS statico su una finestra di addestramento ed erediterai esattamente il problema della correlazione stantia di cui parla tutto questo articolo: la copertura che neutralizzava l'esposizione al mercato lo scorso trimestre e sbagliata questo trimestre.
Il DCC ti da l'hedge ratio come serie storica. La copertura a minima varianza dell'esposizione a ETH usando BTC e il coefficiente di regressione condizionale
Ogni termine sulla destra e un output del DCC. L'hedge ratio si muove per due ragioni distinte, e il DCC le separa in modo pulito: la correlazione cambia (gli asset si accoppiano o si disaccoppiano) e il rapporto di volatilita cambia (un asset diventa relativamente piu volatile). Un beta da OLS mobile impasta entrambi gli effetti insieme con un ritardo; il DCC li attribuisce.
def dynamic_hedge_ratio(R_path, Sigma, base="BTC", target="ETH",
symbols=symbols, index=Z.index):
"""
beta_t to hedge `target` exposure with `base`:
beta_t = rho_t * sigma_target,t / sigma_base,t
(Scaling cancels in the ratio, so scaled units are fine here.)
"""
i = symbols.index(target)
j = symbols.index(base)
rho = R_path[:, i, j]
sig = np.asarray(Sigma)
beta = rho * sig[:, i] / sig[:, j]
return pd.Series(beta, index=index, name=f"beta_{target}_{base}")
beta_t = dynamic_hedge_ratio(R_path, Sigma)
print(beta_t.describe())
spread = (returns["ETH"] - beta_t.shift(1) * returns["BTC"]).dropna()
Alimenta spread in qualunque motore di pairs tu usi. La copertura dinamica non crea di per se un vantaggio — rende lo spread che negozi genuinamente market-neutral nel tempo, cosicche il tuo segnale di ritorno verso la media non e contaminato da un'esposizione direzionale alla deriva. Se costruisci strategie di pairs, questo si inserisce direttamente nei framework di Arbitraggio statistico e pairs trading nel cripto e nell'approccio a distanza per le coppie, sostituendo il loro hedge ratio fisso. La serie di correlazione stessa e anche un input piu pulito per un segnale di coppia basato sulla correlazione rispetto a qualsiasi finestra mobile — ottieni un lisciato e coerente col modello anziche una stima a finestra rumorosa.
Due avvertenze specifiche sull'uso di dal vivo. Primo, ritardalo — negozia su , mai sul contemporaneo, o stai sbirciando. Secondo, un hedge ratio che sbanda ogni giorno genera turnover e commissioni; nel mercato 24/7 del cripto con costi di funding sulla gamba corta, una copertura iper-reattiva puo dissanguare piu di quanto corregga la deriva. Liscia (una EWMA, o ribilancia la copertura solo quando si muove oltre una banda) e dimensiona il tutto con criterio — il dimensionamento delle posizioni su un segnale rumoroso e una disciplina a se, trattata in Dimensionamento con il criterio di Kelly.
Applicazione 2: varianza di portafoglio variabile nel tempo
Per un portafoglio con vettore di pesi , la varianza condizionale e
Con una matrice di covarianza statica — il default di Markowitz — questo numero e una costante che hai calcolato una volta e fingi sia ancora vera. Non lo e. Il rischio di portafoglio respira col mercato, e respira piu forte esattamente quando le correlazioni schizzano, perche in un drawdown sia i termini sia i termini salgono insieme e si moltiplicano. Un portafoglio che sembrava avere il 40% di vol annualizzata nei mercati calmi puo correre all'80%+ in una settimana di stress, e una matrice di covarianza statica ti dira che nulla e cambiato.
def portfolio_vol_path(H_path, weights, index=Z.index, unscale=1e4):
"""
Conditional portfolio volatility sigma_{p,t} = sqrt(w' H_t w).
H_path is in scaled (x100) units, so covariances carry a 100^2
factor: divide by unscale=1e4 to return to raw-return variance.
"""
w = np.asarray(weights)
var_t = np.einsum("i,tij,j->t", w, H_path, w) / unscale
return pd.Series(np.sqrt(var_t), index=index, name="port_vol")
w = np.array([0.4, 0.3, 0.2, 0.1]) # BTC, ETH, SOL, BNB
pv = portfolio_vol_path(H_path, w)
pv_annual = pv * np.sqrt(365)
print(pv_annual.describe())
Questo variabile nel tempo e l'input onesto di cui l'allocazione basata sul rischio ha bisogno. L'ottimizzazione media-varianza (Markowitz per il cripto) con una covarianza campionaria statica sta ottimizzando contro una finzione; alimentarla con (o la sua previsione a breve orizzonte) rende la frontiera efficiente stessa variabile nel tempo e costringe l'ottimizzatore a ridurre il rischio entrando nei regimi di correlazione crescente anziche dopo. Gli approcci risk-parity e gerarchici — la pipeline HRP + CVaR — sono ancora piu sensibili all'input di covarianza, poiche l'intera allocazione e una funzione della matrice di rischio. E se stai confrontando allocatori uno contro l'altro, come in algoritmi di ottimizzazione di portafoglio a confronto, che consumino covarianza statica o dinamica e spesso un driver piu grande del rischio realizzato rispetto alla scelta dell'algoritmo.
L'applicazione diretta e il volatility targeting dell'intero portafoglio: scegli una vol annualizzata obiettivo , e scala l'esposizione lorda per ogni periodo cosicche il rischio realizzato resti all'incirca costante invece di gonfiarsi nelle crisi. Questo chiude il cerchio con la Parte 4, che costruisce e sottopone a backtest esattamente questa regola.
Applicazione 3: la correlazione come segnale di regime
Oltre alla copertura e al dimensionamento, la matrice di correlazione porta un segnale macro. Lo scalare piu utile in assoluto che puoi estrarre e la correlazione media a coppie:
Quando sale attraverso il paniere, il mercato sta entrando in un regime risk-off — le storie idiosincratiche smettono di contare e tutto negozia come un unico beta macro. Questa e l'impronta quantitativa del "le correlazioni vanno a 1 in una crisi". Tende ad anticipare o coincidere con i drawdown, il che ne fa un indicatore di regime utilizzabile anziche un'autopsia in ritardo.
def avg_pairwise_corr(R_path, index=Z.index):
T, d, _ = R_path.shape
iu = np.triu_indices(d, k=1) # upper-triangle off-diagonals
avg = R_path[:, iu[0], iu[1]].mean(axis=1)
return pd.Series(avg, index=index, name="avg_corr")
avg_corr = avg_pairwise_corr(R_path)
roll_q = avg_corr.rolling(365, min_periods=90).quantile(0.80)
risk_off = (avg_corr > roll_q)
Puoi usare risk_off come freno autonomo (taglia l'esposizione lorda, allarga gli stop, metti in pausa le strategie di ritorno verso la media che vengono travolte quando tutto va nella stessa direzione) o come feature in un modello di regime piu formale. Si accompagna naturalmente all'approccio hidden-Markov in rilevamento dei regimi con gli HMM: la correlazione DCC media e una delle variabili osservate piu informative che puoi passare a un HMM, perche e lungimirante riguardo allo stress sistemico in un modo in cui i rendimenti passati non lo sono. L'avvertenza onesta: una correlazione crescente ti dice che la diversificazione sta fallendo, non in quale direzione va il mercato. E un segnale di rischio, non un segnale di alpha, e va dimensionato come tale — vedi l'asimmetria tra perdite e profitti per capire perche trattare un regime di rischio come una scommessa direzionale finisce male.
Considerazioni pratiche
Stabilita di stima e numero di asset
Il DCC scala molto meglio del BEKK, ma "scala" non e "gratis". La matrice di correlation targeting e una correlazione campionaria , e le matrici di correlazione campionarie diventano mal condizionate man mano che si avvicina al numero di osservazioni. Con 4 asset e 1000 giorni sei a posto. Con 60 asset e 400 giorni, e quasi singolare, la sua inversa nella verosimiglianza esplode, e puo vagare fuori dalla definitezza positiva a causa del rumore numerico. Le mitigazioni, all'incirca in ordine di quanto spesso ti serviranno:
- Riduci (shrink) verso un target strutturato (Ledoit-Wolf, o verso l'identita / una matrice a correlazione costante) prima di eseguire la ricorsione. E la singola correzione a maggior leva per i panieri grandi.
- Raggruppa gli asset in una manciata di settori (major, L1, DeFi, meme), modella all'interno e tra i settori a livello settoriale, o esegui il DCC su fattori a componenti principali anziche su asset grezzi.
- Preferisci piu dati a piu asset. Il DCC ha un appetito insaziabile per una storia lunga, pulita e contemporanea — che e esattamente cio che i token giovani non hanno.
Realisticamente, tieni il DCC diretto al massimo a qualche decina di asset. Per un universo ampio, il DCC sui rendimenti fattoriali piu residui idiosincratici e il workaround standard.
Il correlation targeting e una scorciatoia con un costo
Targettizzare rende la stima trattabile ma incorpora la correlazione incondizionale dell'intero campione in ogni . In un backtest rigoroso questa e una fuga di informazione look-ahead: la tua matrice di correlazione al giorno "conosce" la correlazione media dell'intero campione, futuro incluso. Per una valutazione onesta devi ristimare solo sulla finestra di addestramento e tenerla fissa out-of-sample, oppure farla rollare in avanti. E la stessa disciplina che l'intero framework di ottimizzazione walk-forward impone, ed e facile violarla accidentalmente con un comodo np.cov(Z) sull'intero array — come fa il nostro codice didattico qui sopra. Sistemala prima di fidarti di un solo numero di P&L.
Cadenza di riadattamento e disciplina anti-look-ahead
Non devi riottimizzare ogni giorno — sono parametri stabili. Una cadenza di produzione sensata:
- Ristima e i parametri GARCH univariati settimanalmente o mensilmente.
- Esegui il filtro (aggiorna , ) ogni periodo con i parametri congelati per ottenere e freschi. Il filtraggio e economico; l'adattamento no.
- Prevedi sempre, non lisciare mai. Usa costruito dall'informazione fino a per negoziare a . La struttura a due passaggi (adatta su una finestra, poi filtra in avanti) e cio che ti mantiene onesto.
Il divario tra un backtest DCC e la performance dal vivo e quasi sempre una fuga di look-ahead — sull'intero campione, contemporaneo, o riadattamento su dati che includono l'operazione che stai valutando. La disciplina di far corrispondere il backtest alle condizioni dal vivo e un argomento a se in parita backtest-dal vivo, e il DCC e un modello che punisce la sciatteria qui piu della maggior parte. Se, dopo una valutazione walk-forward pulita, la correlazione dinamica non aggiunge nulla rispetto a una semplice stima mobile per la tua strategia, quello e un risultato negativo reale e pubblicabile — la mentalita in risultati negativi onesti si applica direttamente.
DCC asimmetrico (aDCC)
Proprio come il leverage effect univariato (Parte 2) significa che le cattive notizie alzano la volatilita piu di quelle buone, le correlazioni salgono di piu dopo shock congiunti negativi che dopo shock congiunti positivi. Cappiello, Engle & Sheppard (2006) catturano questo con il DCC asimmetrico, aggiungendo un termine guidato dal prodotto esterno dei residui standardizzati nella loro parte negativa :
dove e misura la spinta extra di correlazione da movimenti congiunti al ribasso. Per il cripto, dove la correlazione-da-crollo e il rischio dominante, il termine di asimmetria e di solito significativo e vale l'unico parametro in piu. rmgarch adatta l'aDCC out of the box (model="aDCC"); aggiungere il termine al nostro stimatore NumPy e un esercizio semplice.
Confronto: il DCC contro le alternative
Dove si colloca il DCC tra i modi in cui potresti ottenere una matrice di covarianza per un paniere cripto? Il riassunto onesto:
| Approccio | Parametri | Scala fino a | variabile nel tempo? | PD garantita? | Dipendenza di coda? |
|---|---|---|---|---|---|
| Covarianza campionaria / mobile | 0 (lunghezza finestra) | qualsiasi | grossolanamente (in ritardo, rumorosa) | no (serve rattoppo) | no |
| EWMA (RiskMetrics) | 1 () | qualsiasi | si (singolo decadimento) | si | no |
| CCC-GARCH | marginali + | decine | no ( costante) | si | no |
| DCC-GARCH | marginali + 2 | decine | si | si | no |
| aDCC-GARCH | marginali + 3 | decine | si, asimmetrica | si | parziale |
| BEKK | si (ricca) | si | no | ||
| VECH | si (la piu ricca) | penosa | no | ||
| GARCH-copula | marginali + copula | decine (vine) | copula statica | si | si |
Alcune letture di questa tabella:
- EWMA e la baseline economica che tutti dovrebbero battere prima di sostenere che il DCC aiuta. E in spirito un caso speciale a un parametro — un singolo decadimento esponenziale applicato direttamente alla covarianza — e per molti panieri e sorprendentemente difficile da migliorare out-of-sample. Se il DCC non batte l'EWMA in un walk-forward pulito, usa l'EWMA.
- CCC vs DCC e l'intero punto di questo articolo: stessa fattorizzazione, ma il CCC congela e il DCC lo lascia muovere. I due parametri extra () sono l'intera differenza, e nel cripto si guadagnano il pane.
- BEKK/VECH comprano dinamiche piu ricche — ogni covarianza puo rispondere a ogni shock passato — ma il costo in parametri li confina a panieri minuscoli. Per qualsiasi cosa oltre i 4 asset non sono un'opzione reale.
- GARCH-copula e l'unica riga con un "si" sotto dipendenza di coda. E di nuovo la complementarita: il DCC modella il centro dinamico della distribuzione congiunta, le copule ne modellano le code statiche. Se la tua domanda di rischio e "cosa succede quando tutto si rompe insieme", ricorri alla pipeline con copule; se e "qual e il mio hedge ratio / la mia varianza di portafoglio adesso", ricorri al DCC.
Il default pratico per un desk cripto sistematico: DCC (o aDCC) per gli hedge ratio e la covarianza dinamica nel corpo, un overlay di copula per il rischio di coda e il CVaR, ed EWMA come baseline di controllo che ti mantiene onesto sul fatto che la macchina extra si stia ripagando.
Limitazioni
- Dinamiche scalari. Un solo e un solo per tutte le coppie e una restrizione forte. BTC-ETH e due oscure alt condividono la stessa velocita di aggiustamento. Il Generalized DCC lo rilassa ma reintroduce l'esplosione di parametri che il DCC era progettato per evitare.
- Perdita di efficienza del due passi. Lo stimatore quasi-verosimiglianza e consistente ma non pienamente efficiente, e gli errori standard ingenui sono sbagliati. Usa la correzione di Engle-Sheppard se ti interessa l'inferenza; per la generazione di segnali le stime puntuali bastano.
- Code gaussiane per default. La semplice quasi-verosimiglianza gaussiana sottostima il rischio di coda congiunto. Le innovazioni Student-t aiutano; per una genuina dipendenza di coda (la probabilita di movimenti estremi simultanei), il DCC e lo strumento sbagliato e un modello di copula e quello giusto. Il DCC ti da il corpo dinamico della correlazione; le copule ti danno la coda statica. I desk seri usano entrambi.
- La correlazione non e causalita, e non e direzione. Un crescente avverte che la diversificazione sta fallendo; non dice nulla sulla direzione del mercato. Non sovraccaricare un segnale di rischio con aspettative direzionali.
- Fame di dati. Tutto quanto sopra assume storie lunghe, pulite e sincronizzate. I token cripto piu nuovi e piu interessanti violano tutte e tre.
Riepilogo
- La correlazione statica e una bugia nel cripto. Le correlazioni si aggregano in cluster, persistono e schizzano verso 1 nei drawdown — esattamente quando la diversificazione dovrebbe aiutare. Una singola campionaria media un processo a commutazione di regime in una via di mezzo priva di significato.
- Il GARCH multivariato completo (VECH, BEKK) non scala. Il conteggio dei parametri cresce come ; entrambi sono confinati in pratica a una manciata di asset.
- Il DCC (Engle 2002) fattorizza il problema: , con da adattamenti GARCH univariati indipendenti (riutilizza le Parti 1-2) e da una ricorsione a due parametri. Scala fino a decine di asset perche si ottimizzano solo .
- La ricorsione , normalizzata a , produce una matrice di correlazione definita positiva valida a ogni passo, con , .
archnon fa DCC. Adatta le marginali conarch, poi implementa lo stimatore NumPy/SciPy di ~60 righe qui presente, o usamgarch(Python) ormgarch(R, il riferimento).- Tre frutti concreti: un hedge ratio dinamico per il pairs trading; una varianza di portafoglio onesta e variabile nel tempo per l'allocazione basata sul rischio; e la correlazione media a coppie come segnale di regime risk-off.
- La disciplina e tutto. Il correlation targeting fa trapelare la media dell'intero campione, quindi ristima solo sui dati di addestramento; ritarda ogni hedge ratio; filtra in avanti, non lisciare mai. La valutazione walk-forward e imprescindibile.
- L'aDCC aggiunge un termine di asimmetria al ribasso ed e di solito utile nel cripto, dove la correlazione-da-crollo domina.
- La Parte 4 usa queste previsioni per costruire e sottoporre a backtest una strategia a volatilita obiettivo.
Riferimenti:
- Engle, R. (2002). Dynamic Conditional Correlation: A Simple Class of Multivariate Generalized Autoregressive Conditional Heteroskedasticity Models. Journal of Business & Economic Statistics, 20(3), 339-350. DOI
- Engle, R. & Sheppard, K. (2001). Theoretical and Empirical Properties of Dynamic Conditional Correlation Multivariate GARCH. NBER Working Paper 8554. DOI
- Bollerslev, T. (1990). Modelling the Coherence in Short-Run Nominal Exchange Rates: A Multivariate Generalized ARCH Model. Review of Economics and Statistics, 72(3), 498-505. DOI
- Cappiello, L., Engle, R., & Sheppard, K. (2006). Asymmetric Dynamics in the Correlations of Global Equity and Bond Returns. Journal of Financial Econometrics, 4(4), 537-572. DOI
- Engle, R. & Kroner, K. (1995). Multivariate Simultaneous Generalized ARCH. Econometric Theory, 11(1), 122-150. DOI
- Bollerslev, T., Engle, R., & Wooldridge, J. (1988). A Capital Asset Pricing Model with Time-Varying Covariances. Journal of Political Economy, 96(1), 116-131. DOI
- Galanos, A. (2022). rmgarch: Multivariate GARCH Models. R package. CRAN.
MarketMaker.cc Team
Ricerca Quantitativa e Strategia