← Zurück zu den Artikeln
March 30, 2026
5 min read

Copula-Modelle für die gemeinsame Risikomodellierung in Krypto-Portfolios

Copula-Modelle für die gemeinsame Risikomodellierung in Krypto-Portfolios
#risk
#copula
#portfolio
#tail-dependence
#VaR
📊
Part 4 of 5 · Collection
Portfolio Construction & Risk

Copula-Modelle — gemeinsame Risikoverteilung

Korrelation ist das erste Werkzeug, zu dem die meisten Portfoliomanager greifen, wenn sie die Diversifikation beurteilen. Doch in Kryptomärkten ist Korrelation gefährlich irreführend. Zwei Token können in ruhigen Marktphasen eine Pearson-Korrelation von 0,3 aufweisen und während eines Crashs auf 0,95 springen. Die lineare Korrelation setzt elliptische Verteilungen voraus — eine Annahme, die unter den fetten Tails und asymmetrischen Abhängigkeitsstrukturen zusammenbricht, die für Kryptowährungsrenditen typisch sind.

Copula-Modelle lösen dieses Problem, indem sie das Randverhalten (wie sich jedes Asset einzeln verhält) von der Abhängigkeitsstruktur (wie sich Assets gemeinsam bewegen) trennen. Diese Trennung, die auf dem Satz von Sklar beruht, liefert uns ein flexibles Framework zur Modellierung der vollständigen gemeinsamen Verteilung von Portfoliorenditen — einschließlich der Tails, wo das eigentliche Risiko liegt.

Warum lineare Korrelation bei Krypto versagt

Betrachten wir ein Portfolio aus BTC, ETH, SOL und AVAX. Während des Zusammenbruchs von Terra/Luna im Mai 2022 konvergierten die Korrelationen zwischen diesen Assets gegen 1,0 — genau dann, als Diversifikation am dringendsten gebraucht wurde. Ein Mean-Variance-Optimierer, der von stabilen Korrelationen ausging, hätte das Portfoliorisiko drastisch unterschätzt.

Die zentralen Probleme der Pearson-Korrelation für Krypto:

  1. Nicht-elliptische Verteilungen. Krypto-Renditen weisen deutliche Schiefe und Kurtosis auf. Die täglichen BTC-Renditen zeigen regelmäßig Kurtosis-Werte über 10 (Normalverteilung: 3).
  2. Asymmetrische Abhängigkeit. Assets neigen dazu, in Abschwungphasen stärker korreliert zu sein als in Rallyphasen. Dieses Phänomen des "Korrelationszusammenbruchs" ist an Aktienmärkten gut dokumentiert und bei Krypto noch ausgeprägter.
  3. Tail-Abhängigkeit. Die Wahrscheinlichkeit, dass zwei Assets gleichzeitig extreme Verluste erleiden, wird von der linearen Korrelation nicht erfasst. Zwei Assets können identische Korrelation, aber völlig unterschiedliche Tail-Abhängigkeit aufweisen.

Der Satz von Sklar: die Grundlage

Der Satz von Sklar (1959) besagt, dass jede multivariate gemeinsame Verteilung F(x1,x2,,xd)F(x_1, x_2, \ldots, x_d) zerlegt werden kann in:

F(x1,x2,,xd)=C(F1(x1),F2(x2),,Fd(xd))F(x_1, x_2, \ldots, x_d) = C\bigl(F_1(x_1), F_2(x_2), \ldots, F_d(x_d)\bigr)

wobei FiF_i die Randverteilungsfunktionen sind und C:[0,1]d[0,1]C: [0,1]^d \to [0,1] die Copula ist — eine Funktion, die die gesamte Abhängigkeitsstruktur zwischen den Variablen kodiert.

Umgekehrt gilt: Sind die Randverteilungen stetig, ist die Copula CC eindeutig.

Diese Zerlegung ist mächtig, weil sie es uns erlaubt:

  • Die Randverteilung jedes Assets separat zu modellieren (mit GARCH, EVT oder jeder passenden Verteilung)
  • Die Abhängigkeitsstruktur unabhängig über die Copula zu modellieren
  • Beides zur vollständigen gemeinsamen Verteilung zu kombinieren

Die Dichte der gemeinsamen Verteilung faktorisiert als:

f(x1,,xd)=c(F1(x1),,Fd(xd))i=1dfi(xi)f(x_1, \ldots, x_d) = c\bigl(F_1(x_1), \ldots, F_d(x_d)\bigr) \cdot \prod_{i=1}^{d} f_i(x_i)

wobei cc die Copula-Dichte und fif_i die Randdichten sind.

Copula-Familien und ihre Eigenschaften

Vergleich der Tail-Abhängigkeit zwischen Copula-Familien

Gaußsche Copula

Die Gaußsche Copula wird durch eine Korrelationsmatrix Σ\Sigma parametrisiert:

CΣGauss(u1,,ud)=ΦΣ(Φ1(u1),,Φ1(ud))C_{\Sigma}^{\text{Gauss}}(u_1, \ldots, u_d) = \Phi_{\Sigma}\bigl(\Phi^{-1}(u_1), \ldots, \Phi^{-1}(u_d)\bigr)

wobei ΦΣ\Phi_{\Sigma} die multivariate Normal-CDF und Φ1\Phi^{-1} die univariate Normal-Quantilfunktion ist.

Tail-Abhängigkeit: λL=λU=0\lambda_L = \lambda_U = 0 (für ρ<1\rho < 1).

Die Gaußsche Copula weist keine Tail-Abhängigkeit auf — sie unterschätzt systematisch die Wahrscheinlichkeit gemeinsamer Extremereignisse. Dies war ein Schlüsselfaktor bei der Fehlbewertung von CDOs vor 2008 und ist ebenso gefährlich für die Krypto-Risikomodellierung.

Student-t-Copula

Die t-Copula führt symmetrische Tail-Abhängigkeit über einen Freiheitsgrade-Parameter ν\nu ein:

Cν,Σt(u1,,ud)=tν,Σ(tν1(u1),,tν1(ud))C_{\nu, \Sigma}^{t}(u_1, \ldots, u_d) = t_{\nu, \Sigma}\bigl(t_{\nu}^{-1}(u_1), \ldots, t_{\nu}^{-1}(u_d)\bigr)

Tail-Abhängigkeit:

λL=λU=2tν+1((ν+1)(1ρ)1+ρ)\lambda_L = \lambda_U = 2 \cdot t_{\nu+1}\left(-\sqrt{\frac{(\nu+1)(1-\rho)}{1+\rho}}\right)

Für ν=4\nu = 4 und ρ=0,5\rho = 0,5 ergibt sich λ0,18\lambda \approx 0,18 — eine 18-prozentige Wahrscheinlichkeit, dass sich beide Assets gleichzeitig in ihrem schlechtesten Quantil befinden. Ein niedrigeres ν\nu (schwerere Tails) erhöht diese Wahrscheinlichkeit. Kryptomärkte mit ihren fat-tailed Renditen erfordern typischerweise ein ν\nu im Bereich von 3-8.

Die t-Copula stellt eine deutliche Verbesserung gegenüber der Gaußschen dar, erzwingt aber symmetrische Tail-Abhängigkeit (λL=λU\lambda_L = \lambda_U). In der Praxis zeigen Krypto-Assets oft eine stärkere untere Tail-Abhängigkeit (gemeinsames Abstürzen) als obere Tail-Abhängigkeit (gemeinsames Rallyen).

Clayton-Copula

Die Clayton-Copula erfasst untere Tail-Abhängigkeit — genau die Art asymmetrischen Crash-Clustering-Verhaltens, das wir bei Krypto sehen:

CθClayton(u1,u2)=(u1θ+u2θ1)1/θ,θ>0C_{\theta}^{\text{Clayton}}(u_1, u_2) = \left(u_1^{-\theta} + u_2^{-\theta} - 1\right)^{-1/\theta}, \quad \theta > 0

Tail-Abhängigkeit: λL=21/θ\lambda_L = 2^{-1/\theta}, λU=0\lambda_U = 0.

Mit steigendem θ\theta verstärkt sich die untere Tail-Abhängigkeit. Für θ=2\theta = 2 ergibt sich λL0,71\lambda_L \approx 0,71 — eine sehr hohe Wahrscheinlichkeit gemeinsamer extremer Verluste.

Gumbel-Copula

Die Gumbel-Copula ist das Spiegelbild — sie erfasst obere Tail-Abhängigkeit:

CθGumbel(u1,u2)=exp([(lnu1)θ+(lnu2)θ]1/θ),θ1C_{\theta}^{\text{Gumbel}}(u_1, u_2) = \exp\left(-\left[(-\ln u_1)^{\theta} + (-\ln u_2)^{\theta}\right]^{1/\theta}\right), \quad \theta \geq 1

Tail-Abhängigkeit: λL=0\lambda_L = 0, λU=221/θ\lambda_U = 2 - 2^{1/\theta}.

Frank-Copula

Die Frank-Copula hat keine Tail-Abhängigkeit in beiden Tails (λL=λU=0\lambda_L = \lambda_U = 0), was sie geeignet macht, um Abhängigkeit im zentralen Bereich der Verteilung ohne Tail-Effekte zu modellieren:

CθFrank(u1,u2)=1θln(1+(eθu11)(eθu21)eθ1)C_{\theta}^{\text{Frank}}(u_1, u_2) = -\frac{1}{\theta}\ln\left(1 + \frac{(e^{-\theta u_1}-1)(e^{-\theta u_2}-1)}{e^{-\theta}-1}\right)

Die richtige Copula für Krypto wählen

Für Krypto-Portfolios deuten die empirischen Belege auf:

  • Clayton oder rotierte Gumbel (survival Gumbel) für untere Tail-Abhängigkeit — zur Erfassung der Crash-Ansteckung
  • t-Copula als robuste Allzweck-Wahl, wenn symmetrische Tail-Abhängigkeit akzeptabel ist
  • Joe-Copula zur Erfassung starker oberer Tail-Abhängigkeit in Rally-Phasen

Eine Studie von Bruhn und Jeleskovic (2024) fand heraus, dass GARCH-Copula-Modelle — insbesondere solche mit Student-t-Randverteilungen kombiniert mit t-Copulas — Mean-Variance- und historische CVaR-Ansätze über Abschwung- (2022), Erholungs- (2023) und Stabilitätsphasen (2024) hinweg konsistent übertrafen.

Der Fluch der Dimensionalität: Vine-Copulas kommen ins Spiel

Vine-Copula-Baumstruktur für ein Krypto-Portfolio

Standard-multivariate Copulas (Gauß, t-Copula) skalieren zwar auf hohe Dimensionen, erzwingen jedoch restriktive Annahmen. Archimedische Copulas (Clayton, Gumbel, Frank) sind naturgemäß bivariat — ihre Erweiterung auf d>2d > 2 Dimensionen erfordert, dass alle Paare denselben Abhängigkeitsparameter teilen, was unrealistisch ist.

Vine-Copulas lösen dieses Problem, indem sie eine dd-dimensionale Copula in eine Kaskade bivariater Copulas zerlegen, die in einer Baumstruktur angeordnet sind. Jedes Variablenpaar (bedingt auf andere) erhält seine eigene bivariate Copula-Familie und seinen eigenen Parameter.

Die Pair-Copula-Konstruktion

Für eine dd-dimensionale Dichte lautet die Vine-Copula-Faktorisierung:

f(x1,,xd)=i=1dfi(xi)j=1d1i=1djci,i+ji+1,,i+j1f(x_1, \ldots, x_d) = \prod_{i=1}^{d} f_i(x_i) \cdot \prod_{j=1}^{d-1}\prod_{i=1}^{d-j} c_{i,i+j|i+1,\ldots,i+j-1}

wobei ci,jSc_{i,j|S} eine bivariate Copula-Dichte für die Variablen ii und jj bedingt auf die Menge SS ist.

Eine dd-dimensionale Vine-Copula benötigt (d2)=d(d1)/2\binom{d}{2} = d(d-1)/2 bivariate Copulas. Für ein 10-Asset-Krypto-Portfolio sind das 45 Paar-Copulas — jede möglicherweise aus einer anderen Familie.

Vine-Strukturen: C-Vine, D-Vine, R-Vine

C-Vine (Canonical Vine): Jeder Baum hat einen einzigen Wurzelknoten, der mit allen anderen Knoten verbunden ist. Am besten geeignet, wenn eine Variable dominiert — z. B. BTC als Marktantrieb.

Tree 1:    BTC --- ETH
           BTC --- SOL
           BTC --- AVAX
           BTC --- DOT

Tree 2:    ETH|BTC --- SOL|BTC
           ETH|BTC --- AVAX|BTC
           ETH|BTC --- DOT|BTC

D-Vine (Drawable Vine): Eine sequenzielle Pfadstruktur. Am besten geeignet, wenn Variablen eine natürliche Reihenfolge haben (z. B. nach Marktkapitalisierung oder Sektor).

R-Vine (Regular Vine): Die allgemeinste Struktur — jede gültige Baumfolge. R-Vines subsumieren sowohl C-Vines als auch D-Vines.

Untersuchungen zu Kryptowährungsportfolios legen nahe, dass D-Vine-Strukturen bei Krypto-Assets oft bessere VaR-Prognosen liefern als C-Vine und R-Vine, wobei dies von der konkreten Portfoliozusammensetzung abhängt.

Warum Vine-Copulas bei Krypto wichtig sind

Ein Portfolio aus 8 Krypto-Assets, das mit einer einzigen Clayton-Copula modelliert wird, zwingt alle 28 Paare, denselben θ\theta-Wert zu teilen. Doch BTC-ETH könnte θClayton=3,5\theta_{\text{Clayton}} = 3,5 haben (starke Crash-Abhängigkeit), während SOL-AVAX θ=1,2\theta = 1,2 haben könnte (moderat). Vine-Copulas erlauben es jedem Paar, seine eigene Abhängigkeitsstruktur auszudrücken:

  • BTC-ETH: t-Copula (ν=4\nu=4, ρ=0,72\rho=0,72)
  • BTC-SOL: Clayton (θ=2,1\theta=2,1)
  • ETH-AVAX: Frank (θ=5,3\theta=5,3)
  • SOL-DOT | BTC: Gumbel (θ=1,8\theta=1,8)

Diese Flexibilität ist entscheidend für eine präzise Portfoliorisikoschätzung.

Modellierung der Randverteilungen: GARCH-EVT

Bevor wir die Copula anpassen, müssen wir die Renditereihe jedes Assets in gleichverteilte [0,1][0,1]-Variablen transformieren (die "Probability Integral Transform"). Die Standard-Pipeline:

  1. Ein GARCH-Modell an die Renditereihe jedes Assets anpassen, um zeitvariable Volatilität zu erfassen
  2. Standardisierte Residuen extrahieren zt=(rtμt)/σtz_t = (r_t - \mu_t) / \sigma_t
  3. Die Tails anpassen mit Extremwerttheorie (EVT) — konkret der Generalized Pareto Distribution (GPD) für den oberen und unteren Tail jenseits eines Schwellenwerts (typischerweise das 5. und 95. Perzentil)
  4. Die empirische CDF verwenden für den zentralen Bereich der Verteilung
  5. Die Probability Integral Transform anwenden, um pseudo-gleichverteilte Beobachtungen ui,t=F^i(zi,t)u_{i,t} = \hat{F}_i(z_{i,t}) zu erhalten

Dieser GARCH-EVT-Ansatz wird oft als "semiparametrische" Methode bezeichnet. Er erfasst korrekt:

  • Volatilitätsclustering (GARCH)
  • Fette Tails (GPD aus EVT)
  • Die Gesamtform der Verteilung (empirische CDF für den zentralen Bereich)

Für Krypto-Assets funktioniert ein EGARCH(1,1)- oder GJR-GARCH(1,1)-Modell mit Student-t-Innovationen üblicherweise gut, da es die asymmetrische Volatilitätsreaktion erfasst (schlechte Nachrichten erhöhen die Volatilität stärker als gute).

Portfolio-VaR und -CVaR mit Copulas

Value-at-Risk (VaR)

Der Portfolio-VaR auf dem Konfidenzniveau α\alpha ist:

VaRα=inf{l:P(Ll)α}\text{VaR}_\alpha = \inf\{ l : P(L \leq l) \geq \alpha \}

wobei LL der Portfolioverlust ist. Mit Copulas schätzen wir VaR per Monte-Carlo:

  1. NN Stichproben aus der angepassten Vine-Copula simulieren (im gleichverteilten Raum)
  2. Zurück in den Renditeraum transformieren mit den inversen Rand-CDFs
  3. Portfoliorenditen berechnen: rp=iwirir_p = \sum_i w_i \cdot r_i
  4. VaR ist das α\alpha-Quantil der simulierten Portfolioverlustverteilung

Conditional Value-at-Risk (CVaR / Expected Shortfall)

CVaR ist der erwartete Verlust unter der Bedingung, dass der Verlust den VaR übersteigt:

CVaRα=E[LLVaRα]=11αα1VaRudu\text{CVaR}_\alpha = E[L \mid L \geq \text{VaR}_\alpha] = \frac{1}{1-\alpha}\int_{\alpha}^{1}\text{VaR}_u\, du

CVaR ist kohärent (erfüllt Subadditivität), was es dem VaR für die Portfoliooptimierung überlegen macht. Die Monte-Carlo-Schätzung ist unkompliziert — man mittelt die Verluste, die den VaR übersteigen.

Warum Copula-basiertes Risiko besser ist als korrelationsbasiertes Risiko

Betrachten wir zwei Portfolios mit identischen paarweisen Korrelationen von 0,5:

  • Portfolio A: Gaußsche Copula-Abhängigkeit (keine Tail-Abhängigkeit)
  • Portfolio B: Clayton-Copula-Abhängigkeit (θ=2\theta = 2, λL=0,71\lambda_L = 0,71)

Auf dem 99%-Konfidenzniveau wird Portfolio B einen deutlich höheren VaR und CVaR aufweisen, weil die Clayton-Copula die Tendenz der Assets, gemeinsam abzustürzen, korrekt modelliert. Die Gaußsche Copula unterschätzt dieses Risiko, indem sie davon ausgeht, dass extreme Ko-Bewegungen verschwindend selten sind.

In empirischen Studien zu Krypto-Portfolios kann der Unterschied im 99%-CVaR zwischen Gauß- und Vine-Copula-Modellen 30-40% übersteigen, was bedeutet, dass korrelationsbasierte Modelle das Tail-Risiko um ein Drittel oder mehr unterschätzen können.

Implementierung: Python mit pyvinecopulib

Hier ist eine vollständige Pipeline zum Anpassen einer Vine-Copula an Krypto-Renditen und zur Schätzung von Portfolio-VaR/CVaR.

Schritt 1: Datenvorbereitung und Anpassung der Randverteilungen

import numpy as np
import pandas as pd
from arch import arch_model
from scipy import stats
import pyvinecopulib as pv

def fetch_crypto_returns(symbols, start="2023-01-01", end="2025-12-31"):
    """
    Fetch daily returns for a list of crypto symbols.
    Replace with your data source (ccxt, yfinance, etc.)
    """
    import yfinance as yf
    prices = yf.download(
        [f"{s}-USD" for s in symbols],
        start=start, end=end
    )["Close"]
    prices.columns = symbols
    returns = np.log(prices / prices.shift(1)).dropna()
    return returns

symbols = ["BTC", "ETH", "SOL", "AVAX", "DOT", "LINK", "MATIC", "ATOM"]
returns = fetch_crypto_returns(symbols)

def fit_garch_marginal(series, dist="t"):
    """
    Fit GJR-GARCH(1,1) with Student-t innovations.
    Returns standardized residuals and the fitted model.
    """
    model = arch_model(
        series * 100,  # scale for numerical stability
        vol="GARCH",
        p=1, o=1, q=1,  # GJR-GARCH
        dist=dist,
        mean="AR",
        lags=1
    )
    result = model.fit(disp="off")
    std_resid = result.std_resid.dropna()
    return std_resid, result

residuals = {}
garch_models = {}
for sym in symbols:
    std_resid, model = fit_garch_marginal(returns[sym])
    residuals[sym] = std_resid
    garch_models[sym] = model

residuals_df = pd.DataFrame(residuals).dropna()

Schritt 2: Probability Integral Transform

def semi_parametric_pit(residuals, tail_threshold=0.05):
    """
    Semi-parametric probability integral transform:
    - GPD for tails beyond threshold
    - Empirical CDF for the body
    Returns pseudo-uniform observations in [0, 1].
    """
    n = len(residuals)
    u = np.zeros(n)
    sorted_resid = np.sort(residuals)

    lower_thresh = np.quantile(residuals, tail_threshold)
    upper_thresh = np.quantile(residuals, 1 - tail_threshold)

    for i, x in enumerate(residuals):
        if x <= lower_thresh:
            lower_exceedances = -(residuals[residuals <= lower_thresh] - lower_thresh)
            shape, _, scale = stats.genpareto.fit(lower_exceedances, floc=0)
            u[i] = tail_threshold * (
                1 - stats.genpareto.cdf(-(x - lower_thresh), shape, scale=scale)
            )
        elif x >= upper_thresh:
            upper_exceedances = residuals[residuals >= upper_thresh] - upper_thresh
            shape, _, scale = stats.genpareto.fit(upper_exceedances, floc=0)
            u[i] = 1 - tail_threshold * (
                1 - stats.genpareto.cdf(x - upper_thresh, shape, scale=scale)
            )
        else:
            u[i] = np.mean(residuals <= x)

    u = np.clip(u, 1e-6, 1 - 1e-6)
    return u

U = np.column_stack([
    semi_parametric_pit(residuals_df[sym].values)
    for sym in symbols
])

Schritt 3: Anpassung der Vine-Copula

controls = pv.FitControlsVinecop(
    family_set=[
        pv.BicopFamily.student,
        pv.BicopFamily.clayton,
        pv.BicopFamily.gumbel,
        pv.BicopFamily.frank,
        pv.BicopFamily.joe,
        pv.BicopFamily.bb1,       # Clayton-Gumbel mixture
        pv.BicopFamily.bb7,       # Joe-Clayton mixture
        pv.BicopFamily.gaussian,
    ],
    selection_criterion="bic",    # BIC for model selection
    tree_criterion="tau",          # Kendall's tau for tree structure
    nonparametric_method="constant",
    trunc_lvl=5,                   # Truncate after 5 trees
)

vine = pv.Vinecop(U, controls=controls)

print(f"Log-likelihood: {vine.loglik(U):.2f}")
print(f"AIC: {vine.aic(U):.2f}")
print(f"BIC: {vine.bic(U):.2f}")

for i in range(vine.order.shape[0] - 1):
    pair = vine.get_pair_copula(0, i)
    print(f"Tree 1, Edge {i}: {pair.family} "
          f"(params: {pair.parameters})")

Schritt 4: Monte-Carlo-VaR und -CVaR

def estimate_var_cvar(vine, garch_models, symbols, weights,
                       n_sim=50_000, alpha=0.99, seed=42):
    """
    Estimate portfolio VaR and CVaR using Monte Carlo simulation
    from the fitted vine copula.
    """
    U_sim = vine.simulate(n=n_sim, seeds=[seed])

    returns_sim = np.zeros((n_sim, len(symbols)))
    for j, sym in enumerate(symbols):
        model = garch_models[sym]
        forecasts = model.forecast(horizon=1)
        mu = forecasts.mean.iloc[-1, 0] / 100  # unscale
        sigma = np.sqrt(forecasts.variance.iloc[-1, 0]) / 100

        nu = model.params.get("nu", 5)
        z_sim = stats.t.ppf(U_sim[:, j], df=nu)
        returns_sim[:, j] = mu + sigma * z_sim

    weights = np.array(weights)
    portfolio_returns = returns_sim @ weights

    losses = -portfolio_returns

    var = np.quantile(losses, alpha)

    cvar = np.mean(losses[losses >= var])

    return var, cvar, portfolio_returns

weights = [1.0 / len(symbols)] * len(symbols)

var_99, cvar_99, sim_returns = estimate_var_cvar(
    vine, garch_models, symbols, weights,
    n_sim=100_000, alpha=0.99
)

print(f"1-day 99% VaR:  {var_99*100:.2f}%")
print(f"1-day 99% CVaR: {cvar_99*100:.2f}%")

from scipy.stats import norm
mu_p = sim_returns.mean()
sigma_p = sim_returns.std()
var_gauss = -(mu_p + sigma_p * norm.ppf(0.01))
print(f"\nGaussian VaR:   {var_gauss*100:.2f}%")
print(f"Copula/Gaussian ratio: {var_99/var_gauss:.2f}x")

Schritt 5: Tail-Abhängigkeitsanalyse

def compute_tail_dependence(vine, symbols):
    """
    Extract lower and upper tail dependence coefficients
    from the first tree of the vine copula.
    """
    results = []
    order = vine.order
    n_edges = order.shape[0] - 1

    for i in range(n_edges):
        pair = vine.get_pair_copula(0, i)
        u_pair = pair.simulate(n=100_000, seeds=[42])
        q = 0.01  # 1st percentile

        mask_lower = (u_pair[:, 0] <= q)
        lambda_L = np.mean(u_pair[mask_lower, 1] <= q) if mask_lower.sum() > 0 else 0

        mask_upper = (u_pair[:, 0] >= 1 - q)
        lambda_U = np.mean(u_pair[mask_upper, 1] >= 1 - q) if mask_upper.sum() > 0 else 0

        i_idx = order[0]
        j_idx = order[i + 1]
        results.append({
            "pair": f"{symbols[i_idx]}-{symbols[j_idx]}",
            "family": str(pair.family),
            "lambda_L": round(lambda_L, 4),
            "lambda_U": round(lambda_U, 4),
        })

    return pd.DataFrame(results)

tail_dep = compute_tail_dependence(vine, symbols)
print(tail_dep.to_string(index=False))

Ein typisches Ergebnis für ein Krypto-Portfolio könnte so aussehen:

Pair Family λL\lambda_L λU\lambda_U
BTC-ETH student 0.22 0.22
BTC-SOL clayton 0.35 0.00
BTC-AVAX bb7 0.28 0.12
BTC-DOT student 0.18 0.18
BTC-LINK clayton 0.31 0.00
BTC-MATIC frank 0.00 0.00
BTC-ATOM gumbel 0.00 0.15

Man beachte, wie jedes Paar eine völlig unterschiedliche Abhängigkeitsstruktur aufweisen kann. BTC-SOL zeigt starke untere Tail-Abhängigkeit (Clayton) bei null oberer Tail-Abhängigkeit — sie stürzen gemeinsam ab, rallyen aber nicht notwendigerweise gemeinsam. BTC-MATIC zeigt überhaupt keine Tail-Abhängigkeit (Frank), was auf einen gewissen Diversifikationsvorteil selbst in Extremsituationen hindeutet.

Backtesting des Copula-VaR-Modells

Ein VaR-Modell ist nur nützlich, wenn es gut kalibriert ist. Der Standard-Backtest berechnet VaR-Verletzungen — Tage, an denen der tatsächliche Verlust den prognostizierten VaR überstieg — und testet, ob die Verletzungsrate der erwarteten Rate entspricht.

def backtest_var(returns, symbols, weights, window=500,
                 alpha=0.99, n_sim=20_000):
    """
    Rolling-window VaR backtest using vine copula.
    """
    violations = []
    var_series = []
    T = len(returns)

    for t in range(window, T):
        window_returns = returns.iloc[t-window:t]

        U_window = np.zeros((window, len(symbols)))
        models_t = {}
        for j, sym in enumerate(symbols):
            std_resid, model = fit_garch_marginal(window_returns[sym])
            models_t[sym] = model
            u = pv.to_pseudo_obs(std_resid.values.reshape(-1, 1))
            U_window[:len(u), j] = u.ravel()

        U_clean = U_window[~np.any(U_window == 0, axis=1)]
        vine_t = pv.Vinecop(U_clean, controls=controls)

        var_t, _, _ = estimate_var_cvar(
            vine_t, models_t, symbols, weights,
            n_sim=n_sim, alpha=alpha
        )
        var_series.append(var_t)

        actual_return = (returns.iloc[t][symbols].values
                         * np.array(weights)).sum()
        violations.append(-actual_return > var_t)

    violation_rate = np.mean(violations)
    expected_rate = 1 - alpha
    print(f"Expected violation rate: {expected_rate:.4f}")
    print(f"Actual violation rate:   {violation_rate:.4f}")
    print(f"Number of violations:    {sum(violations)} / {len(violations)}")

    return violations, var_series

Ein gut kalibriertes 99%-VaR-Modell sollte eine Verletzungsrate nahe bei 1% haben. Ist die Rate deutlich höher, unterschätzt das Modell das Risiko. Ist sie deutlich niedriger, ist es zu konservativ.

Praktische Überlegungen

Rechenaufwand

Die Anpassung einer Vine-Copula ist O(d2n)O(d^2 \cdot n) pro Baumebene. Für ein 10-Asset-Portfolio mit 500-Tage-Rolling-Windows kann ein vollständiger Backtest mit 50.000 Monte-Carlo-Simulationen pro Schritt Stunden dauern. Strategien zur Bewältigung:

  • Trunkierte Vines: trunc_lvl=3 oder trunc_lvl=4 setzen — höhere Bäume erfassen schwächere bedingte Abhängigkeiten, die weniger zum Risiko beitragen
  • Reduzierte Simulationsanzahl: 10.000-20.000 Simulationen reichen oft für den 99%-VaR
  • Parallele Berechnung: Die GARCH-Fits für jedes Asset sind unabhängig und können parallelisiert werden
  • Modell-Caching: Die Copula wöchentlich statt täglich neu anpassen, wobei nur die GARCH-Prognosen aktualisiert werden

Regime-Bewusstsein

Kryptomärkte weisen unterschiedliche Regime auf (Bulle, Bär, Seitwärts, Hochvolatilitätsereignisse). Eine einzige Vine-Copula, die über die gesamte Stichprobe angepasst wird, erfasst möglicherweise keine regimeabhängige Abhängigkeit. In Betracht ziehen:

  • Rolling Windows von 250-500 Tagen
  • Regime-wechselnde Copulas, bei denen die Copula-Parameter von einem verborgenen Markov-Zustand abhängen
  • Exponentiell gewichtete Beobachtungen, die aktuelleren Daten mehr Gewicht geben

Häufige Fallstricke

  1. Die PIT vergessen. Werden rohe Renditen direkt statt pseudo-gleichverteilter Beobachtungen in die Copula eingespeist, entstehen bedeutungslose Ergebnisse. Immer zuerst in gleichverteilte Ränder transformieren.
  2. Überanpassung mit zu vielen Familien. Werden alle möglichen bivariaten Familien in die Auswahlmenge aufgenommen, kann dies zu Überanpassung führen, besonders bei kurzen Stichproben. BIC für die Modellauswahl verwenden und eine Beschränkung auf 4-5 Familien in Betracht ziehen.
  3. Serielle Abhängigkeit ignorieren. Copulas modellieren Querschnittsabhängigkeit an einem einzigen Zeitpunkt. Wird der GARCH-Schritt übersprungen und autokorrelierte Renditen in die Copula eingespeist, wird die geschätzte Abhängigkeit durch serielle Effekte verfälscht.
  4. Statische Copulas in dynamischen Märkten. Eine an Bullenmarktdaten von 2021 angepasste Copula wird für einen Crash von 2022 schlecht kalibriert sein. Immer Rolling- oder Expanding-Windows verwenden.

Fazit

Copula-Modelle — insbesondere Vine-Copulas — bieten ein mathematisch rigoroses Framework zur Modellierung des gemeinsamen Risikos von Krypto-Portfolios, das weit über das hinausgeht, was lineare Korrelation erfassen kann. Die wichtigsten Vorteile:

  • Getrennte Modellierung von Rand- und Abhängigkeitsstruktur über den Satz von Sklar
  • Flexible Tail-Abhängigkeit durch geeignete Copula-Familienwahl (Clayton für Crash-Ansteckung, Gumbel für gemeinsame Rally-Bewegungen, t-Copula für symmetrische Tails)
  • Skalierbarkeit für hohe Dimensionen durch Vine-Copula-Zerlegung, bei der jedes Asset-Paar seine eigene bivariate Copula erhält
  • Präzise VaR/CVaR-Schätzung, die nichtlineare, asymmetrische Abhängigkeit berücksichtigt — entscheidend für das Risikomanagement in einem Markt, in dem "alles stürzt gemeinsam ab" die Norm und nicht die Ausnahme ist

Die GARCH-EVT-Copula-Pipeline ist mittlerweile der Standardansatz bei quantitativen Hedgefonds und krypto-fokussierten Risikoabteilungen. Mit Bibliotheken wie pyvinecopulib ist die Implementierungshürde niedrig genug, dass jeder systematische Trader die Copula-basierte Risikomodellierung in seinen Portfoliomanagement-Workflow integrieren kann.

Der Code in diesem Artikel bietet einen funktionierenden Ausgangspunkt. Für den Produktionseinsatz würde man eine ordnungsgemäße Kreuzvalidierung für die GARCH-Ordnungswahl, anspruchsvollere Randmodelle (z. B. EGARCH mit Leverage-Effekten oder realisierte Volatilitätsmaße mit Intraday-Daten) sowie Stresstests unter hypothetischen Copula-Parametern, die auf historische Krisenepisoden kalibriert sind, hinzufügen.


Referenzen

  • Sklar, A. (1959). Fonctions de repartition a n dimensions et leurs marges. Publications de l'Institut de Statistique de l'Universite de Paris, 8, 229-231.
  • Joe, H. (2014). Dependence Modeling with Copulas. Chapman and Hall/CRC.
  • Aas, K., Czado, C., Frigessi, A., & Bakken, H. (2009). Pair-copula constructions of multiple dependence. Insurance: Mathematics and Economics, 44(2), 182-198.
  • Jeleskovic, V. & Bruhn, L. (2024). Cryptocurrency portfolio optimization: Utilizing a GARCH-Copula model within the Markowitz framework. Journal of Corporate Accounting & Finance.
  • Nagler, T. & Vatter, T. (2023). pyvinecopulib: A Python library for vine copula models. GitHub.
  • Tiwari, A. K., et al. (2020). Modeling risk dependence and portfolio VaR forecast through vine copula for cryptocurrencies. PLOS ONE, 15(1), e0242102.
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Dem Markt einen Schritt voraus

Abonniere unseren Newsletter für exklusive KI-Trading-Einblicke, Marktanalysen und Plattform-Updates.

Wir respektieren deine Privatsphäre. Jederzeit abbestellbar.