{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "42505bbc",
   "metadata": {},
   "source": [
    "<div style=\"background-color:#000;\"><img src=\"pqn.png\"></img></div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "880f1027",
   "metadata": {},
   "source": [
    "This code calculates the theta (time decay) of an American call option on Apple Inc. (AAPL) stock using QuantLib and OpenBB libraries. It fetches option chains and historical prices, determines the closest strike price, and computes volatility. The code then sets up the market data, constructs a Black-Scholes-Merton process, and uses a binomial pricing engine to price the option. Finally, it computes and returns the theta of the call option, which measures the sensitivity of the option's price to the passage of time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7d1894f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import QuantLib as ql\n",
    "from openbb import obb\n",
    "import warnings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d7816ee",
   "metadata": {},
   "outputs": [],
   "source": [
    "warnings.filterwarnings(\"ignore\")\n",
    "obb.user.preferences.output_type = \"dataframe\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb909667",
   "metadata": {},
   "source": [
    "Define the stock symbol and fetch option chains and historical prices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f10f430b",
   "metadata": {},
   "outputs": [],
   "source": [
    "symbol = \"AAPL\"\n",
    "chains = obb.derivatives.options.chains(symbol=symbol)\n",
    "prices = obb.equity.price.historical(symbol=symbol, provider=\"yfinance\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c8fec35",
   "metadata": {},
   "source": [
    "Select an expiration date and fetch associated strike prices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dd6d5806",
   "metadata": {},
   "outputs": [],
   "source": [
    "expiration = chains.expiration.unique()[5]\n",
    "strikes = chains.query(\"`expiration` == @expiration\").strike.to_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3647823f",
   "metadata": {},
   "source": [
    "Determine the underlying stock price and identify the closest strike price"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "083edc0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "underlying_price = prices.close.iat[-1]\n",
    "strike_price = (\n",
    "    strikes\n",
    "    .loc[\n",
    "        (strikes-underlying_price)\n",
    "        .abs()\n",
    "        .sort_values(\"strike\")\n",
    "        .index[0]\n",
    "    ].strike\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0df0957c",
   "metadata": {},
   "source": [
    "Calculate volatility, maturity, dividend yield, and risk-free rate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c2877f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "volatility = prices.close.pct_change().std() * np.sqrt(252)\n",
    "maturity = ql.Date(\n",
    "    expiration.day,\n",
    "    expiration.month,\n",
    "    expiration.year,\n",
    ")\n",
    "dividend_yield = 0.0056\n",
    "risk_free_rate = 0.05\n",
    "calculation_date = ql.Date.todaysDate()\n",
    "ql.Settings.instance().evaluationDate = calculation_date"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "450e1ab7",
   "metadata": {},
   "source": [
    "Set up the market data including spot price, yield term structure, dividend yield, and volatility"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e239f26",
   "metadata": {},
   "outputs": [],
   "source": [
    "spot_handle = ql.QuoteHandle(\n",
    "    ql.SimpleQuote(underlying_price)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc227df6",
   "metadata": {},
   "outputs": [],
   "source": [
    "yield_handle = ql.YieldTermStructureHandle(\n",
    "    ql.FlatForward(\n",
    "        calculation_date, \n",
    "        risk_free_rate, \n",
    "        ql.Actual365Fixed()\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a0ec3ad",
   "metadata": {},
   "outputs": [],
   "source": [
    "dividend_handle = ql.YieldTermStructureHandle(\n",
    "    ql.FlatForward(\n",
    "        calculation_date, \n",
    "        dividend_yield, \n",
    "        ql.Actual365Fixed()\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5a2786d",
   "metadata": {},
   "outputs": [],
   "source": [
    "volatility_handle = ql.BlackVolTermStructureHandle(\n",
    "    ql.BlackConstantVol(\n",
    "        calculation_date, \n",
    "        ql.NullCalendar(), \n",
    "        volatility, \n",
    "        ql.Actual365Fixed()\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3e37c12",
   "metadata": {},
   "source": [
    "Construct a Black-Scholes-Merton process for option pricing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "793fa60b",
   "metadata": {},
   "outputs": [],
   "source": [
    "bs_process = ql.BlackScholesMertonProcess(\n",
    "    spot_handle, \n",
    "    dividend_handle, \n",
    "    yield_handle, \n",
    "    volatility_handle\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f33a6256",
   "metadata": {},
   "source": [
    "Set up the pricing engine using a binomial tree model with 1000 steps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba4f9584",
   "metadata": {},
   "outputs": [],
   "source": [
    "engine = ql.BinomialVanillaEngine(bs_process, \"crr\", steps=1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdf6482d",
   "metadata": {},
   "source": [
    "Define the American call option with the calculated strike price and maturity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70cd1554",
   "metadata": {},
   "outputs": [],
   "source": [
    "exercise = ql.AmericanExercise(calculation_date, maturity) \n",
    "call_option = ql.VanillaOption(\n",
    "    ql.PlainVanillaPayoff(ql.Option.Call, strike_price),\n",
    "    exercise\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f383f8e7",
   "metadata": {},
   "source": [
    "Assign the pricing engine to the call option"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "321c90a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "call_option.setPricingEngine(engine)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff91901c",
   "metadata": {},
   "source": [
    "Calculate and return the theta of the call option, normalized by the number of days in a year"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7770ed99",
   "metadata": {},
   "outputs": [],
   "source": [
    "call_theta = call_option.theta() / 365\n",
    "call_theta"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb0a8d41",
   "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
}
