← Terug naar artikelen
July 13, 2026
5 min leestijd

Volatility Targeting en handelen met GARCH-voorspellingen

#volatility
#GARCH
#volatility-targeting
#backtesting
#risk
#crypto
#algorithmic-trading

De eerste drie delen van deze serie leerden je volatiliteit te voorspellen. We bouwden een univariate GARCH(1,1) in Deel 1, voegden leverage en dikke staarten toe met GJR- en Student-t-innovaties in Deel 2, en modelleerden een volledige covariantiematrix door de tijd heen met DCC-GARCH in Deel 3. Aan het einde van elk deel printten we een getal: de verwachte volatiliteit van morgen. En daarna, als we eerlijk zijn, stopten we — alsof het produceren van de voorspelling het doel was.

Dat is het niet. Een volatiliteitsvoorspelling is geen P&L. Niemand is ooit betaald voor een lage QLIKE-score. Een voorspelling wordt pas iets waard op het exacte moment dat ze een beslissing verandert die je anders anders had genomen — hoeveel te kopen, wanneer af te bouwen, hoeveel kapitaal toe te wijzen. Als je voorspelling geen positie verschuift, is haar statistische nauwkeurigheid een privéhobby.

Dit laatste deel gaat over het sluiten van die lus. We nemen de voorspellingen uit Deel 1-3 en zetten ze aan het werk in de schoonste denkbare beslissing: volatility targeting — een positie zo dimensioneren dat de gerealiseerde volatiliteit van de portefeuille een constant doel raakt. Vervolgens doen we het ding waar deze blog meer om geeft dan om welk afzonderlijk model dan ook: we beoordelen eerlijk. We benchmarken GARCH tegen domme-maar-sterke basislijnen (rollende realized vol, EWMA), we gebruiken verliesfuncties die robuust zijn tegen het feit dat we ware volatiliteit nooit kunnen waarnemen, we draaien een walk-forward backtest met kosten en zonder look-ahead, en we stellen onomwonden vast wat vol targeting je wel en niet oplevert. Spoiler: het verbetert risicogecorrigeerde rendementen en temt drawdowns veel betrouwbaarder dan dat het alpha fabriceert.

Waarom Volatility Targeting de juiste test is

Er zijn duurdere manieren om een volatiliteitsvoorspelling te gebruiken — optieprijzen, VaR-limieten, dynamisch hedgen — maar volatility targeting is degene die de waarde van de voorspelling isoleert met de minste besmetting door andere weddenschappen. Het idee is één enkele vergelijking.

Houd een risicovol activum aan met een door het signaal geïmpliceerde richting (voorlopig gewoon "long"). In plaats van een vaste positie schaal je de blootstelling omgekeerd evenredig met de voorspelde volatiliteit:

wt=σtargetσ^t(then capped)w_t = \frac{\sigma_{\text{target}}}{\hat{\sigma}_{t}} \quad \text{(then capped)}

waarbij σ^t\hat{\sigma}_t je voorspelling is van de volatiliteit in de volgende periode (gemaakt met uitsluitend data tot en met tt), en σtarget\sigma_{\text{target}} de geannualiseerde volatiliteit is waarmee je wilt dat de strategie draait — bijvoorbeeld 15% of 20%. Wanneer het model een kalme markt voorspelt, verhoog je de leverage richting (of voorbij) 1,0; wanneer het een storm voorspelt, krimp je. De gerealiseerde volatiliteit van de geschaalde positie is, tot op eerste orde,

Vol(wtrt+1)=wtσt+1=σtargetσ^tσt+1σtarget\text{Vol}(w_t r_{t+1}) = w_t \, \sigma_{t+1} = \frac{\sigma_{\text{target}}}{\hat\sigma_t}\, \sigma_{t+1} \approx \sigma_{\text{target}}

wanneer σ^tσt+1\hat\sigma_t \approx \sigma_{t+1}. De hele kwaliteit van de oefening rust dus op één ding: hoe dicht je voorspelling σ^t\hat\sigma_t ligt bij de werkelijke volatiliteit van de volgende periode σt+1\sigma_{t+1}. Een betere voorspelling levert een vlakker gerealiseerd-volatiliteitsprofiel op en — zoals we zullen zien — een betere Sharpe-ratio. Daarom is dit de juiste test. De voorspelling is geen decoratie; ze is de noemer.

Waarom dit de Sharpe verhoogt en drawdowns beheerst

Twee empirische feiten doen hier het werk.

Volatiliteit is veel beter voorspelbaar dan rendementen. De richting van het BTC-rendement van morgen is op dagelijkse frequentie bijna een muntworp; de magnitude niet. Volatiliteit clustert — grote bewegingen volgen op grote bewegingen — wat de hele reden is dat GARCH bestaat (Deel 1 leidde de AR(1)-in-variantie-structuur af die dit codeert). Een R2R^2 van 0,4-0,6 voor variantie één dag vooruit is routine; hetzelfde getal voor rendementen zou een signaal van Renaissance-kaliber zijn. Vol targeting benut de voorspelbare grootheid en blijft agnostisch over de onvoorspelbare.

Sharpe-ratio's zijn niet constant door de tijd heen; ze dalen wanneer de volatiliteit piekt. Regimes met hoge volatiliteit in crypto — deleveraging-cascades, exchange-failures, de dagen waarop alles 30% gapt — hebben doorgaans een slechter rendement-per-eenheid-risico, niet beter. Door de blootstelling mechanisch te verlagen precies wanneer de voorspelde vol hoog is, weeg je de perioden onder die het meest bijdragen aan drawdowns en het minst aan samengesteld rendement. Moreira en Muir (2017) toonden voor aandelen aan dat volatility-managed portefeuilles — precies deze 1/σ21/\sigma^2-schaling — Sharpe-ratio's verhogen en positieve alpha's produceren tegenover de ongemanagede factor. Het mechanisme is geen magie; het is weigeren om een constante dollarpositie de voorspelbaar turbulente vensters in te dragen.

Het drawdown-voordeel is nog directer. Maximale drawdown wordt gedomineerd door de staart van de verdeling van positierendementen. Aangezien wtrt+1w_t r_{t+1} een volatiliteit heeft die dicht bij σtarget\sigma_{\text{target}} is vastgepind, wordt de dikke linkerstaart die een strategie met vaste notional lijdt tijdens een vol-explosie samengedrukt: je was al klein toen je erin ging. Vol targeting voorspelt geen crashes, maar is systematisch onderblootgesteld wanneer de markt geagiteerd is, en agitatie is wanneer crashes gebeuren.

Verband met Kelly en fractionele positiegrootte

Volatility targeting is een naaste neef van het Kelly-criterium. Voor een enkel activum met verwacht excess-rendement μ\mu en variantie σ2\sigma^2 is de groei-optimale (full-Kelly) fractie

f=μσ2.f^\star = \frac{\mu}{\sigma^2}.

Als je aanneemt dat de Sharpe-ratio μ/σ\mu/\sigma ruwweg constant is — een sterke aanname, maar degene die impliciet in "de markt betaalt een stabiele prijs voor risico" zit — dan is μ=Sσ\mu = S\sigma en f=S/σf^\star = S/\sigma, wat exact volatility targeting is met σtarget=S(Kelly fraction)\sigma_{\text{target}} = S \cdot (\text{Kelly fraction}). Met andere woorden: volatility targeting is Kelly-positiegrootte onder de aanname dat het verwachte rendement met de volatiliteit meeschaalt. We werken full en fractionele Kelly door, en waarom niemand die bij zinnen is full Kelly handelt, in Kelly-criterium en strategie-positiegrootte. De praktische les daarvandaan draagt over: gebruik een fractie van de theoretische grootte, omdat schattingsfout in de noemer (je vol-voorspelling) en vooral de teller (verwacht rendement) full sizing gevaarlijk agressief maakt.

Twee andere verbanden die het waard zijn in gedachten te houden. Ten eerste dimensioneert vol targeting op de symmetrische spreiding van rendementen, maar crypto-payoffs zijn niet symmetrisch — de kost van een dag met 20% omlaag is niet het spiegelbeeld van een dag met 20% omhoog zodra leverage en liquidatie in het spel zijn. We behandelen die asymmetrie direct in verlies-winst-asymmetrie, en een GJR/EGARCH-voorspelling (Deel 2) bakt er al iets van in σ^t\hat\sigma_t door sterker te reageren op negatieve schokken. Ten tweede is een vol-voorspelling een puntschatting; een completer risicobeeld hangt er een interval aan. Conformal prediction voor trading laat zien hoe je modeloutput omzet in distributievrije intervallen waartegen je kunt dimensioneren, wat natuurlijk aansluit bij het eerlijke-evaluatie-thema van dit artikel.

De concurrenten: wat GARCH moet verslaan

Hier is de discipline die een echte evaluatie van een demo scheidt. Voordat je GARCH kroont, moet je haar tegenstanders geven die goedkoop, voor de hand liggend en verrassend moeilijk te verslaan zijn. Als je uitgebreide GJR-t-model een EWMA van vijf regels niet kan overtreffen, heb je iets waardevols geleerd en jezelf veel productiecomplexiteit bespaard.

We benchmarken vier voorspellers van de volatiliteit in de volgende periode.

(a) Trailing realized volatility (rollende standaarddeviatie)

De meest naïeve voorspelling: de volatiliteit van morgen is gelijk aan de steekproefstandaarddeviatie van de laatste nn dagelijkse rendementen.

σ^tRV=1n1i=1n(rti+1rˉ)2\hat\sigma_{t}^{\text{RV}} = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}\bigl(r_{t-i+1}-\bar r\bigr)^2}

Ze heeft één hyperparameter (het venster nn, doorgaans 20-60 dagen) en geen model. Haar gebrek is dat elke observatie in het venster gelijk gewicht krijgt en dan abrupt van een klif valt wanneer ze het venster verlaat — het "ghosting"- of "echo"-effect, waarbij een enkele crash-dag de voorspelling opblaast voor exact nn dagen en dan van de ene op de andere dag verdwijnt, of de markt nu daadwerkelijk gekalmeerd is of niet.

(b) EWMA / RiskMetrics (λ=0.94\lambda = 0.94)

Het exponentieel gewogen voortschrijdend gemiddelde repareert het echo-probleem door geometrisch afnemend gewicht te geven aan oudere gekwadrateerde rendementen:

σ^t2=λσ^t12+(1λ)rt2\hat\sigma_{t}^{2} = \lambda\,\hat\sigma_{t-1}^{2} + (1-\lambda)\,r_{t}^{2}

Dit is de RiskMetrics-schatter (J.P. Morgan, 1996). Met de canonieke λ=0.94\lambda = 0.94 voor dagdata is het effectieve geheugen ruwweg 1/(1λ)171/(1-\lambda) \approx 17 dagen, maar het verval is glad — geen klif. Merk op wat EWMA feitelijk is: het is een geïntegreerde GARCH(1,1) met ω=0\omega = 0 en α+β=1\alpha + \beta = 1, d.w.z. GARCH zonder mean reversion en zonder langetermijnvariantie. Ze heeft nul vrije parameters als je λ=0.94\lambda = 0.94 aanvaardt, en ze is de enige lastigste basislijn in dit hele artikel. Een groot deel van de "GARCH verslaat X"-papers slaagt er stilzwijgend niet in EWMA out of sample te verslaan.

import numpy as np
import pandas as pd

def ewma_vol(returns: pd.Series, lam: float = 0.94, sigma0: float | None = None) -> pd.Series:
    """RiskMetrics EWMA conditional volatility (returns in decimal, e.g. 0.03).

    sigma2_t = lam * sigma2_{t-1} + (1 - lam) * r_t^2
    Returns a series aligned to `returns` where value at t uses info up to t.
    """
    r2 = np.square(returns.values)
    var = np.empty_like(r2)
    var[0] = sigma0**2 if sigma0 is not None else r2[0]
    for t in range(1, len(r2)):
        var[t] = lam * var[t - 1] + (1.0 - lam) * r2[t - 1]  # note: r_{t-1}, no look-ahead
    return pd.Series(np.sqrt(var), index=returns.index, name="ewma_vol")

De ene subtiliteit waarover mensen struikelen: de voorspelling voor periode tt (bruikbaar om een positie te dimensioneren die over tt wordt aangehouden) moet worden opgebouwd uit rendementen die vóór tt zijn waargenomen. In de bovenstaande recursie gebruikt var[t] r2[t-1], dus de reeks is een echte voorspelling één stap vooruit. Deze index goed krijgen is het verschil tussen een backtest en een fantasie — meer daarover in de walk-forward-sectie.

(c) GARCH(1,1) en GJR-t (Deel 1-2)

Onze hoofdrolspelers. Standaard GARCH(1,1):

σt2=ω+αϵt12+βσt12,ω>0, α,β0, α+β<1\sigma_t^2 = \omega + \alpha\, \epsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2, \qquad \omega>0,\ \alpha,\beta\ge0,\ \alpha+\beta<1

met langetermijnvariantie σˉ2=ω/(1αβ)\bar\sigma^2 = \omega/(1-\alpha-\beta) en de voorspelling één stap vooruit die rechtstreeks uit de recursie rolt (Deel 1). De GJR-GARCH-uitbreiding voegt een leverage-term toe zodat negatieve schokken de variantie meer verhogen dan positieve:

σt2=ω+(α+γ1[ϵt1<0])ϵt12+βσt12\sigma_t^2 = \omega + (\alpha + \gamma\, \mathbb{1}[\epsilon_{t-1}<0])\,\epsilon_{t-1}^2 + \beta\,\sigma_{t-1}^2

en gekoppeld aan Student-t-innovaties om de dikke staarten van crypto af te handelen, is dit de GJR-t van Deel 2. De reden dat GARCH EWMA kan verslaan is mean reversion: na een schok trekt GARCH de voorspelling terug richting σˉ2\bar\sigma^2 met een snelheid die door α+β\alpha+\beta wordt bepaald, terwijl EWMA (die geïntegreerd is) nooit terugkeert. Wanneer de volatiliteit piekt en dan normaliseert — het gangbare geval — vervalt de voorspelling van GARCH sneller en nauwkeuriger. Wanneer de volatiliteit werkelijk persistent is, zijn de twee bijna niet van elkaar te onderscheiden.

(d) HAR-RV op realized variance (als je intraday-data hebt)

Als je intraday-bars hebt — en in 24/7-cryptomarkten heb je die vrijwel altijd — kun je een veel minder ruizige volatiliteitsproxy construeren dan dagelijkse gekwadrateerde rendementen: realized variance, de som van de gekwadrateerde intraday-rendementen over de dag.

RVt=i=1Mrt,i2(e.g. M=288 five-minute bars)RV_t = \sum_{i=1}^{M} r_{t,i}^2 \qquad (\text{e.g. } M = 288 \text{ five-minute bars})

Het Heterogeneous Autoregressive-model van Corsi (2009) voorspelt de realized variance van morgen op basis van dagelijkse, wekelijkse en maandelijkse gemiddelden van eerdere RVRV — een grove maar opmerkelijk effectieve manier om langgeheugen-persistentie te vangen met drie regressoren:

RVt+1=β0+βdRVt+βwRVt(5)+βmRVt(22)+ut+1RV_{t+1} = \beta_0 + \beta_d\, RV_t + \beta_w\, \overline{RV}_t^{(5)} + \beta_m\, \overline{RV}_t^{(22)} + u_{t+1}

waarbij RVt(5)\overline{RV}_t^{(5)} en RVt(22)\overline{RV}_t^{(22)} de trailing 5-daagse en 22-daagse gemiddelden van dagelijkse RVRV zijn. Het is een gewone OLS-regressie, ze benut de hoogwaardigere intraday-proxy, en ze is vaak de beste dagelijkse-vol-voorspeller van de vier — die GARCH dikwijls verslaat juist omdat RVRV een schoner doel is dan r2r^2.

import numpy as np
import pandas as pd

def realized_variance(intraday_returns: pd.Series, day_index) -> pd.Series:
    """Daily realized variance = sum of squared intraday (log) returns per day.
    `intraday_returns` indexed by timestamp; `day_index` maps to calendar day.
    """
    return intraday_returns.pow(2).groupby(day_index).sum()

def har_features(rv: pd.Series) -> pd.DataFrame:
    """Build HAR regressors from a daily realized-variance series."""
    df = pd.DataFrame({"rv": rv})
    df["rv_d"] = df["rv"].shift(1)                          # yesterday
    df["rv_w"] = df["rv"].shift(1).rolling(5).mean()        # trailing week
    df["rv_m"] = df["rv"].shift(1).rolling(22).mean()       # trailing month
    df["target"] = df["rv"]                                 # predict today's RV
    return df.dropna()

def fit_har(rv: pd.Series):
    """Fit HAR-RV by OLS. Returns (coef_dict, predict_fn)."""
    df = har_features(rv)
    X = np.column_stack([np.ones(len(df)), df["rv_d"], df["rv_w"], df["rv_m"]])
    y = df["target"].values
    beta, *_ = np.linalg.lstsq(X, y, rcond=None)
    names = ["const", "rv_d", "rv_w", "rv_m"]

    def predict(rv_d, rv_w, rv_m):
        return float(beta @ np.array([1.0, rv_d, rv_w, rv_m]))

    return dict(zip(names, beta)), predict

Een noot over log-HAR: omdat RVRV rechtsscheef en strikt positief is, regresseren veel practitioners logRVt+1\log RV_{t+1} op log-HAR-features, wat de fit verbetert en positieve voorspellingen garandeert. Wanneer je terug exponentieert moet je een half-variantie Jensen-correctie toevoegen, RV^=exp(y^+12σ^u2)\widehat{RV} = \exp(\hat{y} + \tfrac{1}{2}\hat\sigma_u^2), anders zul je systematisch te laag voorspellen.

Met vier voorspellers in handen — RV, EWMA, GARCH/GJR-t, HAR — wordt de vraag: hoe beslissen we welke het best is, wanneer we het ding dat ze allemaal proberen te voorspellen nooit kunnen zien?

Een volatiliteitsvoorspelling eerlijk beoordelen

Dit is de kernles van het artikel, dus vertraag hier.

Je wilt voorspellingen σ^t2\hat\sigma_t^2 vergelijken met de ware conditionele variantie σt2\sigma_t^2. Maar σt2\sigma_t^2 is een latente grootheid — het is een parameter van het datagenererende proces, nooit direct waargenomen. Alles wat je krijgt is één gerealiseerd rendement per dag. Dus elke volatiliteitsevaluatie is eigenlijk een vergelijking van je voorspelling met een ruizige proxy voor de waarheid. De twee standaardproxy's:

  • Gekwadrateerde rendementen rt2r_t^2. Zuiver voor σt2\sigma_t^2 onder een model met nulgemiddelde (E[rt2Ft1]=σt2\mathbb{E}[r_t^2 \mid \mathcal{F}_{t-1}] = \sigma_t^2), maar extreem ruizig: een enkel dagelijks rendement is een schatting op basis van één observatie van een standaarddeviatie. De proxy rt2r_t^2 kan 0 zijn (een vlakke dag) zelfs wanneer de ware vol hoog is, of enorm op een gelukkige staart-dag.
  • Realized variance RVtRV_t uit intraday-data. Veel minder ruizig — de intraday-bemonstering middelt de idiosyncratische enkele-rendement-ruis uit — wat precies de reden is dat HAR-RV werkt en waarom je RVRV als je proxy zou moeten gebruiken als je überhaupt intraday-data hebt.

De subtiliteit die bijna iedereen betrapt: omdat de proxy ruizig is, is de keuze van verliesfunctie niet onschuldig. Rangschik twee voorspellingen met de verkeerde verliesfunctie en de ruizige proxy kan de rangorde omdraaien, waardoor het je vertelt dat de slechtere voorspelling beter is. Patton (2011) werkte precies uit welke verliesfuncties "robuust" zijn in de zin dat het rangschikken van voorspellingen op verwacht verlies op de ruizige proxy dezelfde rangorde geeft die je op de ware (niet-waarneembare) variantie zou krijgen. Slechts een specifieke familie kwalificeert. Twee leden doen er in de praktijk toe.

MSE versus QLIKE

De mean squared error op variantie:

LMSE(σ2,h)=(σ2h)2L_{\text{MSE}}(\sigma^2, h) = (\sigma^2 - h)^2

waarbij h=σ^2h = \hat\sigma^2 de voorspelling is en σ2\sigma^2 wordt geproxyd door r2r^2 of RVRV. MSE is robuust in Pattons zin (de rangorde is proxy-consistent), maar ze is symmetrisch en schaalafhankelijk: ze bestraft een over-voorspelling en een onder-voorspelling van dezelfde absolute grootte gelijk, en ze weegt fouten tijdens perioden met hoge volatiliteit enorm veel zwaarder dan fouten tijdens kalme perioden. Een model dat de kalme 95% van de dagen perfect raakt maar de variantievoorspellingen op de drie crisisdagen laat ontsporen, zal er onder MSE verschrikkelijk uitzien, zelfs als het gedrag tijdens de crisis precies is wat je eigenlijk wilt.

De QLIKE (quasi-likelihood) verliesfunctie is het werkpaard:

LQLIKE(σ2,h)=σ2hlogσ2h1L_{\text{QLIKE}}(\sigma^2, h) = \frac{\sigma^2}{h} - \log\frac{\sigma^2}{h} - 1

Het is de verliesfunctie die door een Gaussische likelihood op de variantie wordt geïmpliceerd, ze is ook robuust in Pattons zin, en ze heeft twee eigenschappen die haar de voorkeurskeuze voor volatiliteit maken. Ten eerste is ze asymmetrisch in de juiste richting: ze bestraft onder-voorspelling van de variantie meer dan over-voorspelling. Voor een risicomanager of een vol-targeter is dat de correcte asymmetrie — vol onder-voorspellen betekent dat je te veel positie hebt genomen precies voordat het ertoe deed, wat de dure fout is. Ten tweede is ze (ruwweg) schaalinvariant: omdat ze afhangt van de verhouding σ2/h\sigma^2/h, kost een voorspelfout van 10% ongeveer hetzelfde of ze nu op een kalme dag of een crisisdag optreedt, zodat de evaluatie niet wordt gekaapt door een handvol observaties met hoge variantie zoals dat bij MSE gebeurt. Die robuustheid tegen de heteroskedasticiteit van de proxy is precies wat je wilt wanneer het hele punt is dat de volatiliteit wild varieert.

Merk op dat LQLIKE0L_{\text{QLIKE}} \ge 0, met gelijkheid dan en slechts dan als h=σ2h = \sigma^2. Lager is beter, net als bij MSE.

import numpy as np

def qlike(proxy: np.ndarray, forecast_var: np.ndarray) -> np.ndarray:
    """Per-observation QLIKE loss. `proxy` is r^2 or RV (a variance proxy),
    `forecast_var` is h = sigma_hat^2. Both strictly positive, same units.
    """
    ratio = proxy / forecast_var
    return ratio - np.log(ratio) - 1.0

def mse_var(proxy: np.ndarray, forecast_var: np.ndarray) -> np.ndarray:
    """Per-observation MSE on the variance scale."""
    return np.square(proxy - forecast_var)

Twee operationele waarschuwingen. Houd proxy en voorspelling in identieke eenheden (beide dagelijkse varianties, of beide geannualiseerd) anders is de verhouding betekenisloos. En laat forecast_var nooit nul raken — knip het af op een kleine ondergrens, want log0\log 0 zal het hele gemiddelde vergiftigen.

Mincer-Zarnowitz-regressie

Een enkel verliesgetal vertelt je welke voorspelling beter is; het vertelt je niet hoe een voorspelling verkeerd is. De Mincer-Zarnowitz (1969) regressie doet dat wel. Regresseer de proxy op de voorspelling:

rt2  =  a+bσ^t2+ut.r_t^2 \;=\; a + b\,\hat\sigma_t^2 + u_t.

Onder een optimale, zuivere voorspelling geldt a=0a = 0 en b=1b = 1: gemiddeld is de gerealiseerde variantie gelijk aan de voorspelling. Afwijkingen diagnosticeren de pathologie:

  • b<1b < 1 met a>0a > 0: de klassieke signatuur van een voorspelling die te volatiel is — ze overreageert en voorspelt extremen die niet volledig materialiseren. Zeer gebruikelijk voor modellen die rechtstreeks door gekwadrateerde rendementen worden gedreven.
  • b>1b > 1: de voorspelling onderreageert en schaalt te weinig met de ware variantie.
  • Lage regressie-R2R^2: zelfs als a,ba,b er gemiddeld goed uitzien, volgt de voorspelling de variantie van dag tot dag slecht. Omdat de proxy zo ruizig is, wees niet gealarmeerd dat de MZ-R2R^2 tegen r2r^2 vaak slechts 0,05-0,20 is; tegen RVRV zal ze veel hoger zijn. De R2R^2 tegen r2r^2 is ver onder 1 begrensd hoe goed de voorspelling ook is, puur vanwege de proxy-ruis.

Een gezamenlijke FF-toets van H0:(a,b)=(0,1)H_0: (a,b) = (0,1) geeft een formele kalibratiecontrole. In de praktijk gebruik je MZ als diagnostiek om een voorspelling te begrijpen, en QLIKE om voorspellingen te rangschikken.

Diebold-Mariano: is het verschil echt?

Stel dat de gemiddelde QLIKE van GARCH 0,183 is en die van EWMA 0,191. GARCH "wint". Maar is 0,008 een echte voorsprong of steekproefruis? De Diebold-Mariano (1995) toets beantwoordt precies dit. Definieer het verliesdifferentieel per periode

dt=L(proxyt,htA)L(proxyt,htB)d_t = L(\text{proxy}_t, h^A_t) - L(\text{proxy}_t, h^B_t)

voor twee voorspellingen AA en BB (hier LL = QLIKE). De nulhypothese is gelijke voorspellende nauwkeurigheid, H0:E[dt]=0H_0: \mathbb{E}[d_t] = 0. De statistiek is het gemiddelde differentieel gestandaardiseerd door zijn langetermijn- (HAC) standaardfout, omdat dtd_t serieel gecorreleerd is:

DM=dˉLRV^(dt)/T  d  N(0,1)\text{DM} = \frac{\bar d}{\sqrt{\widehat{\mathrm{LRV}}(d_t)/T}} \;\xrightarrow{d}\; \mathcal{N}(0,1)

waarbij LRV^\widehat{\mathrm{LRV}} een langetermijnvariantieschatting van het Newey-West-type is. Een DM-statistiek voorbij ±1.96\pm 1.96 verwerpt gelijke nauwkeurigheid op 5%. Cruciaal is dat DM een toets over voorspellingen is, niet over geneste modellen, en dat ze de seriële afhankelijkheid in de verliesreeks afhandelt die een naïeve tt-toets op dtd_t zou negeren.

import numpy as np
from scipy import stats

def diebold_mariano(loss_a: np.ndarray, loss_b: np.ndarray, h: int = 1):
    """Diebold-Mariano test of equal predictive accuracy.
    loss_a, loss_b: per-period losses (e.g. QLIKE) for forecasts A and B.
    h: forecast horizon; Newey-West lag = h - 1 (>=0), plus a small-sample buffer.
    Returns (DM_stat, p_value). Negative DM => A has lower loss (A better).
    """
    d = np.asarray(loss_a) - np.asarray(loss_b)
    T = len(d)
    d_bar = d.mean()

    lag = max(h - 1, 0)
    gamma0 = np.mean((d - d_bar) ** 2)
    lrv = gamma0
    for k in range(1, lag + 1):
        w = 1.0 - k / (lag + 1)
        cov = np.mean((d[k:] - d_bar) * (d[:-k] - d_bar))
        lrv += 2.0 * w * cov

    dm = d_bar / np.sqrt(lrv / T)

    adj = np.sqrt((T + 1 - 2 * h + h * (h - 1) / T) / T)
    dm *= adj
    p = 2.0 * (1.0 - stats.t.cdf(abs(dm), df=T - 1))
    return float(dm), float(p)

Het illustratieve resultaat in dat commentaar is de eerlijke en gangbare uitkomst, en het is de hele reden dat deze sectie bestaat: GARCH boekt vaak een iets lager gemiddeld verlies dan EWMA, en net zo vaak slaagt die voorsprong er niet in de DM-significantielat te halen. Als je alleen ooit de gemiddelde QLIKE rapporteert, zul je jezelf overtuigen van voorsprongen die een DM-toets zou hebben afgekeurd. Rapporteer de DM-statistiek. Dit is dezelfde discipline die we toepassen op strategierendementen in eerlijke evaluatie zonder robuuste voorsprong — een puntschatting die een benchmark verslaat is geen voorsprong totdat je hebt uitgesloten dat het ruis is.

De backtest: een walk-forward vol-targeted strategie

Nu combineren we de twee helften — een voorspeller en een positiegrootteregel — tot een strategie en beoordelen we ze op de enige manier die iets betekent: walk-forward, out of sample, met kosten.

De strategie is bewust eenvoudig, want eenvoud is wat ons in staat stelt het resultaat toe te schrijven aan de vol-voorspelling in plaats van aan een slim signaal. Long-only, vol-targeted BTC. Elke dag voorspel je de volatiliteit van de volgende dag, zet je de positie op wt=min(σtarget/σ^t, wmax)w_t = \min(\sigma_{\text{target}}/\hat\sigma_t,\ w_{\max}), houd je overnight aan, en herhaal je. Een long/flat-variant zet de positie uit wanneer een trendfilter negatief is; een kleine-portefeuille-variant dimensioneert op de DCC-covariantiematrix uit Deel 3 in plaats van op de variantie van een enkel activum. We beschrijven het long-only-geval volledig en noteren de uitbreidingen.

Walk-forward-mechanica en het no-look-ahead-contract

De allerbelangrijkste eigenschap van deze backtest is dat elke grootheid die wordt gebruikt om de positie op dag t+1t+1 te dimensioneren berekenbaar is met uitsluitend data die beschikbaar is bij de close van dag tt. GARCH-parameters worden herschat op een rollend venster dat eindigt bij tt; de voorspelling is de σ^t+1\hat\sigma_{t+1} één stap vooruit uit die fit; de positie wordt uit die voorspelling gezet; en het verdiende rendement is wt+1rt+1w_{t+1} r_{t+1}, waarbij rt+1r_{t+1} het rendement van de volgende dag is, dat het model nooit zag. GARCH herfitten op de volledige steekproef en dan het verleden "voorspellen" is de meest voorkomende manier waarop mensen per ongeluk een geweldige backtest fabriceren. We behandelen deze look-ahead-val en de algemene methodologie in walk-forward optimalisatie.

import numpy as np
import pandas as pd
from arch import arch_model

def walk_forward_voltarget(
    returns: pd.Series,          # daily log returns in decimal (e.g. 0.021)
    proxy_var: pd.Series,        # variance proxy aligned to returns (RV or r^2)
    target_vol_annual: float = 0.20,
    window: int = 750,           # rolling estimation window (days)
    refit_every: int = 5,        # refit GARCH weekly, forecast daily (cost saver)
    w_max: float = 3.0,          # leverage cap
    cost_bps: float = 5.0,       # per-unit-turnover cost in basis points
    ann: int = 365,              # crypto trades 365 days/yr
):
    """Long-only vol-targeted BTC, GARCH(1,1)-t forecasts, strictly walk-forward.
    Returns a DataFrame with position, forecast vol, net returns, and turnover.
    """
    idx = returns.index
    target_daily = target_vol_annual / np.sqrt(ann)

    fcast_vol = pd.Series(index=idx, dtype=float, name="fcast_vol")
    last_res = None

    for i in range(window, len(returns) - 1):
        if (i - window) % refit_every == 0 or last_res is None:
            train = returns.iloc[i - window:i + 1] * 100.0   # scale for the optimizer
            am = arch_model(train, mean="Constant", vol="GARCH",
                            p=1, o=1, q=1, dist="t")           # GJR-t, Part 2
            last_res = am.fit(disp="off")

        f = last_res.forecast(horizon=1, reindex=False)
        var_next = f.variance.values[-1, 0] / (100.0 ** 2)    # unscale
        fcast_vol.iloc[i + 1] = np.sqrt(var_next)

    raw_w = (target_daily / fcast_vol).clip(upper=w_max)
    position = raw_w.shift(0)                                  # w_{i+1} known at close i
    gross_ret = position * returns                            # earned on day i+1

    turnover = position.diff().abs().fillna(0.0)
    cost = turnover * (cost_bps / 1e4)
    net_ret = (gross_ret - cost).dropna()

    out = pd.DataFrame({
        "position": position,
        "fcast_vol": fcast_vol,
        "gross_ret": gross_ret,
        "turnover": turnover,
        "net_ret": net_ret,
    }).dropna()
    return out

def performance_stats(net_ret: pd.Series, ann: int = 365) -> dict:
    """Sharpe, realized vol, max drawdown, CAGR, annual turnover."""
    mu = net_ret.mean() * ann
    sigma = net_ret.std() * np.sqrt(ann)
    sharpe = mu / sigma if sigma > 0 else np.nan
    equity = (1.0 + net_ret).cumprod()
    peak = equity.cummax()
    max_dd = (equity / peak - 1.0).min()
    cagr = equity.iloc[-1] ** (ann / len(net_ret)) - 1.0
    return {
        "sharpe": round(sharpe, 2),
        "realized_vol": round(sigma, 3),
        "max_drawdown": round(max_dd, 3),
        "cagr": round(cagr, 3),
    }

Een paar implementatienoten die er meer toe doen dan ze lijken:

  • Schaling voor de optimizer. arch-fits zijn numeriek gelukkiger wanneer rendementen in procenten zijn, vandaar de * 100 en de bijpassende / 100**2 bij het terugschalen van de variantie. Vergeet het terugschalen en je target-vol is 10.000x mis.
  • Herfit-cadans. GARCH-parameters elke afzonderlijke dag herschatten is duur en voegt bijna niets toe — de parameters zijn week op week stabiel. Wekelijks herfitten (refit_every=5) terwijl je dagelijks voorspelt (de recursie werkt σt2\sigma_t^2 bij vanuit nieuwe rendementen zelfs zonder herfitten) is het standaardcompromis. Dit weerspiegelt het caching-advies uit de copula-pijplijn in copula-modellen voor gezamenlijk risico.
  • De cap wmaxw_{\max} is niet cosmetisch. Wanneer de voorspelde vol instort in een doodstil regime, kan σtarget/σ^t\sigma_{\text{target}}/\hat\sigma_t exploderen naar 5x, 10x leverage. Ongecapte vol targeting zal je maar al te graag catastrofale leverage aanreiken precies voor een verandering van volatiliteitsregime — het exacte moment waarop de voorspelling op het punt staat het meest verkeerd te zijn. Cap het (hier 3x) en erken dat de cap precies zal binden in de kalmste, achteraf gezien gevaarlijkste perioden.
  • Kosten schalen met turnover, en vol targeting is een turnover-machine. Elke wiebeling in de voorspelling herdimensioneert de positie. Op een activum met lage vol en een schokkerige voorspelling kun je het boek dagelijks omwoelen. De cost_bps-term is geen afrondingsdetail; voor een vol-target met hoge turnover kan het een betekenisvolle fractie van de bruto Sharpe-verbetering opeten.

Hoe de output eruitziet (illustratief)

Dit draaien op dagelijkse BTC-data over een venster van meerdere jaren, met vergelijking van de vier voorspellers als de dimensioneringsnoemer, leidt doorgaans tot een tabel met de volgende vorm. De onderstaande getallen zijn illustratief — met de hand gekozen om het typische patroon te tonen, niet de output van een echte backtest — maar de ordening en magnitudes zijn representatief voor wat practitioners rapporteren.

Dimensioneringsvoorspelling Sharpe Realized vol Target-vol Max drawdown Jaarlijkse turnover
Vaste notional (geen targeting) 0.71 0.68 -0.78 0.1x
Rollende RV (60d) 0.94 0.24 0.20 -0.41 12x
EWMA (λ=0.94\lambda=0.94) 1.02 0.21 0.20 -0.35 19x
GARCH(1,1)-t 1.05 0.21 0.20 -0.33 22x
HAR-RV (intraday-proxy) 1.09 0.20 0.20 -0.31 20x

Twee varianten die het waard zijn te bouwen

Het long-only-geval isoleert de voorspelling, maar twee uitbreidingen zijn gangbaar genoeg om expliciet te tonen.

Long/flat met een trend-gate. Vol targeting dimensioneert de positie maar neemt geen richtingsvisie in — ze is altijd long. Een goedkope, eerlijke verbetering is om de positie uit te zetten (gate) wanneer een traag trendfilter negatief wordt, zodat je de vol-targeted long alleen in uptrends aanhoudt en anders vlak zit. Dit houdt de dimensioneringslogica identiek en legt er een grof regimefilter bovenop; het pretendeert niet entries te timen, alleen te vermijden dat je door duidelijke downtrends heen aanhoudt.

def apply_trend_gate(position: pd.Series, price: pd.Series, fast: int = 20, slow: int = 100):
    """Zero out the (already vol-targeted) position when the fast SMA is below
    the slow SMA. Both SMAs use only past prices, so no look-ahead is introduced.
    """
    fast_ma = price.rolling(fast).mean().shift(1)   # shift: known at prior close
    slow_ma = price.rolling(slow).mean().shift(1)
    gate = (fast_ma > slow_ma).astype(float)        # 1.0 in uptrend, 0.0 otherwise
    return position * gate.reindex(position.index).fillna(0.0)

De trend-gate snijdt de turnover aan de onderkant (je stopt met het omwoelen van een krimpende positie in een bearmarkt) maar voegt zijn eigen regimerisico toe — het whipsawt in choppy zijwaartse markten en loopt achter op keerpunten. Of het helpt is een empirische vraag die je moet beantwoorden met dezelfde walk-forward, DM-geteste striktheid als de dimensioneringsregel zelf; een trendfilter is precies het soort toevoeging dat er in-sample geweldig uitziet en out of sample verdampt.

Portefeuille-vol-targeting op de DCC-covariantie. Voor een boek van meerdere activa wordt de scalaire voorspelling σ^t\hat\sigma_t de portefeuillevolatiliteit wΣtw\sqrt{w^\top \Sigma_t w}, waarbij Σt\Sigma_t de tijdsvariërende covariantiematrix is uit de DCC-GARCH van Deel 3. Je kiest basisgewichten w0w_0 (gelijk gewicht, market cap, of een mean-variance-tilt), berekent de voorspelde vol van de portefeuille onder Σt\Sigma_t, en schaalt de hele gewichtsvector om de portefeuilletarget te raken.

def portfolio_voltarget_weights(base_w, cov_forecast, target_vol_daily, w_gross_max=3.0):
    """Scale a base weight vector so forecast portfolio vol hits the target.
    base_w:        (n,) base allocation (need not sum to 1).
    cov_forecast:  (n, n) one-step-ahead covariance from DCC-GARCH (daily units).
    """
    base_w = np.asarray(base_w, dtype=float)
    port_var = float(base_w @ cov_forecast @ base_w)
    port_vol = np.sqrt(max(port_var, 1e-12))
    scale = min(target_vol_daily / port_vol, w_gross_max / np.abs(base_w).sum())
    return scale * base_w

Dit is de natuurlijke brug naar de literatuur over portefeuilleconstructie: de basisgewichten w0w_0 kunnen komen uit Markowitz mean-variance of een risicogebaseerde methode zoals HRP/CVaR, en vol targeting zit er dan bovenop als een overlay die het geaggregeerde risico naar een constante schaalt. De DCC-matrix doet ertoe omdat correlaties pieken in crashes (Deel 3) — een portefeuille die er in kalme markten gediversifieerd uitziet kan een veel hogere voorspelde vol hebben dan een statische covariantie impliceert, precies wanneer het ertoe doet, en de overlay snijdt de bruto blootstelling in reactie daarop.

Diagnostiek die je altijd zou moeten plotten

Vertrouw nooit alleen op de samenvattingstabel. Plot voor elke vol-target drie dingen en bekijk ze met eigen ogen voordat je enig Sharpe-getal gelooft. Ten eerste, de gerealiseerde rollende vol van de strategie tegen de target-lijn — ze zou de target dicht moeten volgen; een systematische afwijking erboven betekent dat je voorspelling te laag gebiast is (de dure richting). Ten tweede, de positie/leverage-reeks — let op de cap die bindt en op leverage-pieken vlak voor drawdowns, de signatuur van een voorspelling die door een regimeverandering werd betrapt. Ten derde, de voorspelling-versus-proxy-scatter (het Mincer-Zarnowitz-beeld) — een wolk met een helling ver van 1 vertelt je dat de voorspelling mis-geschaald is op een manier die het QLIKE-gemiddelde kan verbergen. Deze drie plots vangen meer bugs en meer zelfbedrog dan welke afzonderlijke statistiek dan ook.

Lees deze tabel zoals je elke backtest-tabel zou moeten lezen: kijk naar wat robuust is en wat marginaal is. De robuuste feiten springen eruit. Elke vol-targeted variant verplettert vaste notional op Sharpe en, nog dramatischer, op drawdown en realized-vol-stabiliteit — de strategie met vaste notional draait op 68% geannualiseerde vol met een drawdown van 78%, wat simpelweg on-investeerbaar is. En elke targeting-methode levert een gerealiseerde vol dicht bij de 20%-target, wat de hele belofte is van het werkende mechanisme. De marginale feiten zijn de verschillen tussen de voorspellers: HAR gaat GARCH voorbij gaat EWMA voorbij gaat rollende RV voorbij, maar de gaten zijn klein — een tiende van een Sharpe-punt — en zouden, getest met Diebold-Mariano op de voorspellingen of een bootstrap op de rendementen, vaak niet de significantie halen. Dat kleine, fragiele, regime-afhankelijke gat tussen de geavanceerde en de naïeve voorspeller is de eerlijke kop van deze hele serie.

Eerlijk zijn over wat dit je oplevert

Deze blog heeft een hele collectie over backtesten zonder jezelf voor de gek te houden, dus laten we die op ons eigen resultaat toepassen in plaats van stilletjes te hopen dat jij dat niet doet.

Volatility targeting verbetert het risicogecorrigeerde rendement en drawdowns. Het fabriceert geen alpha uit het niets. Kijk nog eens naar de tabel. De Sharpe-verbetering door targeting is echt en de moeite waard — maar ontleed het en het meeste ervan komt van het niet aanhouden van een constante positie in regimes met hoge vol, wat mechanisch de ergste drawdowns vermijdt en het samenstellingspad stabiliseert. De strategie is nog steeds long BTC; ze heeft geen visie die de markt niet aanreikt. Als BTC een negatieve Sharpe heeft over je steekproef, zal vol targeting je een minder slechte negatieve Sharpe aanreiken, niet een positieve. Het hervormt de rendementsverdeling — dunnere staarten, stabielere vol, betere geometrische samenstelling — maar de rauwe richtingsvoorsprong is wat de onderliggende long ook is. Laat een prachtige equity-curve je niet doen geloven dat je alpha hebt gevonden wanneer je risicomanagement hebt gevonden. Moreira-Muir vonden echte alpha in aandelenfactoren door vol-management, maar dat resultaat gaat over de tijdsvariërende risico-rendementsafweging van de factor, en het draagt niet automatisch over naar een enkel crypto-activum over een andere steekproef.

De voorsprong in voorspelkwaliteit van GARCH boven EWMA is vaak klein en regime-afhankelijk. Dit is de ongemakkelijke opbrengst van Deel 1-3. Je bouwde steeds geavanceerdere modellen — leverage-termen, Student-t-staarten, dynamische correlaties — en de marginale bijdrage van elk aan een vol-targeting-P&L, boven een naïeve EWMA, valt vaak binnen de ruisband. Het voordeel van GARCH (mean reversion na schokken) verschijnt vooral in specifieke regimes: scherpe pieken die dan normaliseren. In slepende trends of persistente regimes met hoge vol verschilt het nauwelijks van EWMA. Dit maakt GARCH niet nutteloos — de mean-reversion-structuur, de interpreteerbare parameters, de mogelijkheid om paden vooruit te simuleren en opties tegen de voorspelling te prijzen hebben allemaal waarde die EWMA mist — maar als je enige gebruik dimensionering is, draai de DM-toets voordat je de complexiteitskost betaalt, en weet dat regimedetectie je hetzelfde vertelt vanuit een andere invalshoek: het winnende model hangt af van het regime.

Backtest-Sharpe is een bovengrens op live-Sharpe, en vol targeting verbreedt het gat. Omdat de strategie turnover-zwaar en leverage-geschaald is, is ze ongewoon gevoelig voor de wrijvingen die een naïeve backtest weglaat: je fills zijn slechter dan de close waarop je dimensioneerde, funding-kosten op leveraged perp-posities lopen continu op, en de leverage-cap interacteert met margin- en liquidatiemechanica die een simpele w * return negeert. Elk van deze maakt live slechter dan backtest. We behandelen dit gat systematisch in backtest-live-pariteit; voor vol targeting specifiek, budgetteer ervoor door conservatieve kosten, een realistische (lage) leverage-cap te gebruiken, en door uit te voeren op de open van de volgende bar in plaats van de close waarop je het signaal berekende.

Een uitweiding over de volatility risk premium. Alles hierboven voorspelt gerealiseerde volatiliteit. Er is een parallel, verhandelbaar object: implied volatiliteit, geprijsd in opties, die gemiddeld boven de daaropvolgende gerealiseerde vol ligt — de volatility risk premium, compensatie voor het dragen van het risico van een vol-piek. Dat gat is zelf een bron van rendement (variance verkopen oogst het, kopen hedget staartrisico), en het is een werkelijk ander spel dan vol targeting: het is een weddenschap op de prijs van volatiliteit in plaats van een gebruik van een voorspelling van volatiliteit. We streven het hier niet na, maar de machinerie begint met het prijsmodel in Black-Scholes optieprijzen, en een goede realized-vol-voorspelling (Deel 1-2) is precies de input die je nodig hebt om te beoordelen of de implied vol rijk of goedkoop is. Je GARCH-voorspelling vergelijken met de implied vol van de optiemarkt is een van de eerlijkere toepassingen van alles wat je in deze serie hebt gebouwd.

Praktische overwegingen

Een allegaartje van dingen die een werkende vol-target scheiden van een fragiele.

  • Schat op de juiste horizon. Als je een positie dimensioneert die één dag wordt aangehouden, voorspel dan één-dags-vol. Als je wekelijks herbalanceert, voorspel (en target) dan wekelijkse vol, of aggregeer de dagelijkse GARCH-voorspelling over de horizon — de multi-step GARCH-voorspelling keert terug richting σˉ2\bar\sigma^2, wat de naïeve "hdaily\sqrt{h}\cdot\text{daily}"-schaling negeert. Deel 1 behandelt multi-step GARCH-voorspelling.
  • Annualisering in 24/7-markten. Crypto handelt 365 dagen per jaar zonder weekenden of feestdagen, dus annualiseer dagelijkse vol met 365\sqrt{365}, niet de 252\sqrt{252} uit aandelen. Dit fout krijgen schaalt je target stilzwijgend met ~20% verkeerd.
  • De noemer kan een covariantiematrix zijn. Voor een boek met meerdere activa vervang je de scalaire σ^t\hat\sigma_t door portefeuillevol wΣtw\sqrt{w^\top \Sigma_t w} uit de DCC-GARCH van Deel 3, en schaal je de hele vector van gewichten om de portefeuilletarget-vol te raken. Dit verbindt vol targeting met mean-variance-dimensionering (Markowitz voor crypto) en risicogebaseerde allocatie (HRP- en CVaR-pijplijnen) — vol targeting is het speciale enkel-activum-geval van schalen naar een portefeuille-risicobudget.
  • Vol targeting is procyclisch op een subtiele manier. Wanneer iedereen dezelfde 1/σ1/\sigma-regel draait, dwingt een vol-piek gesynchroniseerde deleveraging af, wat de prijzen omlaag duwt, wat de gerealiseerde vol verhoogt, wat meer deleveraging afdwingt. Deze feedback (goed gedocumenteerd in de "volmageddon" van 2018 en diverse crypto-deleveraging-cascades) betekent dat de regel minder goed werkt precies wanneer veel spelers hem gebruiken. Het is geen reden om het op te geven, maar het is een reden om leverage te cappen en om niet aan te nemen dat je fills tijdens een vol-piek zullen lijken op fills in een kalme markt.
  • Floor en clip de voorspelling. Een nul- of bijna-nul-voorspelling van de vol produceert oneindige leverage. Floor σ^t\hat\sigma_t altijd op een verstandig minimum en cap de positie, en log hoe vaak elk bindt — als de cap het grootste deel van de tijd bindt, is je target te agressief voor het activum.

Samenvatting

  • Een volatiliteitsvoorspelling heeft geen waarde totdat ze een beslissing verandert. Volatility targeting — blootstelling dimensioneren als wt=σtarget/σ^tw_t = \sigma_{\text{target}}/\hat\sigma_t (gecapt) — is de schoonste test van de waarde van een voorspelling, omdat voorspelkwaliteit rechtstreeks in kaart wordt gebracht op een vlakker gerealiseerd-vol-profiel en een hogere Sharpe.
  • Vol targeting verhoogt het risicogecorrigeerde rendement en beheerst vooral drawdowns, omdat volatiliteit voorspelbaar is (ze clustert) terwijl richting dat niet is, en omdat Sharpe-ratio's dalen in regimes met hoge vol die de regel automatisch onderweegt. Het is Kelly-dimensionering onder de aanname dat het verwachte rendement met de volatiliteit meeschaalt.
  • Benchmark GARCH eerlijk tegen sterke basislijnen: rollende realized vol, EWMA/RiskMetrics (λ=0.94\lambda=0.94, een IGARCH met nul vrije parameters), en HAR-RV op een intraday realized-variance-proxy. EWMA en HAR zijn moeilijk te verslaan.
  • Je kunt ware volatiliteit niet waarnemen, dus beoordeel tegen een proxy (r2r^2 of, veel beter, RVRV) met verliesfuncties die robuust zijn tegen proxy-ruis. Verkies QLIKE boven MSE: het bestraft onder-voorspellen meer (de dure fout) en is schaalinvariant, zodat het niet wordt gekaapt door een paar dagen met hoge vol. Gebruik Mincer-Zarnowitz om bias te diagnosticeren en de Diebold-Mariano-toets om te beslissen of de voorsprong van een voorspelling echt of ruis is.
  • In een walk-forward, kostenbewuste backtest verslaat vol targeting betrouwbaar vaste notional op Sharpe en drawdown, en de vier voorspellers clusteren dicht bij elkaar — de voorsprong van GARCH boven EWMA is klein, regime-afhankelijk en vaak niet statistisch significant. Rapporteer de DM-toets, niet alleen het gemiddelde verlies.
  • Wees eerlijk: vol targeting is risicomanagement, geen alpha. Het hervormt de rendementsverdeling van welke richtingsweddenschap je ook al had; het creëert geen voorsprong uit het niets. En het is turnover-zwaar en leverage-geschaald, dus live-resultaten lopen meer achter op de backtest dan gewoonlijk.

Referenties:

  • Patton, A. J. (2011). Volatility forecast comparison using imperfect volatility proxies. Journal of Econometrics, 160(1), 246-256. DOI
  • Corsi, F. (2009). A simple approximate long-memory model of realized volatility. Journal of Financial Econometrics, 7(2), 174-196. DOI
  • Diebold, F. X., & Mariano, R. S. (1995). Comparing predictive accuracy. Journal of Business & Economic Statistics, 13(3), 253-263. DOI
  • Mincer, J., & Zarnowitz, V. (1969). The evaluation of economic forecasts. In Economic Forecasts and Expectations, NBER.
  • Moreira, A., & Muir, T. (2017). Volatility-managed portfolios. Journal of Finance, 72(4), 1611-1644. DOI
  • J.P. Morgan / Reuters (1996). RiskMetrics Technical Document, 4th ed.
  • Bollerslev, T. (1986). Generalized autoregressive conditional heteroskedasticity. Journal of Econometrics, 31(3), 307-327. DOI
  • Glosten, L. R., Jagannathan, R., & Runkle, D. E. (1993). On the relation between the expected value and the volatility of the nominal excess return on stocks. Journal of Finance, 48(5), 1779-1801. DOI
  • Harvey, D., Leybourne, S., & Newbold, P. (1997). Testing the equality of prediction mean squared errors. International Journal of Forecasting, 13(2), 281-291. DOI
Disclaimer: De informatie in dit artikel is uitsluitend bedoeld voor educatieve en informatieve doeleinden en vormt geen financieel, beleggings- of handelsadvies. Het handelen in cryptovaluta brengt een aanzienlijk risico op verlies met zich mee.

MarketMaker.cc Team

Kwantitatief onderzoek en strategie

Bespreek op Telegram
Newsletter

Blijf de markt voor

Abonneer je op onze nieuwsbrief voor exclusieve AI-handelsinzichten, marktanalyses en platformupdates.

We respecteren je privacy. Je kunt je op elk moment afmelden.