Files
strategy-lab/to_explore/pyquantnews/97_VolatilitySwaps.ipynb
David Brazda e3da60c647 daily update
2024-10-21 20:57:56 +02:00

478 lines
11 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "fa226836",
"metadata": {},
"source": [
"<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
]
},
{
"cell_type": "markdown",
"id": "71f198b7",
"metadata": {},
"source": [
"This code calculates the implied and realized volatility of an asset, and evaluates the payoff of a volatility swap. It uses QuantLib to set up financial instruments and yield curves, and NumPy and Pandas for simulations and calculations. First, it calculates the implied volatility from the market price of a European option using the Black-Scholes model. Then, it simulates asset price paths to compute realized volatility. Finally, it calculates the value of a volatility swap based on these volatilities."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aebedeea",
"metadata": {},
"outputs": [],
"source": [
"import QuantLib as ql\n",
"import numpy as np\n",
"import pandas as pd"
]
},
{
"cell_type": "markdown",
"id": "afba19b8",
"metadata": {},
"source": [
"Define financial parameters such as notional amount, volatility strike, days to maturity, and observation period"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8babe8d0",
"metadata": {},
"outputs": [],
"source": [
"notional = 100_000 \n",
"volatility_strike = 0.2438\n",
"days_to_maturity = 148\n",
"observation_period = 252"
]
},
{
"cell_type": "markdown",
"id": "095d1289",
"metadata": {},
"source": [
"Define additional financial parameters such as risk-free rate, dividend yield, and current spot price"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0465a508",
"metadata": {},
"outputs": [],
"source": [
"risk_free_rate = 0.0525\n",
"dividend_yield = 0.0052\n",
"spot_price = 188.64"
]
},
{
"cell_type": "markdown",
"id": "5b2c2242",
"metadata": {},
"source": [
"Create calendar and day count convention objects for use in QuantLib calculations"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "184f9cea",
"metadata": {},
"outputs": [],
"source": [
"calendar = ql.NullCalendar()\n",
"day_count = ql.Actual360()"
]
},
{
"cell_type": "markdown",
"id": "c5ab97e8",
"metadata": {},
"source": [
"Set today's date and evaluation date in QuantLib"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9340a5d8",
"metadata": {},
"outputs": [],
"source": [
"today = ql.Date().todaysDate()\n",
"ql.Settings.instance().evaluationDate = today"
]
},
{
"cell_type": "markdown",
"id": "ed0a8506",
"metadata": {},
"source": [
"Create the risk-free rate term structure handle using a flat forward curve"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "176f2878",
"metadata": {},
"outputs": [],
"source": [
"risk_free_ts = ql.YieldTermStructureHandle(\n",
" ql.FlatForward(today, risk_free_rate, day_count)\n",
")"
]
},
{
"cell_type": "markdown",
"id": "5f2dfc50",
"metadata": {},
"source": [
"Create the dividend yield term structure handle using a flat forward curve"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ad960a14",
"metadata": {},
"outputs": [],
"source": [
"dividend_ts = ql.YieldTermStructureHandle(\n",
" ql.FlatForward(today, dividend_yield, day_count)\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e3f40a4f",
"metadata": {},
"source": [
"Create the spot price handle using a simple quote"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1e4912d",
"metadata": {},
"outputs": [],
"source": [
"spot_handle = ql.QuoteHandle(ql.SimpleQuote(spot_price))"
]
},
{
"cell_type": "markdown",
"id": "62fea0f2",
"metadata": {},
"source": [
"Define parameters for the European option including strike price, market price, and expiration date"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2f5df3be",
"metadata": {},
"outputs": [],
"source": [
"strike_price = 190\n",
"option_price = 11.05\n",
"expiration_date = today + ql.Period(days_to_maturity, ql.Days)"
]
},
{
"cell_type": "markdown",
"id": "19846fe1",
"metadata": {},
"source": [
"Set up the option with a plain vanilla payoff and European exercise"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74083fa4",
"metadata": {},
"outputs": [],
"source": [
"payoff = ql.PlainVanillaPayoff(ql.Option.Call, strike_price)\n",
"exercise = ql.EuropeanExercise(expiration_date)\n",
"european_option = ql.VanillaOption(payoff, exercise)"
]
},
{
"cell_type": "markdown",
"id": "9c2377f9",
"metadata": {},
"source": [
"Set up the volatility structure with an initial guess for implied volatility"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da3828fd",
"metadata": {},
"outputs": [],
"source": [
"volatility_handle = ql.BlackVolTermStructureHandle(\n",
" ql.BlackConstantVol(today, calendar, volatility_strike, day_count)\n",
")"
]
},
{
"cell_type": "markdown",
"id": "cfe48958",
"metadata": {},
"source": [
"Use the Black-Scholes-Merton process for option pricing"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d5cb1639",
"metadata": {},
"outputs": [],
"source": [
"bsm_process = ql.BlackScholesMertonProcess(\n",
" spot_handle, dividend_ts, risk_free_ts, volatility_handle\n",
")"
]
},
{
"cell_type": "markdown",
"id": "3e912452",
"metadata": {},
"source": [
"Calculate the implied volatility from the market price of the option"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7bf19f08",
"metadata": {},
"outputs": [],
"source": [
"implied_volatility = european_option.impliedVolatility(\n",
" option_price, bsm_process, 1e-4, 1000, 1e-8, 4.0\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "30681256",
"metadata": {},
"outputs": [],
"source": [
"print(f\"Implied Volatility: {implied_volatility:.4f}\")"
]
},
{
"cell_type": "markdown",
"id": "2f3ce2ad",
"metadata": {},
"source": [
"Set the random seed for reproducibility and define simulation parameters"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c65b7472",
"metadata": {},
"outputs": [],
"source": [
"np.random.seed(42)\n",
"time_steps = observation_period\n",
"dt = 1 / observation_period"
]
},
{
"cell_type": "markdown",
"id": "99f00915",
"metadata": {},
"source": [
"Initialize an array to store simulated asset prices and set the initial price"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c1d53659",
"metadata": {},
"outputs": [],
"source": [
"prices = np.zeros((time_steps + 1, 1))\n",
"prices[0] = spot_price"
]
},
{
"cell_type": "markdown",
"id": "47b026f4",
"metadata": {},
"source": [
"Simulate asset price paths using a geometric Brownian motion model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6a86ebdc",
"metadata": {},
"outputs": [],
"source": [
"for t in range(1, time_steps + 1):\n",
" z = np.random.normal(size=1)\n",
" prices[t] = (\n",
" prices[t-1] \n",
" * np.exp(\n",
" (risk_free_rate - 0.5 * implied_volatility**2) * \n",
" dt + \n",
" implied_volatility * \n",
" np.sqrt(dt) * z\n",
" )\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "0129e267",
"metadata": {},
"source": [
"Convert the simulated prices to a pandas DataFrame for further analysis"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a96eb0e0",
"metadata": {},
"outputs": [],
"source": [
"prices_df = pd.DataFrame(prices, columns=['Price'])"
]
},
{
"cell_type": "markdown",
"id": "fa7e51f8",
"metadata": {},
"source": [
"Calculate daily returns from the simulated prices"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "721ae250",
"metadata": {},
"outputs": [],
"source": [
"prices_df['Return'] = prices_df['Price'].pct_change().dropna()"
]
},
{
"cell_type": "markdown",
"id": "89a06767",
"metadata": {},
"source": [
"Calculate realized volatility from the daily returns"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "92d2be43",
"metadata": {},
"outputs": [],
"source": [
"realized_volatility = np.std(prices_df['Return']) * np.sqrt(observation_period)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8190344a",
"metadata": {},
"outputs": [],
"source": [
"print(f\"Realized Volatility: {realized_volatility:.4f}\")"
]
},
{
"cell_type": "markdown",
"id": "bccc2823",
"metadata": {},
"source": [
"Calculate the time to maturity in years"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "06aa9217",
"metadata": {},
"outputs": [],
"source": [
"time_to_maturity = days_to_maturity / observation_period"
]
},
{
"cell_type": "markdown",
"id": "c5dd73ce",
"metadata": {},
"source": [
"Calculate the payoff of the volatility swap based on realized and strike volatility"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "830e946c",
"metadata": {},
"outputs": [],
"source": [
"volatility_swap_value = (\n",
" (realized_volatility - volatility_strike) * \n",
" notional * \n",
" np.sqrt(time_to_maturity)\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "15e99f26",
"metadata": {},
"outputs": [],
"source": [
"print(f\"Volatility Swap Value: ${volatility_swap_value:.2f}\")"
]
},
{
"cell_type": "markdown",
"id": "2ad683ab",
"metadata": {},
"source": [
"<a href=\"https://pyquantnews.com/\">PyQuant News</a> is where finance practitioners level up with Python for quant finance, algorithmic trading, and market data analysis. Looking to get started? Check out the fastest growing, top-selling course to <a href=\"https://gettingstartedwithpythonforquantfinance.com/\">get started with Python for quant finance</a>. For educational purposes. Not investment advise. Use at your own risk."
]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all",
"main_language": "python",
"notebook_metadata_filter": "-all"
}
},
"nbformat": 4,
"nbformat_minor": 5
}