{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Options Pricing Toolkit\n",
        "\n",
        "Build a small options pricing lab with Black-Scholes, Greeks, implied volatility, and your own extensions.\n",
        "\n",
        "**How to use this notebook:** We give you the structure, the key ideas, starter code, and places where you need to make the important research decisions yourself."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## What you should have by the end\n",
        "\n",
        "- A working prototype\n",
        "- A few graphs/tables that explain what your model is doing\n",
        "- A short write-up of your assumptions\n",
        "- At least one extension that makes the project feel like yours"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Concept\n",
        "\n",
        "Options are useful because they force you to think about uncertainty, volatility, convexity, and risk.\n",
        "\n",
        "The goal here is not to copy-paste Black-Scholes and call it a project. The goal is to build a pricing toolkit where you can test assumptions, compare pricing methods, and understand why the outputs move."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Starter Code\n",
        "\n",
        "1. Implement Black-Scholes for calls and puts.\n",
        "2. Add Greeks.\n",
        "3. Add an implied volatility solver.\n",
        "4. Compare model prices to a small manually-entered option chain.\n",
        "5. Write down where the model is obviously wrong."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import math\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from scipy.stats import norm\n",
        "from scipy.optimize import brentq\n",
        "\n",
        "def black_scholes_price(S, K, T, r, sigma, option_type=\"call\"):\n",
        "    # Starter implementation. Extend this with dividends, rates, or alternative assumptions.\n",
        "    if T <= 0 or sigma <= 0:\n",
        "        raise ValueError(\"T and sigma must be positive.\")\n",
        "\n",
        "    d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))\n",
        "    d2 = d1 - sigma * math.sqrt(T)\n",
        "\n",
        "    if option_type == \"call\":\n",
        "        return S * norm.cdf(d1) - K * math.exp(-r * T) * norm.cdf(d2)\n",
        "    elif option_type == \"put\":\n",
        "        return K * math.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)\n",
        "    else:\n",
        "        raise ValueError(\"option_type must be 'call' or 'put'\")\n",
        "\n",
        "black_scholes_price(S=100, K=100, T=30/365, r=0.04, sigma=0.25, option_type=\"call\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "def implied_volatility(market_price, S, K, T, r, option_type=\"call\"):\n",
        "    # TODO:\n",
        "    # - Handle deep ITM/OTM options more carefully.\n",
        "    # - Add warnings when no valid implied vol exists.\n",
        "    # - Compare this with a Newton method.\n",
        "    def objective(sigma):\n",
        "        return black_scholes_price(S, K, T, r, sigma, option_type) - market_price\n",
        "\n",
        "    return brentq(objective, 1e-6, 5.0)\n",
        "\n",
        "# TODO: replace this toy option chain with your own small dataset.\n",
        "option_chain = pd.DataFrame({\n",
        "    \"strike\": [90, 95, 100, 105, 110],\n",
        "    \"market_call\": [11.8, 7.9, 4.7, 2.5, 1.2],\n",
        "    \"T\": [30/365] * 5\n",
        "})\n",
        "\n",
        "option_chain[\"implied_vol\"] = option_chain.apply(\n",
        "    lambda row: implied_volatility(row[\"market_call\"], 100, row[\"strike\"], row[\"T\"], 0.04, \"call\"),\n",
        "    axis=1\n",
        ")\n",
        "\n",
        "option_chain"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Open-ended extensions\n",
        "\n",
        "- Add dividends\n",
        "- Compare Black-Scholes vs binomial tree\n",
        "- Build an implied volatility smile chart\n",
        "- Estimate historical volatility and compare it to implied volatility\n",
        "- Add a small dashboard where changing volatility/rate/time changes the option price"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Final write-up prompts\n",
        "\n",
        "Answer these before putting the project on your resume:\n",
        "\n",
        "1. What problem were you trying to solve?\n",
        "2. What assumptions did your model make?\n",
        "3. Where did the model work well?\n",
        "4. Where did it fail?\n",
        "5. What would you improve with more time?\n",
        "6. What is one interesting thing you learned that was not obvious before starting?"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.x"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
