GARCH(1,1): previsione della volatilita nel crypto
Apri un grafico dei rendimenti giornalieri di BTC e noterai qualcosa che i manuali sul random walk non ti avevano preparato ad affrontare: la calma e il caos arrivano a raffiche. Una giornata in calo del 6% raramente e sola. Si colloca dentro una settimana di oscillazioni del 4-8%, poi il mercato tira il fiato e scivola attraverso un mese di sonnolente sedute all'1% prima della tempesta successiva. I rendimenti in se appaiono quasi imprevedibili — non puoi dire in modo affidabile se domani sara in rialzo o in ribasso — ma la loro ampiezza e profondamente prevedibile. La turbolenza di oggi ti dice molto su quella di domani.
Quasi ogni strumento di rischio a cui un trader ricorre presuppone silenziosamente che questo non sia vero. Black-Scholes prezza un'opzione con un unico costante. Un Value-at-Risk statico moltiplica una singola stima di volatilita per un quantile normale. Uno stop-loss fisso al 3% tratta un martedi laterale e piatto e le ore attorno a un dato FOMC o a un de-peg importante di un exchange come se portassero lo stesso rischio. Ognuno di questi si rompe esattamente nello stesso modo: comprime una quantita che varia nel tempo in una costante, e poi rimane sorpreso quando la costante si rivela mobile.
Questo articolo e la Parte 1 di una serie in quattro parti sulla modellazione della volatilita per il crypto. Costruisce le fondamenta: il modello GARCH(1,1), perche si adatta cosi bene ai rendimenti crypto, come stimarlo onestamente per massima verosimiglianza con la libreria arch, e come trasformare una previsione di varianza condizionale in due cose immediatamente utili — una dimensione di posizione e un'ampiezza di stop che respirano entrambe con il mercato. La Parte 2 aggiunge asimmetria e code pesanti, la Parte 3 passa al multivariato e la Parte 4 assembla il backtest completo di volatility targeting. Qui manteniamo l'applicazione deliberatamente semplice; la strategia onesta e validata in walk-forward e l'oggetto della Parte 4.
I fatti stilizzati dei rendimenti crypto
Prima di modellare qualsiasi cosa, conviene essere precisi su cosa stiamo cercando di riprodurre. I rendimenti finanziari empirici — azioni, FX e crypto in particolare — condividono un piccolo insieme di regolarita statistiche robuste documentate da decenni. Di solito sono chiamati fatti stilizzati, e tre di essi guidano tutto cio che segue.
1. Clustering di volatilita. I grandi movimenti tendono a essere seguiti da grandi movimenti (di segno qualsiasi), e i piccoli movimenti da piccoli movimenti. Mandelbrot lo noto nei prezzi del cotone nel 1963. Formalmente, mentre i rendimenti sono quasi serialmente incorrelati, i rendimenti al quadrato (una proxy della varianza realizzata) mostrano un'autocorrelazione positiva forte e a lento decadimento.
2. Code pesanti (leptocurtosi). La distribuzione incondizionata dei rendimenti ha molta piu massa negli estremi rispetto a una gaussiana. Dove una distribuzione normale ha curtosi 3, i log-rendimenti giornalieri di BTC si collocano abitualmente sopra 8-10, e i rendimenti crypto a frequenza piu alta possono essere peggiori. Le giornate a sei sigma, che un modello normale dice dovrebbero accadere all'incirca una volta ogni milione di anni, compaiono diverse volte in un decennio.
3. Nessuna autocorrelazione lineare nei rendimenti, forte autocorrelazione nei rendimenti al quadrato. Questa e l'impronta digitale che separa un genuino processo di volatilita da un banale trend. Se regredisci sui propri ritardi non ottieni nulla di sfruttabile. Se regredisci sui suoi ritardi, trovi un segnale chiaro e persistente. Questa e esattamente la struttura che un modello di varianza dovrebbe catturare — ed esattamente cio che un modello a costante butta via.
Possiamo osservare tutti e tre con poche righe. Nulla qui richiede una fonte di dati speciale; usa ccxt in produzione, ma per uno snippet riproducibile yfinance va bene.
import numpy as np
import pandas as pd
import yfinance as yf
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.stats.diagnostic import acorr_ljungbox
from scipy import stats
px = yf.download("BTC-USD", start="2021-01-01", end="2025-12-31")["Close"]
ret = np.log(px / px.shift(1)).dropna() # log returns
ret = ret.rename("btc")
print(f"Observations: {len(ret)}")
print(f"Ann. volatility: {ret.std() * np.sqrt(365):.2%}")
print(f"Skewness: {stats.skew(ret):.2f}")
print(f"Excess kurtosis: {stats.kurtosis(ret):.2f}") # 0 == Gaussian
lb_ret = acorr_ljungbox(ret, lags=[10], return_df=True)
lb_ret2 = acorr_ljungbox(ret ** 2, lags=[10], return_df=True)
print("\nLjung-Box p-value, returns (lag 10):", float(lb_ret["lb_pvalue"].iloc[0]))
print("Ljung-Box p-value, squared (lag 10):", float(lb_ret2["lb_pvalue"].iloc[0]))
La lettura tipica (illustrativa — la tua finestra sara diversa): curtosi in eccesso ben sopra 3, un p-value di Ljung-Box sui rendimenti grezzi che non riesce a rifiutare l'ipotesi di nessuna autocorrelazione, e un p-value sui rendimenti al quadrato che e di fatto zero. Quell'ultimo contrasto e tutto il gioco. Non c'e nulla da tradare nel segno dei rendimenti sull'orizzonte giornaliero, ma c'e moltissima struttura nella loro varianza, e quella struttura e prevedibile.
Una nota sulla natura 24/7 del crypto. A differenza delle azioni, non c'e un gap notturno ne una chiusura del weekend, quindi il giorno e una barra pulita di 24 ore e il fattore di annualizzazione e , non . Il clustering di volatilita sopravvive anche alle scale intraday, cosa che conta se esegui GARCH su barre orarie — i cambi di segno del funding rate e le cascate di liquidazioni iniettano scoppi di varianza acuti e raggruppati che un modello giornaliero appiattisce.
Da ARCH a GARCH
Il problema e ora posto in modo netto: modellare una varianza che non e costante ma dipende dal passato recente. Il primo modello a farlo in modo appropriato fu l'ARCH di Engle (Autoregressive Conditional Heteroskedasticity, 1982), che gli valse il premio Nobel nel 2003.
Scriviamo il rendimento come una media condizionale piu uno shock:
Qui e la varianza condizionale — la varianza di dato tutto cio che e noto fino al tempo — e e un'innovazione standardizzata (normale standard nel caso piu semplice). La parola condizionale fa tutto il lavoro: incondizionatamente la varianza puo essere costante, ma condizionata a ieri si muove.
L'ARCH() di Engle rende la varianza di oggi una somma pesata degli ultimi shock al quadrato:
con e per mantenere la varianza positiva. Questo cattura direttamente il clustering: un grande shock spinge in alto , che aumenta la probabilita di un altro grande shock, che mantiene la varianza elevata. Il problema e il decadimento empirico. La persistenza della volatilita nei mercati reali si estende su molti ritardi, quindi per adattarla un modello ARCH ha bisogno di un elevato — spesso 8, 10 o piu — e cio significa stimare un lungo e instabile vettore di che tende all'overfitting.
L'intuizione di Bollerslev nel 1986 fu aggiungere un termine che assorbe tutta quella persistenza con un singolo parametro. La ricorsione GARCH(1,1) — ARCH generalizzato — e:
Tre parametri, tre interpretazioni pulite:
- — il livello base o pavimento. Una costante che ancora il livello di lungo periodo della varianza. La varianza non decade mai sotto cio che sostiene.
- — la reazione alle notizie. Quanto violentemente la varianza risponde alla sorpresa di ieri . Un grande significa che la varianza condizionale e nervosa e sensibile agli shock.
- — la persistenza o memoria. Quanta parte della varianza di ieri si trasferisce a oggi. Un grande significa che la volatilita e liscia e lenta a svanire — la calma resta calma, le tempeste restano tempestose.
L'eleganza sta nella ricorsione. Poiche conteneva a sua volta un termine , espandendo all'indietro si mostra che GARCH(1,1) e un ARCH() con pesi geometricamente decrescenti sugli shock al quadrato passati:
Quindi il singolo ti compra una memoria infinita, pesata esponenzialmente, degli shock passati. E per questo che un semplice GARCH(1,1) — tre parametri — batte abitualmente modelli ARCH con dieci, ed e per questo che e diventato il cavallo da tiro della modellazione applicata della volatilita. E, di fatto, un cugino stretto dello stimatore di varianza EWMA di RiskMetrics, che e il caso speciale , con fissato a 0,94. GARCH lo generalizza lasciando che siano i dati a scegliere , e un genuino livello di mean-reversion.
Proprieta: stazionarieta, varianza di lungo periodo e half-life
La ricorsione GARCH(1,1) ha alcune proprieta che vale la pena derivare, perche sono cio che ti permette di ragionare sul modello anziche limitarti ad adattarlo alla cieca.
Varianza incondizionata (di lungo periodo). Assumiamo che il processo sia stazionario in covarianza cosicche la varianza incondizionata esista e sia costante nel tempo. Prendiamo il valore atteso di entrambi i lati della ricorsione. Poiche :
Questo e il livello verso cui la volatilita fa mean-reversion. Esiste — ed e positivo — solo quando .
Condizione di stazionarieta. La stessa disuguaglianza,
e la condizione di stazionarieta in covarianza per GARCH(1,1). La quantita e la persistenza del processo di varianza: e il coefficiente AR(1) che governa quanto uno shock di varianza decade verso . Se , la varianza incondizionata e infinita (o non definita) e gli shock non si estinguono mai completamente.
Possiamo vedere la mean-reversion esplicitamente. Definiamo la deviazione di varianza . Un po' di algebra sulla ricorsione (sostituendo ) da, in valore atteso:
Il divario tra la varianza corrente e il suo livello di lungo periodo si riduce di un fattore a ogni passo. Questa e esattamente la previsione multi-step che useremo piu avanti.
Half-life della volatilita. Quanto tempo serve perche uno shock di varianza decada a meta strada verso la normalita? Poniamo e risolviamo:
Per l'half-life e di circa 13,5 giorni; per e di circa 34 giorni; per e di circa 69 giorni. Questo singolo numero e spesso piu intuitivo dei parametri grezzi — ti dice, nelle unita delle tue barre, quanto e appiccicosa la volatilita.
Il problema near-IGARCH nel crypto. Ecco la ruga specifica del crypto. Quando adatti GARCH(1,1) ai rendimenti di BTC o ETH, trovi quasi sempre molto vicino a 1 — valori di 0,98, 0,99, a volte 0,995 sono di routine. Questo e il regime near-IGARCH (Integrated GARCH). Ha conseguenze reali:
- L'half-life diventa enorme (settimane o mesi), quindi il modello tratta la volatilita come molto persistente e a stento in mean-reversion.
- La stima di diventa estremamente sensibile: un piccolo cambiamento di da 0,99 a 0,995 raddoppia la varianza di lungo periodo implicita. Non fidarti mai della stima puntuale della volatilita di lungo periodo in questo regime senza un intervallo di confidenza.
- Le previsioni multi-step fanno mean-reversion cosi lentamente che, per orizzonti pratici sotto un paio di settimane, GARCH si comporta quasi come un random walk nella varianza (che e cio che EWMA assume).
Se la near-integrazione sia genuina o un artefatto di rotture strutturali (uno spostamento permanente del livello di volatilita letto dal modello come un unico lungo episodio persistente) e un dibattito reale. E una ragione in piu per riadattare su finestre mobili anziche adattare una volta sola su tutta la storia, un punto su cui torniamo tra le insidie. La struttura a regimi in particolare e gestita meglio da un modello di switching esplicito — vedi rilevamento dei regimi con modelli di Markov nascosti, che e complementare a GARCH anziche un sostituto.
Stima per massima verosimiglianza
I parametri GARCH si stimano per massima verosimiglianza. La logica e diretta: dato , la ricorsione produce un intero percorso di varianze condizionali , e sotto una distribuzione assunta per le innovazioni possiamo scrivere quanto siano probabili i rendimenti osservati. Scegliamo poi per massimizzare quella verosimiglianza.
Assumiamo innovazioni gaussiane , cosicche . La densita condizionale di un'osservazione e
Poiche il modello e scritto in forma condizionale, la verosimiglianza congiunta si fattorizza in un prodotto di densita a un passo, e la log-verosimiglianza e una semplice somma:
Due fatti strutturali da notare. Primo, compare sia come penalita ( — il modello e punito per dichiarare una varianza alta) sia nel residuo standardizzato ( — il modello e punito per essere sorpreso). L'ottimo bilancia i due, ed e questo che fa seguire la varianza. Secondo, la ricorsione ha bisogno di un seme ; la scelta usuale e la varianza campionaria dei rendimenti, e con qualche migliaio di osservazioni il seme conta a stento.
Non c'e forma chiusa per il massimizzatore, quindi ottimizziamo numericamente (arch usa un metodo quasi-Newton con gradienti analitici o numerici). La superficie di verosimiglianza e generalmente ben condizionata per GARCH(1,1), ma due cose mordono in pratica: i vincoli di positivita () e il comportamento vicino al confine quando , dove l'ottimizzatore puo avanzare lentamente. Entrambi sono gestiti per te da una buona libreria — e dovresti usarne una. Costruirsi a mano una MLE per GARCH e un buon esercizio didattico ma una cattiva scelta di produzione.
La libreria arch
Il pacchetto arch di Kevin Sheppard e lo strumento standard in Python. L'intero fit sta in quattro righe.
from arch import arch_model
r = ret * 100.0
model = arch_model(r, mean="Constant", vol="Garch", p=1, q=1, dist="normal")
res = model.fit(disp="off")
print(res.summary())
Una parola sui nomi degli argomenti, perche sono una comune fonte di confusione. In arch, p e il numero di varianze ritardate (termini , l'ordine GARCH) e q e il numero di residui al quadrato ritardati (termini , l'ordine ARCH). Quindi p=1, q=1 e il GARCH(1,1) che abbiamo derivato. (La notazione originale di Bollerslev lo scrive GARCH() con per l'ordine ARCH — le due convenzioni sono trasposte. Fidati della documentazione della libreria, non della tua memoria.)
Leggendo il sommario, la tabella dei coefficienti appare all'incirca cosi (valori illustrativi per i rendimenti giornalieri di BTC, non un esperimento reale):
Volatility Model
==========================================================
coef std err t P>|t|
----------------------------------------------------------
omega 0.4821 0.201 2.40 0.016
alpha[1] 0.0912 0.021 4.34 0.000
beta[1] 0.8994 0.024 37.5 0.000
==========================================================
Come leggerla:
alpha[1] + beta[1]= 0.0912 + 0.8994 = 0.9906. Persistenza appena sotto 1 — il regime near-IGARCH, esattamente come avvertito. Half-life giorni.omega= 0.4821, quindi la varianza di lungo periodo e in unita percentuale al quadrato, cioe una volatilita giornaliera di lungo periodo di , ovvero all'incirca annualizzata. E un numero plausibile per BTC.- Sia
alphachebetasono fortemente significativi. Chealphasia piccolo rispetto abetae tipico: la varianza crypto e per lo piu persistenza (memoria), con una reazione modesta ma reale a nuovi shock.
La trappola dello scaling ×100
Questo e il modo singolo piu comune di ottenere assurdita da arch, quindi si guadagna la sua sottosezione. L'ottimizzatore funziona meglio quando i numeri che vede sono a . I log-rendimenti giornalieri sono , quindi i loro quadrati sono e deve stare attorno a — in un intervallo dove i gradienti numerici perdono precisione e il fit puo silenziosamente non convergere o restituire errori standard spazzatura.
La soluzione e adattare su rendimenti scalati per 100 (cioe in percentuale), come sopra. arch emettera persino un DataScaleWarning se te ne dimentichi. Tutto cio che leggi dal modello e allora in unita percentuali o percentuali al quadrato, e devi descalare in modo coerente:
sigma_pct = np.sqrt(res.forecast(horizon=1).variance.iloc[-1, 0])
sigma_decimal = sigma_pct / 100.0
print(f"1-day-ahead conditional vol: {sigma_decimal:.2%}")
Mescolare quantita scalate e non scalate — dare, poniamo, una volatilita percentuale a una formula di dimensionamento che si aspetta decimali — produce errori di esattamente 100x, facili da non notare perche il codice gira senza problemi. Scegli una convenzione (io tengo tutto in decimali fuori dal fit e scalo solo al confine di arch) e non attraversarla mai.
Previsione della varianza condizionale
Un modello adattato e utile solo se prevede. GARCH fornisce previsioni pulite e analitiche a qualunque orizzonte.
Un passo avanti. Al tempo (la fine del campione) conosciamo e , quindi la varianza successiva e deterministica:
Nessun valore atteso necessario — tutto a destra e osservato.
Multi-step avanti. Per non conosciamo ancora gli shock intermedi, quindi prendiamo valori attesi condizionali. Usando (perche ), la ricorsione collassa in un semplice AR(1) nella varianza prevista:
Iterando questo dalla previsione a un passo si ottiene la forma chiusa, che e il risultato di mean-reversion derivato prima scritto esplicitamente:
Leggi questo con attenzione, perche e la geometria di ogni previsione GARCH. La struttura a termine della varianza parte dalla varianza condizionale di oggi e decade geometricamente verso il livello di lungo periodo . Se oggi e piu calmo della media, la curva di previsione sale verso ; se oggi e una crisi, scende verso di esso. La velocita di quel decadimento e fissata interamente da — e nel regime crypto near-IGARCH, dove , il decadimento e cosi lento che per orizzonti sotto un paio di settimane la previsione si muove a stento dal livello di oggi. Vale la pena interiorizzarlo: per periodi di detenzione brevi, la previsione GARCH crypto e essenzialmente domani assomiglia a oggi, solo con un ritorno alla media molto lento.
Aggregazione a un orizzonte di detenzione. I trader raramente si curano della varianza di un singolo giorno futuro. Se detieni una posizione per giorni e i rendimenti sono condizionatamente incorrelati (il fatto stilizzato dall'inizio), la varianza del rendimento cumulato di giorni e la somma delle varianze previste a un giorno:
Questo e il numero contro cui effettivamente dimensioni — la volatilita del P&L sul tuo periodo di detenzione. Nota che non e affatto lo scaling ingenuo , che e corretto solo se la varianza e costante. Quando la varianza di oggi e sopra , la previsione con ritorno alla media rende la vera volatilita di giorni piu bassa della regola della radice quadrata; quando oggi e calmo, e piu alta. Ottenere questo correttamente e la differenza tra uno stop che rispetta la struttura a termine e uno che non lo fa.
Nel codice:
H = 10
fc = res.forecast(horizon=H, reindex=False)
var_path_pct2 = fc.variance.iloc[-1].values # [E_T sigma^2_{T+1}, ..., T+H]
var_path = var_path_pct2 / (100.0 ** 2) # back to decimal variance
daily_vol = np.sqrt(var_path)
print("Forecast daily vol path:", np.round(daily_vol * 100, 2), "%")
H_day_var = var_path.sum()
H_day_vol = np.sqrt(H_day_var)
print(f"{H}-day holding-period vol: {H_day_vol:.2%}")
naive = np.sqrt(var_path[0] * H)
print(f"Naive sqrt(H) * sigma_1: {naive:.2%}")
Per orizzonti piu lunghi GARCH supporta anche previsioni di simulazione (method="simulation"), che propagano in avanti la distribuzione delle innovazioni e ti danno l'intera densita di previsione, non solo la sua varianza — utile quando le innovazioni sono non gaussiane, come saranno una volta che passeremo alle distribuzioni Student-t e asimmetriche nella Parte 2. Per le quantita lineari nella varianza qui sopra, il percorso analitico e esatto e gratuito.
Diagnostica: il modello ha davvero funzionato?
Adattare un modello non e la stessa cosa che validarlo. Tutto il punto di GARCH e assorbire l'eteroschedasticita condizionale — il clustering di volatilita — cosicche cio che rimane sia (vicino a) i.i.d. Il controllo giusto e quindi guardare i residui standardizzati
e chiedersi: il clustering e sparito? Se il modello ha catturato la dinamica della varianza, gli dovrebbero avere varianza unitaria e, soprattutto, i loro quadrati non dovrebbero mostrare autocorrelazione residua. Eseguiamo tre test.
1. Ljung-Box sui residui standardizzati. Verifica che non resti autocorrelazione lineare nel livello di (questo in realta testa il modello della media, non il modello della varianza). Non dovrebbe rifiutare.
2. Ljung-Box sui residui standardizzati al quadrato. Questo e quello importante. Se ha ancora autocorrelazione significativa, il modello di varianza non e riuscito a rimuovere il clustering — c'e struttura che GARCH(1,1) non ha catturato, e potresti aver bisogno di un ordine piu alto, di una variante asimmetrica o di una diversa distribuzione delle innovazioni. Non dovrebbe rifiutare.
3. Test ARCH-LM (test dei moltiplicatori di Lagrange di Engle). Regredisci sui propri ritardi e testa la significativita congiunta. E essenzialmente una versione formale del test 2 e chiede direttamente c'e un effetto ARCH rimasto? Un risultato non significativo conferma che l'eteroschedasticita condizionale e stata modellata via.
from statsmodels.stats.diagnostic import acorr_ljungbox
from arch.unitroot.cointegration import engle_granger # (unrelated; shown for import clarity)
z = res.std_resid.dropna() # standardized residuals
z2 = z ** 2
lb_z = acorr_ljungbox(z, lags=[10, 20], return_df=True)
lb_z2 = acorr_ljungbox(z2, lags=[10, 20], return_df=True)
print("Ljung-Box on standardized residuals:\n", lb_z, "\n")
print("Ljung-Box on SQUARED standardized residuals:\n", lb_z2, "\n")
lm = res.arch_lm_test(lags=10, standardized=True)
print(lm)
print(f"\nStd-resid excess kurtosis: {stats.kurtosis(z):.2f}")
Come appare un buon output: i p-value di Ljung-Box su saltano da quasi zero (sui rendimenti al quadrato grezzi) a comodamente sopra 0,05, e il test ARCH-LM non riesce a rifiutare. Questa e la tua prova che il modello ha fatto il suo lavoro sul secondo momento.
Come appare un output imperfetto — e cosa dovresti aspettarti con un semplice GARCH(1,1) gaussiano sul crypto — e che i test di clustering passano ma la curtosi dei residui standardizzati e ancora elevata (diciamo 4-6 anziche 0). GARCH rimuove il clustering ma resta una singola distribuzione incondizionata a code pesanti, perche le innovazioni gaussiane non possono riprodurre le code. Quella pesantezza residua delle code non e un bug da correggere qui; e la motivazione della Parte 2, GARCH asimmetrico e l'effetto leva nel crypto, dove le innovazioni Student-t e skewed-t e il termine di asimmetria GJR/EGARCH affrontano esattamente questo.
Applicazione: dimensionamento e stop scalati sulla volatilita
Abbiamo ora una previsione della volatilita di domani (e dei prossimi giorni). Cosa ne facciamo? I due usi piu semplici e di maggior valore sono il dimensionamento delle posizioni e il posizionamento degli stop. Manteniamo entrambi deliberatamente basilari qui — la strategia completa di vol targeting con tutto il suo apparato pratico e la Parte 4.
Dimensionamento delle posizioni con target di volatilita
L'idea e detenere una posizione il cui contributo di rischio sia grosso modo costante nel tempo, anziche una posizione il cui nozionale sia costante. Se dispieghi sempre la stessa dimensione in dollari, il tuo rischio gonfia nei regimi ad alta volatilita e si rattrappisce in quelli calmi — l'opposto di cio che vuoi. Il volatility targeting inverte questo: punta a una volatilita target fissa del P&L, e lascia che la previsione detti la dimensione.
Per una volatilita annualizzata target (diciamo 20%) e una volatilita annualizzata prevista , il peso di posizione e
Quando la volatilita prevista e alta, riduci la scala; quando e bassa, la aumenti. Questo e l'intero meccanismo. Poiche e previsto — noto in prima che il rendimento in sia realizzato — non c'e look-ahead, a patto che tu sia disciplinato sul timing (piu su questo nelle insidie).
def vol_target_weight(sigma_forecast_annual, sigma_target_annual=0.20,
w_max=3.0):
"""Volatility-scaled position weight. Inputs/outputs in decimals.
w_max caps leverage so a tiny forecast vol can't demand insane size."""
w = sigma_target_annual / sigma_forecast_annual
return float(np.clip(w, 0.0, w_max))
sigma_1d = np.sqrt(res.forecast(horizon=1).variance.iloc[-1, 0]) / 100.0
sigma_ann = sigma_1d * np.sqrt(365)
w = vol_target_weight(sigma_ann, sigma_target_annual=0.20)
print(f"Forecast annual vol: {sigma_ann:.1%} -> position weight: {w:.2f}x")
Questo e un cugino di primo grado delle regole appropriate di allocazione del capitale. Il volatility targeting risponde alla domanda quanto dovrebbe scalare il rischio con la volatilita, mentre il criterio di Kelly risponde a quanto dovrebbe scalare il rischio con l'edge — e i due si moltiplicano insieme in uno stack di dimensionamento completo: dimensione edge / varianza. Nota che il termine di varianza di Kelly e esattamente la previsione GARCH che hai appena calcolato, ed e per questo che un modello di volatilita live affina materialmente il dimensionamento di Kelly rispetto a una stima storica statica. Se la tua stima di edge porta a sua volta incertezza quantificata, la conformal prediction offre un modo distribution-free per ampliare o restringere la dimensione di conseguenza, e si compone in modo pulito con il vol targeting.
Il cap w_max non e opzionale. Nel regime near-IGARCH un tratto tranquillo puo spingere la volatilita prevista piuttosto in basso, e richiedera una leva che va bene sulla carta e rovinosa quando la calma si rompe — cosa che, per il clustering di volatilita, alla fine fa, spesso bruscamente. Limitare la leva e il riconoscimento rozzo ma efficace che la tua previsione e una media condizionale, non una garanzia, e che il payoff dell'aver torto e asimmetrico. Quell'asimmetria — un conto fatto saltare non e recuperabile con una vincita simmetrica — e esattamente l'asimmetria perdita-profitto che dovrebbe renderti sistematicamente piu prudente di quanto suggerisca una regola basata sulla sola varianza.
Stop scalati sulla volatilita
Uno stop a percentuale fissa ha la stessa malattia di una dimensione di posizione fissa: uno stop al 3% e un grilletto ipersensibile in un mercato calmo e un errore di arrotondamento in uno violento. Ti fa buttare fuori da buone posizioni per rumore ordinario durante i regimi ad alta volatilita e restituisce troppo durante le transizioni. La soluzione e impostare la distanza dello stop in unita di volatilita prevista.
dove e la volatilita prevista sul tuo orizzonte di detenzione atteso (la quantita aggregata dalla sezione sulla previsione) e e un multiplo — tipicamente da 1,5 a 3 — scelto in modo che lo stop sia fuori dalle fluttuazioni normali ma dentro un genuino movimento avverso.
def vol_scaled_stop(entry_price, side, sigma_H, k=2.0):
"""
entry_price : fill price
side : +1 long, -1 short
sigma_H : forecast volatility over the holding horizon (decimal)
k : stop width in vol units
Returns the stop price.
"""
stop_frac = k * sigma_H
return entry_price * (1.0 - side * stop_frac)
var_path = res.forecast(horizon=10, reindex=False).variance.iloc[-1].values / (100.0 ** 2)
sigma_H = np.sqrt(var_path.sum())
entry = float(px.iloc[-1])
stop = vol_scaled_stop(entry, side=+1, sigma_H=sigma_H, k=2.0)
print(f"Entry {entry:,.0f} | 10-day vol {sigma_H:.2%} | 2-sigma stop {stop:,.0f}")
Poiche usa la previsione con struttura a termine e ritorno alla media anziche un numero storico piatto, lo stop si allarga automaticamente entrando in regimi turbolenti e si stringe quando la volatilita si placa — la struttura a termine fa l'adattamento per te. E la stessa previsione che alimenta sia la dimensione sia lo stop, il che e un pregio: in un regime ad alta volatilita detieni simultaneamente meno e dai alla posizione piu spazio, e i due effetti si compongono in un rischio di coda materialmente inferiore. Dimensionamento e stop sono due proiezioni di un'unica visione della volatilita, non due manopole indipendenti.
Questo e quanto ci spingiamo con l'applicazione nella Parte 1. Una strategia reale deve gestire i costi di transazione da ribilanciamento costante, il timing di quando la previsione e calcolata rispetto a quando l'ordine e piazzato, il controllo del turnover e — soprattutto — una valutazione out-of-sample onesta. Tutto cio e la Parte 4: la strategia GARCH di volatility targeting, dove costruiamo e testiamo in walk-forward l'intera cosa.
Insidie
GARCH e facile da adattare e facile con cui ingannarsi. Le modalita di fallimento sono ricorrenti.
Scaling dei rendimenti. Trattato sopra, ma e il bug numero uno, quindi vale la pena ripeterlo: adatta arch su rendimenti × 100, e descala ogni output (varianza per , volatilita per ). Un silenzioso errore di 100x qui avvelena ogni calcolo a valle di dimensionamento e stop.
Look-ahead nel fitting. Il killer sottile. Se adatti il modello sull'intera storia e poi calcoli previsioni su quella stessa storia, ogni previsione ha visto segretamente il futuro — i parametri sono stati stimati usando dati successivi alla data di previsione. Il fit in-sample apparira meraviglioso e la performance live non gli assomigliera affatto. Ogni previsione backtestata deve provenire da un modello adattato solo su dati disponibili in quel momento: riadatta su una finestra espandente o mobile, prevedi un passo, avanza. Questo non e negoziabile ed e l'intero soggetto della walk-forward optimization. Il divario tra un GARCH in-sample e uno correttamente walk-forward e il divario tra una demo e un sistema che sopravvive al contatto con i mercati live — vedi anche parita backtest-live.
Timing della previsione. Correlato ma distinto. La previsione per il giorno deve essere calcolata dalle informazioni disponibili alla chiusura del giorno (o quando chiude la tua barra), e la posizione deve essere eseguibile a un prezzo che potresti realmente ottenere. Calcolare la previsione usando la chiusura del giorno e poi tradare all'apertura del giorno e un look-ahead che gonfia silenziosamente ogni risultato.
Overfitting di ordini alti. GARCH(1,1) e quasi sempre sufficiente. La tentazione di adattare GARCH(2,2) o GARCH(3,1) perche spinge in su la log-verosimiglianza in-sample e di solito noise-fitting; i parametri extra raramente migliorano le previsioni out-of-sample e spesso rendono l'ottimizzatore instabile vicino al confine. Preferisci il modello parsimonioso, e se devi confrontare ordini, confrontali per perdita di previsione out-of-sample su uno split walk-forward, non per AIC in-sample. Quando le diagnostiche dei residui mostrano ancora un problema, la soluzione e di solito una migliore distribuzione delle innovazioni o un termine di asimmetria (Parte 2), non un ordine piu alto.
Rotture strutturali lette come persistenza. Come notato, uno spostamento permanente del livello di volatilita (un nuovo regime di mercato, un cambiamento nella microstruttura del mercato) puo essere assorbito da GARCH come una persistenza spuriamente alta, spingendo verso 1. Se la tua stima di volatilita di lungo periodo appare instabile tra le finestre, sospetta una rottura anziche fidarti della stima puntuale near-IGARCH. Riadattamenti mobili e, dove appropriato, un modello di regimi esplicito proteggono da questo.
Trattare le previsioni di volatilita come previsioni di rendimento. GARCH prevede l'ampiezza dei movimenti, non la loro direzione. Ti dice quanto e probabile che sia grande l'oscillazione di domani, non in che verso. E esattamente per questo che la sua casa naturale e la gestione del rischio — dimensionamento, stop, VaR — anziche la generazione di segnali. Non confondere una buona previsione di varianza per un edge.
Dove va tutto questo
GARCH(1,1) e la fondazione, ed e deliberatamente incompleto. La serie ci costruisce sopra in tre direzioni:
- Asimmetria e code pesanti — la vera volatilita crypto risponde di piu ai movimenti al ribasso che a quelli al rialzo (l'effetto leva), e le innovazioni gaussiane non possono riprodurre le code. GJR-GARCH, EGARCH e le innovazioni Student-t / skewed-t sono la Parte 2.
- Volatilita multivariata — le correlazioni tra asset crypto sono esse stesse variabili nel tempo e schizzano nei crash. Modellare l'intera matrice di covarianza in modo dinamico e la Parte 3: DCC-GARCH, che si collega direttamente alla media-varianza di Markowitz e all'allocazione basata su CVaR una volta che la covarianza e dinamica.
- La strategia completa — dimensionamento, stop, costi, turnover e valutazione walk-forward onesta si uniscono nella Parte 4.
E dove le marginali GARCH alimentano il rischio congiunto: il modello univariato di varianza condizionale qui e esattamente il primo stadio della pipeline GARCH-EVT-copula per il VaR/CVaR di portafoglio. Una volta che hai i residui standardizzati da un fit GARCH per asset, li trasformi e li incolli insieme con una copula — le marginali sono GARCH, la dipendenza e la copula. Quella costruzione, inclusa la dipendenza di coda e il trattamento EVT delle code, e trattata in profondita nei modelli copula per il rischio crypto congiunto; questo articolo e il motore univariato che ci sta sotto.
Riepilogo
- I rendimenti crypto mostrano clustering di volatilita, code pesanti e nessuna autocorrelazione dei rendimenti ma forte autocorrelazione dei rendimenti al quadrato. Qualsiasi strumento che assume volatilita costante — Black-Scholes con un unico , VaR statico, stop a percentuale fissa — e mal specificato rispetto a questi fatti.
- GARCH(1,1), , modella la varianza condizionale variabile nel tempo con tre parametri: un livello base , una reazione allo shock e una persistenza . E un ARCH() con memoria geometricamente decrescente, ed e per questo che batte l'ARCH di ordine alto.
- La stazionarieta richiede ; la varianza di lungo periodo e , la persistenza e e l'half-life della volatilita e . Il crypto si colloca nel regime near-IGARCH (): molto persistente, lento al ritorno alla media e con una fragile stima della varianza di lungo periodo.
- Stima per massima verosimiglianza. La log-verosimiglianza gaussiana e una somma di densita a un passo; adattala con
arch_model(r*100, vol="Garch", p=1, q=1). Ricorda lo scaling ×100 e descala ogni output in modo coerente. - Le previsioni fanno mean-reversion geometricamente verso la varianza di lungo periodo al tasso . Aggrega le previsioni di varianza giornaliera per ottenere la volatilita dell'orizzonte di detenzione — non la regola ingenua .
- Valida con Ljung-Box sui residui standardizzati al quadrato e con il test ARCH-LM. Superarli conferma che il clustering e stato modellato via; le code pesanti residue che restano motivano la Parte 2.
- Applicalo al dimensionamento con target di volatilita (, con cap) e agli stop scalati sulla volatilita (). Un'unica previsione guida entrambi, quindi i regimi ad alta volatilita ottengono simultaneamente dimensione piu piccola e stop piu ampi.
- Le insidie che contano: scaling dei rendimenti, look-ahead nel fitting (adatta solo su dati passati, sempre walk-forward), timing della previsione, ordini eccessivi, e non scambiare mai una previsione di varianza per una previsione di direzione.
Bibliografia:
- Engle, R. F. (1982). Autoregressive Conditional Heteroscedasticity with Estimates of the Variance of United Kingdom Inflation. Econometrica, 50(4), 987-1007. DOI
- Bollerslev, T. (1986). Generalized Autoregressive Conditional Heteroskedasticity. Journal of Econometrics, 31(3), 307-327. DOI
- Mandelbrot, B. (1963). The Variation of Certain Speculative Prices. The Journal of Business, 36(4), 394-419. DOI
- Nelson, D. B. (1991). Conditional Heteroskedasticity in Asset Returns: A New Approach. Econometrica, 59(2), 347-370. DOI
- Katsiampa, P. (2017). Volatility estimation for Bitcoin: A comparison of GARCH models. Economics Letters, 158, 3-6. DOI
- Chu, J., Chan, S., Nadarajah, S., & Osterrieder, J. (2017). GARCH Modelling of Cryptocurrencies. Journal of Risk and Financial Management, 10(4), 17. DOI
- Sheppard, K. (2023). arch: Autoregressive Conditional Heteroskedasticity (ARCH) and other tools for financial econometrics in Python. GitHub.
MarketMaker.cc Team
Ricerca Quantitativa e Strategia