Als leitender Finanztechnologie-Ingenieur mit über acht Jahren Erfahrung in der Derivatepreisbewertung habe ich zahlreiche Volatilitätsmodelle für Kryptowährungsoptionen implementiert und evaluiert. In diesem deep-dive Artikel vergleiche ich zwei der leistungsfähigsten Ansätze — das lokale Volatilitätsmodell (Local Volatility, LV) und das SABR-Modell — hinsichtlich ihrer mathematischen Grundlagen, Implementierungseffizienz und praktischen Anwendbarkeit im Produktionsumfeld.

1. Theoretischer Hintergrund und mathematische Grundlagen

Die Preisgestaltung von Kryptowährungsoptionen stellt uns vor einzigartige Herausforderungen: extreme Volatilität, Fat-Tailed-Verteilungen, Regime-Shifts und die berüchtigte Volatility-Smile-Sruktur, die bei Bitcoin- und Ethereum-Optionen besonders ausgeprägt ist.

1.1 Lokales Volatilitätsmodell (Dupire-Framework)

Das lokale Volatilitätsmodell, formalisiert durch den Dupire-Gleichung, leitet die即时波动率Oberfläche direkt aus den beobachtbaren Marktpreisen ab:

"""
Lokales Volatilitätsmodell nach Dupire
Implementierung für Kryptowährungsoptionen
"""
import numpy as np
from scipy.interpolate import CubicSpline
from scipy.optimize import brentq

class LocalVolatilityModel:
    def __init__(self, spot: float, rate: float, dividend: float = 0.0):
        self.S = spot          # Spot-Preis (z.B. BTC/USD)
        self.r = rate          # Risikofreier Zinssatz
        self.q = dividend      # Dividenden-/Staking-Yield für Krypto
        
    def dupire_local_vol(self, strikes: np.ndarray, maturities: np.ndarray, 
                         call_prices: np.ndarray) -> np.ndarray:
        """
        Berechnet die lokale Volatilitätsfläche mittels Dupire-Gleichung
        
        σ²(K,T) = [∂C/∂T + (r-q)K·∂C/∂K + qC] / [½K²·∂²C/∂K²]
        
        Args:
            strikes: Ausübungspreise (Array)
            maturities: Laufzeiten (Array)
            call_prices: Marktpreise der Calls (Matrix: rows=strikes, cols=maturities)
        """
        n_strikes = len(strikes)
        n_mats = len(maturities)
        local_vol = np.zeros((n_strikes, n_mats))
        
        # Analytische Gradienten via finite differences
        dK = np.gradient(strikes)
        dT = np.gradient(maturities)
        
        for i, T in enumerate(maturities):
            for j, K in enumerate(strikes):
                if j == 0 or j == n_strikes - 1 or i == 0:
                    local_vol[j, i] = np.nan
                    continue
                    
                # Partielle Ableitungen zweiter Ordnung
                d2C_dK2 = (call_prices[j+1, i] - 2*call_prices[j, i] + 
                          call_prices[j-1, i]) / (dK[j]**2)
                
                # Partielle Ableitung erster Ordnung
                dC_dK = (call_prices[j+1, i] - call_prices[j-1, i]) / (2*dK[j])
                
                # Zeitliche Ableitung
                if i > 0:
                    dC_dT = (call_prices[j, i] - call_prices[j, i-1]) / dT[i]
                else:
                    dC_dT = 0.0
                
                # Vermeidung Division durch Null
                if abs(d2C_dK2) < 1e-10:
                    local_vol[j, i] = local_vol[j-1, i] if j > 0 else 0.5
                    continue
                
                # Dupire-Formel
                numerator = dC_dT + (self.r - self.q) * K * dC_dK + self.q * call_prices[j, i]
                denominator = 0.5 * (K ** 2) * d2C_dK2
                
                if denominator > 0:
                    local_vol[j, i] = np.sqrt(numerator / denominator)
                else:
                    local_vol[j, i] = np.nan
                    
        return local_vol
    
    def price_european_pde(self, K: float, T: float, is_call: bool = True,
                           n_space: int = 500, n_time: int = 200) -> float:
        """
        Finite-Difference PDE-Löser für europäische Optionen unter lokaler Volatilität
        Crank-Nicolson Schema mit Richardson-Extrapolation
        
        Returns: Optionspreis
        """
        S_max = 3 * K
        dS = S_max / n_space
        dt = T / n_time
        
        # Volatilitäts-Gitter (vereinfacht: konstante lokale Vol)
        sigma = self._get_local_vol_at_maturity(T)
        
        # Preisgitter initialisieren
        V = np.zeros((n_space + 1, n_time + 1))
        
        # Randbedingungen
        if is_call:
            V[:, -1] = np.maximum(np.linspace(0, S_max, n_space + 1) - K, 0)
        else:
            V[:, -1] = np.maximum(K - np.linspace(0, S_max, n_space + 1), 0)
        
        # Crank-Nicolson Iteration
        for n in range(n_time - 1, -1, -1):
            for i in range(1, n_space):
                S = i * dS
                
                # Koeffizienten
                a = 0.25 * dt * (sigma**2 * S**2 / dS**2 - 
                                (self.r - self.q) * S / dS)
                b = -0.5 * dt * (sigma**2 * S**2 / dS**2 + self.r)
                c = 0.25 * dt * (sigma**2 * S**2 / dS**2 + 
                                (self.r - self.q) * S / dS)
                
                V[i, n] = a * V[i-1, n+1] + (1 + b) * V[i, n+1] + c * V[i+1, n+1]
        
        # Lineare Interpolation für finalen Preis
        idx = int(self.S / dS)
        return np.interp(self.S, np.linspace(0, S_max, n_space + 1), V[:, 0])

1.2 SABR-Modell (Stochastic Alpha Beta Rho)

Das SABR-Modell parametert die Volatilitätsdynamik direkt im risikoneutralen Maß und modelliert vier stochastische Prozesse simultan:

"""
SABR-Modell Implementierung für Krypto-Optionen
Stochastic Alpha, Beta, Rho, Sigma
"""
from dataclasses import dataclass
from typing import Tuple, Optional
import numpy as np
from scipy.stats import norm
from scipy.optimize import minimize, differential_evolution

@dataclass
class SAB