Volatility Trading and Arbitrage Strategies › Variance Swaps

Michael BrenndoerferDecember 30, 20259 min read
Quantitative FinanceAll chapters
Part 11 of 15Quantitative Trading Strategies
Chapter 63Volatility Trading and Arbitrage Strategies › Variance Swaps
Reading section 9 of 15 · Variance Swaps · 9 minSection 9 of 15 · 9 min

Book contents

Section 9 of 15 · 9 min read

Volatility Trading and Arbitrage Strategies › Variance SwapsOne section · Sections in this chapter9 / 15 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Variance swaps are derivatives that provide pure exposure to realized variance, making them a cleaner instrument for volatility trading than delta-hedged options. Unlike options, which have path-dependent Greeks that require continuous hedging, variance swaps pay off directly based on the total realized variance over their life. This simplicity makes them attractive for you if you want volatility exposure without the operational complexity of dynamic hedging.

Structure and Payoff

A variance swap is a forward contract on realized variance. At maturity, the payoff is:

Payoff=Nvar×(σreal2−Kvar)\text{Payoff} = N_{\text{var}} \times (\sigma_{\text{real}}^2 - K_{\text{var}})

where:

  • Payoff\text{Payoff}: payment received at maturity
  • NvarN_{\text{var}}: variance notional (typically quoted as vega notional divided by 2Kvol2K_{\text{vol}})
  • σreal2\sigma_{\text{real}}^2: annualized realized variance over the swap's life
  • KvarK_{\text{var}}: variance strike (typically expressed as volatility: Kvol2=KvarK_{\text{vol}}^2 = K_{\text{var}})

The payoff formula is simple. You receive a payment proportional to the difference between actual realized variance and a predetermined strike level. If the market is more volatile than the strike implies, you profit. If the market is calmer than expected, you pay the seller. Unlike options, there is no optionality here: the trade settles purely based on what happens to variance, with no path dependence in terms of when or how the moves occur.

The realized variance is calculated from daily log returns:

σreal2=252n∑i=1n(ln⁡SiSi−1)2\sigma_{\text{real}}^2 = \frac{252}{n} \sum_{i=1}^{n} \left( \ln \frac{S_i}{S_{i-1}} \right)^2

where:

  • nn: number of observation days
  • SiS_i: price of the asset on day ii
  • Si−1S_{i-1}: price of the asset on day i−1i-1
  • 252252: annualization factor (assuming trading days)

Note that this formula uses the sum of squared returns directly, not the variance around the mean. This is the standard market convention and ensures the calculation is path-dependent and captures all price movements. The distinction matters: by using squared returns rather than variance around the mean, the calculation captures both the variance and any drift in the returns. This convention means that a market that trends strongly in one direction will register higher "realized variance" than one that fluctuates around a stable level, even if the two have identical standard deviations. This feature makes variance swaps sensitive to all price movements, not just deviations from trend.

In[30]:
Code
def variance_swap_realized(prices):
    """Calculate realized variance for a variance swap."""
    log_returns = np.diff(np.log(prices))
    n = len(log_returns)
    realized_var = (252 / n) * np.sum(log_returns**2)
    return realized_var


def variance_swap_payoff(prices, strike_vol, vega_notional):
    """
    Calculate variance swap payoff.

    Parameters:
    - prices: Array of daily prices
    - strike_vol: Volatility strike (e.g., 0.20 for 20%)
    - vega_notional: Notional amount per 1% move in volatility
    """
    realized_var = variance_swap_realized(prices)
    strike_var = strike_vol**2
    realized_vol = np.sqrt(realized_var)

    # Variance notional = vega_notional / (2 * strike_vol)
    var_notional = vega_notional / (2 * strike_vol)

    payoff = var_notional * (realized_var - strike_var)

    return {
        "realized_vol": realized_vol,
        "strike_vol": strike_vol,
        "realized_var": realized_var,
        "strike_var": strike_var,
        "var_notional": var_notional,
        "payoff": payoff,
    }


# Generate sample price paths
np.random.seed(42)
n_days = 30

# Low volatility regime
returns_low = np.random.normal(0, 0.012, n_days)  # ~19% annualized
prices_low = 100 * np.exp(np.cumsum(returns_low))
prices_low = np.insert(prices_low, 0, 100)

# High volatility regime
returns_high = np.random.normal(0, 0.022, n_days)  # ~35% annualized
prices_high = 100 * np.exp(np.cumsum(returns_high))
prices_high = np.insert(prices_high, 0, 100)

strike_vol = 0.25
vega_notional = 100000  # $100,000 per vol point

result_low = variance_swap_payoff(prices_low, strike_vol, vega_notional)
result_high = variance_swap_payoff(prices_high, strike_vol, vega_notional)
Out[31]:
Console
Variance Swap Payoff Analysis
==================================================
Strike Volatility: 25.0%
Vega Notional: $100,000
Variance Notional: $200,000

Low Volatility Scenario:
  Realized Vol: 17.2%
  Long Variance Swap Payoff: $-6,560

High Volatility Scenario:
  Realized Vol: 32.2%
  Long Variance Swap Payoff: $8,301
Out[32]:
Visualization
Convex long-variance-swap payoff crosses zero at the 25 percent strike; marked low-volatility and high-volatility scenarios fall in the loss and profit regions.
The convex payoff profile of a variance swap relative to realized volatility. Because the swap settles on variance (volatility squared), an increase in volatility generates disproportionately larger profits than a decrease of the same magnitude generates losses.

The variance swap generates a positive payoff when realized volatility exceeds the strike volatility. Note that in the high volatility scenario, the payoff is substantial because the realized variance (volatility squared) increases quadratically, making the payout convex with respect to volatility moves. This convexity is a critical feature of variance swaps: a move from 25% to 35% volatility generates more profit than a move from 25% to 15% volatility generates loss, making variance swaps attractive instruments for long volatility positions.

Replication and Pricing

The variance swap strike is determined by a portfolio of options across all strikes. The theoretical result, derived by Demeterfi, Derman, Kamal, and Zou (1999), shows that variance can be replicated by a portfolio of options with weights inversely proportional to the square of the strike:

Kvar=2erTT[∫0FP(K)K2dK+∫F∞C(K)K2dK]K_{\text{var}} = \frac{2e^{rT}}{T} \left[ \int_0^{F} \frac{P(K)}{K^2} dK + \int_{F}^{\infty} \frac{C(K)}{K^2} dK \right]

where:

  • KvarK_{\text{var}}: variance strike
  • rr: risk-free interest rate
  • TT: time to expiration
  • FF: forward price of the underlying asset
  • KK: strike price
  • P(K)P(K): price of a put option with strike KK
  • C(K)C(K): price of a call option with strike KK

Variance exposure can be constructed from a portfolio of vanilla options. The key insight is that options at different strikes contribute differently to variance: lower strike options contribute more (because of the 1/K21/K^2 weighting) than higher strike options. This overweighting of low strikes reflects the fact that the logarithm function used in variance calculation is more sensitive to downward price moves than upward ones.

The formula splits the replication into two parts based on the forward price FF:

  • ∫0FP(K)K2dK\int_0^{F} \frac{P(K)}{K^2} dK: replicates variance using out-of-the-money puts for strikes below the forward price
  • ∫F∞C(K)K2dK\int_{F}^{\infty} \frac{C(K)}{K^2} dK: replicates variance using out-of-the-money calls for strikes above the forward price

Using out-of-the-money options at each strike is a convention that minimizes the cost of maintaining the replicating portfolio, as OTM options have lower premiums than their in-the-money counterparts while providing the same exposure to variance.

In practice, this integral is approximated using available option strikes:

In[33]:
Code
def price_variance_swap_strike(strikes, option_prices, is_call, S, r, T):
    """
    Approximate variance swap strike using discrete option prices.

    Uses out-of-the-money options: puts below forward, calls above.
    """
    F = S * np.exp(r * T)  # Forward price

    # Sort strikes
    sorted_idx = np.argsort(strikes)
    strikes = strikes[sorted_idx]
    option_prices = option_prices[sorted_idx]
    is_call = is_call[sorted_idx]

    variance = 0

    for i in range(len(strikes)):
        K = strikes[i]
        price = option_prices[i]

        # Use OTM options only
        if K < F and not is_call[i]:  # OTM put
            # Calculate strike spacing for numerical integration
            if i > 0:
                dK = (strikes[i] - strikes[i - 1]) / 2
            else:
                dK = strikes[1] - strikes[0]
            if i < len(strikes) - 1:
                dK += (strikes[i + 1] - strikes[i]) / 2

            variance += (2 / K**2) * price * dK

        elif K >= F and is_call[i]:  # OTM call
            if i > 0:
                dK = (strikes[i] - strikes[i - 1]) / 2
            else:
                dK = strikes[1] - strikes[0]
            if i < len(strikes) - 1:
                dK += (strikes[i + 1] - strikes[i]) / 2

            variance += (2 / K**2) * price * dK

    # Annualize
    variance = (np.exp(r * T) / T) * variance

    return np.sqrt(variance)


# Example: Price variance swap from option chain
S = 100
r = 0.05
T = 30 / 365
sigma = 0.25

# Generate option chain
strikes = np.arange(85, 116, 2.5)
option_prices = []
is_call = []

for K in strikes:
    if K < S:  # Use put
        price = black_scholes_put(S, K, T, r, sigma)
        option_prices.append(price)
        is_call.append(False)
    else:  # Use call
        price, _, _ = black_scholes_call(S, K, T, r, sigma)
        option_prices.append(price)
        is_call.append(True)

option_prices = np.array(option_prices)
is_call = np.array(is_call)

fair_vol = price_variance_swap_strike(strikes, option_prices, is_call, S, r, T)
Out[34]:
Console
Black-Scholes Implied Vol: 25.0%
Variance Swap Fair Vol: 21.5%

Difference: -3.54% (convexity adjustment)

The variance swap fair strike is typically slightly higher than at-the-money implied volatility due to the convexity in the variance payoff. This convexity adjustment compensates for the asymmetric exposure to volatility: realized variance of 40% versus 20% strike creates more P&L than 20% versus 20%. The convexity adjustment exists because variance is the square of volatility: a symmetrical distribution of volatility outcomes translates into an asymmetrical distribution of variance outcomes, with large volatility outcomes contributing disproportionately to expected variance.

Vega Notional Versus Variance Notional

Variance swaps can be quoted in two ways:

Variance notional (NvarN_{var}): The dollar amount paid per point of variance. A variance notional of $1,000 pays $1,000 for each 0.0001 (1 bp) difference in variance.

Vega notional: The approximate P&L for a 1 percentage point change in volatility. For a vega notional of $100,000, if volatility moves from 25% to 26%, the P&L is approximately $100,000.

The relationship between them is:

Nvar=Nvega2×KvolN_{\text{var}} = \frac{N_{\text{vega}}}{2 \times K_{\text{vol}}}

where:

  • NvarN_{\text{var}}: variance notional
  • NvegaN_{\text{vega}}: vega notional (P&L per 1 vol point change)
  • KvolK_{\text{vol}}: volatility strike

This conversion is approximate because the variance payoff is convex in volatility. The formula comes from taking the derivative of variance with respect to volatility at the strike level: the derivative of σ2\sigma^2 with respect to σ\sigma is 2σ2\sigma, so at the strike volatility KvolK_{vol}, a small change in volatility creates a variance change of approximately 2×Kvol2 \times K_{vol} times the volatility change. The vega notional convention makes it easier for you to think in terms of volatility points, which are more intuitive than variance points.