Skip to content

Delta-Gamma-Vega Hedging

Background

Delta-Gamma-Vega Hedging Simulation.

This module implements multi-greek hedging strategies that simultaneously hedge delta (directional risk), gamma (convexity), and vega (volatility risk).

Realistic hedging requires managing multiple Greeks, not just delta.


Code

```python

delta_gamma_vega_hedging.py

""" Delta-Gamma-Vega Hedging Simulation.

This module implements multi-greek hedging strategies that simultaneously hedge delta (directional risk), gamma (convexity), and vega (volatility risk).

Realistic hedging requires managing multiple Greeks, not just delta. """

import numpy as np import pandas as pd from typing import Dict, Tuple, List from scipy.stats import norm

======================================================================

class MultiGreekHedger: """Portfolio hedging with delta, gamma, and vega constraints."""

def __init__(self, S0: float, K_option: float, T: float, r: float, sigma: float):
    """
    Initialize hedging portfolio.

    Args:
        S0: Initial spot price
        K_option: Option strike to hedge
        T: Time to maturity
        r: Risk-free rate
        sigma: Volatility
    """
    self.S0 = S0
    self.K_option = K_option
    self.T = T
    self.r = r
    self.sigma = sigma

    # Hedge instruments: spot, call, put
    self.K_call_hedge = S0  # ATM call
    self.K_put_hedge = S0   # ATM put

def _compute_d1_d2(self, S: float, K: float, T: float) -> Tuple[float, float]:
    """Compute d1, d2 for Black-Scholes."""
    d1 = (np.log(S / K) + (self.r + 0.5 * self.sigma**2) * T) / (
        self.sigma * np.sqrt(T)
    )
    d2 = d1 - self.sigma * np.sqrt(T)
    return d1, d2

def get_greeks(self, S: float, K: float, T: float, position: str = "long") -> Dict:
    """
    Compute Greeks for an option position.

    Args:
        S: Current spot price
        K: Option strike
        T: Time to maturity
        position: "long" (bought) or "short" (sold)

    Returns:
        Dictionary with delta, gamma, vega
    """
    d1, d2 = self._compute_d1_d2(S, K, T)

    delta = norm.cdf(d1)
    gamma = norm.pdf(d1) / (S * self.sigma * np.sqrt(T)) if S > 0 else 0
    vega = S * norm.pdf(d1) * np.sqrt(T) / 100  # Per 1% vol change

    if position == "short":
        delta = -delta
        gamma = -gamma
        vega = -vega

    return {"delta": delta, "gamma": gamma, "vega": vega}

def construct_hedge(
    self,
    S: float,
    position: str = "long",
    target_delta: float = 0.0,
    target_gamma: float = 0.0,
    target_vega: float = 0.0
) -> Dict[str, float]:
    """
    Construct hedge portfolio to neutralize Greeks.

    Uses spot (delta only), call (positive gamma/vega), and put (positive gamma/vega)
    to hedge a short option position.

    Args:
        S: Current spot price
        position: "long" (short option, long hedge) or "short" (long option, short hedge)
        target_delta/gamma/vega: Target Greeks to achieve

    Returns:
        Dictionary with quantities: {'spot': x, 'call': y, 'put': z}
    """
    # Greeks for each instrument at current S
    greeks_spot = {"delta": 1, "gamma": 0, "vega": 0}
    greeks_call = self.get_greeks(S, self.K_call_hedge, self.T, position="long")
    greeks_put = self.get_greeks(S, self.K_put_hedge, self.T, position="long")

    # System: Ax = b where x = [n_spot, n_call, n_put]
    # A = [[Δ_spot, Δ_call, Δ_put],
    #      [Γ_spot, Γ_call, Γ_put],
    #      [V_spot, V_call, V_put]]
    A = np.array([
        [greeks_spot["delta"], greeks_call["delta"], greeks_put["delta"]],
        [greeks_spot["gamma"], greeks_call["gamma"], greeks_put["gamma"]],
        [greeks_spot["vega"], greeks_call["vega"], greeks_put["vega"]]
    ])

    b = np.array([target_delta, target_gamma, target_vega])

    try:
        x = np.linalg.solve(A, b)
        return {
            "spot": x[0],
            "call": x[1],
            "put": x[2]
        }
    except np.linalg.LinAlgError:
        # If system singular, use least-squares approximation
        x, _, _, _ = np.linalg.lstsq(A, b, rcond=None)
        return {
            "spot": x[0],
            "call": x[1],
            "put": x[2]
        }

def portfolio_greeks(
    self,
    S: float,
    positions: Dict[str, float]
) -> Dict[str, float]:
    """
    Compute total Greeks for a portfolio.

    Args:
        S: Current spot price
        positions: Dictionary with keys 'option', 'spot', 'call', 'put'
                  and quantities (negative = short)

    Returns:
        Dictionary with total delta, gamma, vega
    """
    total_greeks = {"delta": 0, "gamma": 0, "vega": 0}

    # Option position
    if "option" in positions:
        greeks_opt = self.get_greeks(S, self.K_option, self.T, "long")
        for greek in total_greeks:
            total_greeks[greek] += positions["option"] * greeks_opt[greek]

    # Spot position
    if "spot" in positions:
        total_greeks["delta"] += positions["spot"]

    # Call hedge
    if "call" in positions:
        greeks_call = self.get_greeks(S, self.K_call_hedge, self.T, "long")
        for greek in total_greeks:
            total_greeks[greek] += positions["call"] * greeks_call[greek]

    # Put hedge
    if "put" in positions:
        greeks_put = self.get_greeks(S, self.K_put_hedge, self.T, "long")
        for greek in total_greeks:
            total_greeks[greek] += positions["put"] * greeks_put[greek]

    return total_greeks

def simulate_p_and_l(
    self,
    initial_S: float,
    positions: Dict[str, float],
    spot_scenarios: np.ndarray,
    vol_changes: np.ndarray = None,
    time_decay: float = 1/252  # One trading day
) -> Tuple[np.ndarray, pd.DataFrame]:
    """
    Simulate P&L across spot and volatility scenarios.

    Args:
        initial_S: Initial spot price
        positions: Dictionary with quantities
        spot_scenarios: Array of spot prices to test
        vol_changes: Array of volatility changes (in percent)
        time_decay: Time decay in years (default 1 day)

    Returns:
        Tuple of (pnl_array, summary_df)
    """
    if vol_changes is None:
        vol_changes = np.array([-1, 0, 1])  # -1%, 0%, +1% vol changes

    # Compute initial portfolio Greeks at spot 0 (reference)
    initial_greeks = self.portfolio_greeks(initial_S, positions)

    pnl_matrix = np.zeros((len(spot_scenarios), len(vol_changes)))

    for i, S_new in enumerate(spot_scenarios):
        for j, vol_change in enumerate(vol_changes):
            dS = S_new - initial_S
            sigma_new = self.sigma + vol_change / 100

            # Taylor expansion: P&L ≈ Δ·dS + 0.5·Γ·dS² + V·dσ - Θ·dt
            greeks = self.portfolio_greeks(initial_S, positions)

            delta_pnl = greeks["delta"] * dS
            gamma_pnl = 0.5 * greeks["gamma"] * dS**2
            vega_pnl = greeks["vega"] * vol_change  # vol_change in percent

            # Simplified theta (ignored for brevity)
            pnl = delta_pnl + gamma_pnl + vega_pnl
            pnl_matrix[i, j] = pnl

    # Create summary dataframe
    summary_data = {
        'Spot': spot_scenarios,
    }
    for j, vol_change in enumerate(vol_changes):
        summary_data[f'Vol {vol_change:+.0f}%'] = pnl_matrix[:, j]

    df = pd.DataFrame(summary_data)
    return pnl_matrix, df

def compute_hedge_effectiveness(
    self,
    S_initial: float,
    positions_unhedged: Dict[str, float],
    positions_hedged: Dict[str, float],
    scenarios: List[Tuple[float, float]]  # (spot, vol_change_pct)
) -> Dict:
    """
    Compare P&L variance between hedged and unhedged portfolios.

    Args:
        S_initial: Initial spot
        positions_unhedged: Unhedged portfolio
        positions_hedged: Hedged portfolio
        scenarios: List of (S_new, vol_change) scenarios

    Returns:
        Dictionary with variance reduction metrics
    """
    pnl_unhedged = []
    pnl_hedged = []

    for S_new, vol_change in scenarios:
        # Compute Greeks at initial spot
        dS = S_new - S_initial

        # Unhedged
        greeks_uh = self.portfolio_greeks(S_initial, positions_unhedged)
        pnl_uh = (greeks_uh["delta"] * dS + 
                 0.5 * greeks_uh["gamma"] * dS**2 + 
                 greeks_uh["vega"] * vol_change)
        pnl_unhedged.append(pnl_uh)

        # Hedged
        greeks_h = self.portfolio_greeks(S_initial, positions_hedged)
        pnl_h = (greeks_h["delta"] * dS + 
                0.5 * greeks_h["gamma"] * dS**2 + 
                greeks_h["vega"] * vol_change)
        pnl_hedged.append(pnl_h)

    pnl_unhedged = np.array(pnl_unhedged)
    pnl_hedged = np.array(pnl_hedged)

    var_reduction = 1 - (np.var(pnl_hedged) / np.var(pnl_unhedged))

    return {
        "unhedged_var": np.var(pnl_unhedged),
        "hedged_var": np.var(pnl_hedged),
        "variance_reduction": var_reduction,
        "unhedged_mean_pnl": np.mean(pnl_unhedged),
        "hedged_mean_pnl": np.mean(pnl_hedged)
    }

def example_delta_gamma_vega_hedge(): """Example: hedge a short call position.""" hedger = MultiGreekHedger(S0=100, K_option=100, T=0.25, r=0.05, sigma=0.20)

# Short 1 call option
positions_unhedged = {
    "option": -1,  # Short
    "spot": 0,
    "call": 0,
    "put": 0
}

print("Multi-Greek Hedging Example")
print("=" * 60)
print("\nUnhedged Position (short 1 call at-the-money):")
greeks_unhedged = hedger.portfolio_greeks(100, positions_unhedged)
print(f"  Delta: {greeks_unhedged['delta']:.4f}")
print(f"  Gamma: {greeks_unhedged['gamma']:.6f}")
print(f"  Vega:  {greeks_unhedged['vega']:.4f}")

# Construct hedge
hedge_positions = hedger.construct_hedge(100, "short")
print(f"\nHedge Portfolio:")
print(f"  Spot:  {hedge_positions['spot']:.4f} shares")
print(f"  Call:  {hedge_positions['call']:.4f} contracts")
print(f"  Put:   {hedge_positions['put']:.4f} contracts")

# Hedged position
positions_hedged = {
    "option": -1,
    "spot": hedge_positions["spot"],
    "call": hedge_positions["call"],
    "put": hedge_positions["put"]
}

print("\nHedged Position:")
greeks_hedged = hedger.portfolio_greeks(100, positions_hedged)
print(f"  Delta: {greeks_hedged['delta']:.4f}")
print(f"  Gamma: {greeks_hedged['gamma']:.6f}")
print(f"  Vega:  {greeks_hedged['vega']:.4f}")

# P&L scenarios
spots = np.linspace(90, 110, 21)
vol_changes = np.array([-2, -1, 0, 1, 2])

pnl, df = hedger.simulate_p_and_l(100, positions_hedged, spots, vol_changes)
print("\nP&L at different spot/vol levels (hedged):")
print(df.to_string(index=False))

if name == "main": example_delta_gamma_vega_hedge() ```

Exercises

Exercise 1. To hedge a short call position against delta, gamma, and vega risks, you need three instruments. Explain why the spot asset alone cannot hedge gamma or vega.

Solution to Exercise 1

The spot asset has \(\Delta = 1\), \(\Gamma = 0\), \(\text{Vega} = 0\). It can only adjust the portfolio delta. Gamma and vega hedging require instruments with nonzero gamma and vega, which means other options. The system \(A\mathbf{x} = \mathbf{b}\) with \(A = [\Delta, \Gamma, \text{Vega}]\) for spot, call, and put gives three equations in three unknowns, uniquely determining the hedge quantities.


Exercise 2. If the portfolio delta is \(-0.55\), gamma is \(-0.02\), and vega is \(-0.38\), and a hedging call has \(\Delta = 0.5\), \(\Gamma = 0.02\), \(\text{Vega} = 0.19\), compute the number of calls needed to neutralize gamma.

Solution to Exercise 2

To neutralize gamma: \(n_{\text{call}} \times 0.02 = 0.02\), so \(n_{\text{call}} = 1\). After adding 1 call: delta becomes \(-0.55 + 0.5 = -0.05\) and vega becomes \(-0.38 + 0.19 = -0.19\). Additional spot and put positions are needed to zero out remaining delta and vega.


Exercise 3. Explain the P&L Taylor expansion: \(\text{P\&L} \approx \Delta \cdot dS + \frac{1}{2}\Gamma \cdot (dS)^2 + \text{Vega} \cdot d\sigma - \Theta \cdot dt\). Which terms dominate for small versus large moves?

Solution to Exercise 3

For small moves (\(|dS| < 1\%\)): the linear delta term dominates. For large moves (\(|dS| > 5\%\)): the quadratic gamma term becomes important, especially for short gamma positions. The vega term dominates during volatility regime changes. Theta is the constant daily cost/income. A delta-gamma-vega-neutral portfolio has P&L \(\approx -\Theta \cdot dt\) plus higher-order residuals.


Exercise 4. Compute the variance reduction from delta-gamma-vega hedging versus delta-only hedging for a short call, given scenarios with \(dS \in [-10, 10]\) and \(d\sigma \in [-2\%, 2\%]\).

Solution to Exercise 4

Delta-only hedging leaves residual gamma and vega exposure: P&L variance \(\approx \text{Var}(\frac{1}{2}\Gamma(dS)^2) + \text{Var}(\text{Vega} \cdot d\sigma) + 2\text{Cov}\). For typical parameters, \(\text{Var}(\Gamma \text{ term}) \approx \Gamma^2 \mathbb{E}[(dS)^4]/4\) and \(\text{Var}(\text{Vega term}) \approx \text{Vega}^2\text{Var}(d\sigma)\). Full hedging eliminates both, leaving only higher-order terms. Typical variance reduction is 90--99%.