{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c1d5e21b",
   "metadata": {},
   "source": [
    "<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed31c33c",
   "metadata": {},
   "source": [
    "This code calculates and visualizes the theta of a European call option over time until expiration. It sets up the necessary financial instruments using QuantLib, including the option, interest rate curve, dividend yield curve, and volatility surface. The theta is calculated for each day leading up to expiration and stored in a list. Finally, the theta values are plotted against the days to expiration, providing insight into the option's time decay."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ce21918",
   "metadata": {},
   "outputs": [],
   "source": [
    "import QuantLib as ql\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "687a3623",
   "metadata": {},
   "source": [
    "Define option parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fbd7243",
   "metadata": {},
   "outputs": [],
   "source": [
    "expiry_date = ql.Date(15, 6, 2023)\n",
    "strike_price = 100\n",
    "spot_price = 105\n",
    "volatility = 0.2\n",
    "risk_free_rate = 0.01\n",
    "dividend_yield = 0.02"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49f2c17d",
   "metadata": {},
   "source": [
    "Set up the QuantLib calendar and day count convention"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "59c625a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "calendar = ql.UnitedStates(ql.UnitedStates.NYSE)\n",
    "day_count = ql.Actual365Fixed()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "462a8820",
   "metadata": {},
   "source": [
    "Create the QuantLib objects for the option"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "402532e0",
   "metadata": {},
   "outputs": [],
   "source": [
    "exercise = ql.EuropeanExercise(expiry_date)\n",
    "payoff = ql.PlainVanillaPayoff(ql.Option.Call, strike_price)\n",
    "option = ql.VanillaOption(payoff, exercise)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2af0a464",
   "metadata": {},
   "source": [
    "Create the interest rate curve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e1e92d44",
   "metadata": {},
   "outputs": [],
   "source": [
    "risk_free_rate_handle = ql.YieldTermStructureHandle(\n",
    "    ql.FlatForward(0, calendar, ql.QuoteHandle(ql.SimpleQuote(risk_free_rate)), day_count)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b90496fb",
   "metadata": {},
   "source": [
    "Create the dividend yield curve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60284e6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "dividend_yield_handle = ql.YieldTermStructureHandle(\n",
    "    ql.FlatForward(0, calendar, ql.QuoteHandle(ql.SimpleQuote(dividend_yield)), day_count)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ed9af74",
   "metadata": {},
   "source": [
    "Create the volatility surface"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f0b4be8",
   "metadata": {},
   "outputs": [],
   "source": [
    "volatility_handle = ql.BlackVolTermStructureHandle(\n",
    "    ql.BlackConstantVol(0, calendar, ql.QuoteHandle(ql.SimpleQuote(volatility)), day_count)\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c976b34a",
   "metadata": {},
   "source": [
    "Create the Black-Scholes process"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a6dd5ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "spot_handle = ql.QuoteHandle(ql.SimpleQuote(spot_price))\n",
    "bs_process = ql.BlackScholesMertonProcess(\n",
    "    spot_handle, \n",
    "    dividend_yield_handle, \n",
    "    risk_free_rate_handle, \n",
    "    volatility_handle\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a97ac96c",
   "metadata": {},
   "source": [
    "Define the range of days to expiration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "813206a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "days_to_expiry = range(365, 15, -1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0944d10",
   "metadata": {},
   "outputs": [],
   "source": [
    "theta_values = []"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fab6d8f2",
   "metadata": {},
   "source": [
    "Calculate theta for each day"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12acf9e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "for days in days_to_expiry:\n",
    "    expiry_date = calendar.advance(ql.Date().todaysDate(), ql.Period(int(days), ql.Days))\n",
    "    exercise = ql.EuropeanExercise(expiry_date)\n",
    "    option = ql.VanillaOption(payoff, exercise)\n",
    "    \n",
    "    # Set up the pricing engine\n",
    "    engine = ql.AnalyticEuropeanEngine(bs_process)\n",
    "    option.setPricingEngine(engine)\n",
    "    \n",
    "    # Calculate theta\n",
    "    theta = option.theta() / 365\n",
    "    theta_values.append(theta)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b61c5992",
   "metadata": {},
   "source": [
    "Plot the theta values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "edd4d555",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(figsize=(10, 6))\n",
    "plt.plot(days_to_expiry, theta_values, label='Theta')\n",
    "plt.xlabel('Days to Expiration')\n",
    "plt.ylabel('Theta')\n",
    "plt.title('Option Theta over Time to Expiration')\n",
    "plt.gca().invert_xaxis()\n",
    "ticks = range(365, 15, -50)\n",
    "plt.xticks(ticks)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec3156e1",
   "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"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
