Demystifying volatility swaps

May 25, 2024
Facebook logo.
Twitter logo.
LinkedIn logo.

Demystifying volatility swaps

Volatility can wreak havoc on your portfolio. Understanding and managing this risk is the number one job for any serious investor. Many use options or futures to hedge against volatility, but these approaches can fall short and leave portfolios exposed during extreme market conditions.

This is where volatility swaps can help

These derivatives traders hedge against volatility in a more targeted and effective way. However, pricing and using volatility swaps can be mysterious. Something only professionals with advanced modeling techniques can understand.

Not anymore.

By reading today’s newsletter, you’ll value a volatility swap on Apple using October expiration call options using QuantLib.

Let’s go!

Demystifying volatility swaps (easily value one on Apple with Python)

Volatility swaps give traders a way to directly trade market volatility. Volatility swaps let investors to gain exposure to the volatility of an asset without actually owning the asset itself.

This is useful for managing risk during extreme market conditions.

The pricing of volatility swaps involves setting strike volatility based on implied volatility. Then, we calculate the payoff based on the difference between the realized and the strike.

In practice, volatility swaps provide a more precise hedging tool. This is especially true during periods of high market uncertainty. They offer better liquidity and reduced transaction costs compared to other hedging tools.

By using volatility swaps, you can hedge against market volatility and enhance your trading performance.

Let’s see how they work.

Imports and set up

Import the libraries we’ll use for the analysis and set up the basic parameters for the volatility swap.

1import QuantLib as ql
2import numpy as np
3import pandas as pd
4
5notional = 100_000
6volatility_strike = 0.2438
7days_to_maturity = 148
8observation_period = 252
9
10risk_free_rate = 0.0525
11dividend_yield = 0.0052
12spot_price = 188.64

The swap requires a notional amount, a strike price, number of days to maturity, and number of trading days in a year. We also define the market data, such as risk-free rate, dividend yield, and the initial price of the underlying asset.

Set up QuantLib for our swap

Create QuantLib objects for the interest rate curve, the dividend yield curve, and the underlying asset price.

1calendar = ql.NullCalendar()
2day_count = ql.Actual360()
3
4today = ql.Date().todaysDate()
5ql.Settings.instance().evaluationDate = today
6
7risk_free_ts = ql.YieldTermStructureHandle(
8    ql.FlatForward(today, risk_free_rate, day_count)
9)
10
11dividend_ts = ql.YieldTermStructureHandle(
12    ql.FlatForward(today, dividend_yield, day_count)
13)
14
15# Underlying asset price
16spot_handle = ql.QuoteHandle(ql.SimpleQuote(spot_price))

This code initializes a calendar and day count convention, sets today’s date, and creates yield term structures for the risk-free rate and dividend yield. It also establishes a handle for the underlying asset’s spot price.

Compute implied volatility and simulate realized volatility

In our example, we’ll assume a European style exercise. You can change this to use American style exercise if you’d like. We use this valuation model to compute the implied volatility.

1strike_price = 190
2option_price = 11.05
3expiration_date = today + ql.Period(days_to_maturity, ql.Days)
4
5payoff = ql.PlainVanillaPayoff(ql.Option.Call, strike_price)
6exercise = ql.EuropeanExercise(expiration_date)
7european_option = ql.VanillaOption(payoff, exercise)
8
9volatility_handle = ql.BlackVolTermStructureHandle(
10    ql.BlackConstantVol(today, calendar, volatility_strike, day_count)
11)
12
13bsm_process = ql.BlackScholesMertonProcess(
14    spot_handle, dividend_ts, risk_free_ts, volatility_handle
15)
16
17implied_volatility = european_option.impliedVolatility(
18    option_price, bsm_process, 1e-4, 1000, 1e-8, 4.0
19)

The code sets up a European call option for Apple with a strike price of $190 using the October 18th expiration. It constructs a Black-Scholes-Merton process with the given market data and volatility. Finally, it calculates the implied volatility from the market price of the option.

Now we need to simulate the underlying price paths to estimate future realized volatility. Use the geometric Brownian motion model to simulate the underlying asset’s price paths and calculate the realized volatility.

1np.random.seed(42)
2
3time_steps = observation_period
4dt = 1 / observation_period
5
6prices = np.zeros((time_steps + 1, 1))
7prices[0] = spot_price
8
9for t in range(1, time_steps + 1):
10    z = np.random.normal(size=1)
11    prices[t] = (
12        prices[t-1] 
13        * np.exp(
14            (risk_free_rate - 0.5 * implied_volatility**2) * 
15            dt + 
16            implied_volatility * 
17            np.sqrt(dt) * z
18        )
19    )
20
21prices_df = pd.DataFrame(prices, columns=['Price'])
22
23prices_df['Return'] = prices_df['Price'].pct_change().dropna()
24
25realized_volatility = np.std(prices_df['Return']) * np.sqrt(observation_period)

This code simulates the price paths of Apple using a geometric Brownian motion. It initializes prices and calculates each subsequent price based on the risk-free rate, implied volatility, and a random shock. Finally, it calculates daily returns from these simulated prices and computes the realized volatility over the observation period.

Calculate the value of the volatility swap

The value of the volatility swap is the difference between the realized volatility and the strike volatility, scaled by the notional amount and the time to maturity.

1time_to_maturity = days_to_maturity / observation_period
2
3volatility_swap_value = (
4    (realized_volatility - volatility_strike) * 
5    notional * 
6    np.sqrt(time_to_maturity)
7)
8
9print(f"Volatility Swap Value: ${volatility_swap_value:.2f}")

First we calculate the time to maturity by dividing the number of days to maturity by the number of trading days in a year. We then compute the value of the volatility swap by scaling the difference between realized volatility and the volatility strike by the notional amount and the square root of the time to maturity.

Man with glasses and a wristwatch, wearing a white shirt, looking thoughtfully at a laptop with a data screen in the background.