{ "cells": [ { "cell_type": "markdown", "id": "210b03fe66534e08a2480f88d801bb69", "metadata": {}, "source": [ "# Greeks demo (European options on futures, Black-76)\n", "\n", "We start from a single **call** option instance and, for each Greek,\n", "1) explain what it means, 2) compute it analytically using `volkit.future`,\n", "3) approximate it numerically using a small **central difference** bump, and\n", "4) print the values and the approximation error.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "dd31db91225549ce9fa5ea7897e314f0", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from volkit import (\n", " price_euro_future,\n", " delta_euro_future,\n", " gamma_euro_future,\n", " vega_euro_future,\n", " theta_euro_future,\n", " rho_euro_future,\n", " dual_delta_euro_future,\n", " vanna_euro_future,\n", " vomma_euro_future,\n", " lambda_euro_future,\n", ")\n", "\n", "np.set_printoptions(precision=10, suppress=True)\n", "\n", "# Base parameters (edit me)\n", "F = 100.0\n", "K = 100.0\n", "T = 0.5 # years\n", "r = 0.02 # cc rate\n", "sigma = 0.20\n", "cp = 'call'" ] }, { "cell_type": "markdown", "id": "6998482eb64d445d80bb811d011a5db5", "metadata": {}, "source": [ "## Numerical greeks\n", "\n", "Below, we compare the analytical greeks from this library with numerical approximation. The goal is to\n", "\n", "* show alignment between the two methods\n", "* illustrate the definitions of various greeks\n", "\n", "We use the following numerical approximation schemes:\n", "\n", "**Central difference for first derivative**\n", "\n", "$$\n", "f'(x) \\approx \\dfrac{f(x+h)-f(x-h)}{2h}\n", "$$\n", "\n", "**Central difference for second derivative**\n", "\n", "$$\n", "f''(x) \\approx \\dfrac{f(x+h)-2f(x)+f(x-h)}{h^2}\n", "$$\n", "\n", "**Mixed partial derivatives (vanna)**\n", "\n", "$$\n", "\\partial^2 f/\\partial x\\,\\partial y \\approx \\dfrac{f(x+h,y+k)-f(x+h,y-k)-f(x-h,y+k)+f(x-h,y-k)}{4hk}\n", "$$\n" ] }, { "cell_type": "markdown", "id": "6c99daed8a5c4d3cac53f1e3250c040a", "metadata": {}, "source": [ "## Baseline price\n", "This is the present value (discounted) of the option under Black-76." ] }, { "cell_type": "code", "execution_count": 6, "id": "ae9967ca4c394247892ff0be93f2e68c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Price: 5.581106724604814\n" ] } ], "source": [ "P0 = price_euro_future(F, K, T, r, sigma, cp)\n", "print('Price:', P0)" ] }, { "cell_type": "markdown", "id": "b552533c13794f9691a27ffff76b4fb0", "metadata": {}, "source": [ "## Delta: sensitivity to futures price $\\partial V / \\partial F$\n", "How much the option price $V$ moves per 1-unit change in the **futures** price $F$." ] }, { "cell_type": "code", "execution_count": 7, "id": "adc397e3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Delta analytic : 0.5229304504976081\n", "Delta numerical: 0.5229304435331983\n", "abs err: 6.964409826615281e-09\n" ] } ], "source": [ "Delta_ana = delta_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hF = 1e-4 * F\n", "\n", "Pp = price_euro_future(F + hF, K, T, r, sigma, cp)\n", "Pm = price_euro_future(F - hF, K, T, r, sigma, cp)\n", "Delta_num = (Pp - Pm) / (2 * hF)\n", "\n", "print('Delta analytic :', Delta_ana)\n", "print('Delta numerical:', Delta_num)\n", "print('abs err:', abs(Delta_ana - Delta_num))" ] }, { "cell_type": "markdown", "id": "53c729f4", "metadata": {}, "source": [ "## Gamma: curvature w.r.t. future price\n", "\n", "**Gamma** measures how much *delta* changes when the future price $F$ moves by a small amount $dF$:\n", "$$\n", "\\Gamma \\,=\\, \\frac{\\partial^2 V}{\\partial F^2}.\n", "$$\n", "Below we compute Gamma **analytically** using `gamma_euro_future`, and **numerically** by bumping $F$ in `price_euro_future` with a central difference in $F$.\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "dbd65cd7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Gamma analytic : 0.027859055399082604\n", "Gamma numerical: 0.027859054269892318\n", "abs err: 1.1291902864141168e-09\n" ] } ], "source": [ "Gamma_ana = gamma_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hF = np.maximum(1e-4 * (np.abs(F) + 1.0), 1e-6)\n", "\n", "P0 = price_euro_future(F, K, T, r, sigma, cp)\n", "Pp = price_euro_future(F + hF, K, T, r, sigma, cp)\n", "Pm = price_euro_future(F - hF, K, T, r, sigma, cp)\n", "Gamma_num = (Pp - 2.0*P0 + Pm) / (hF**2)\n", "\n", "print('Gamma analytic :', Gamma_ana)\n", "print('Gamma numerical:', Gamma_num)\n", "print('abs err:', abs(Gamma_ana - Gamma_num))" ] }, { "cell_type": "markdown", "id": "90648ca1", "metadata": {}, "source": [ "## Vega: sensitivity to volatility\n", "\n", "**Vega** measures how much the option price changes for a small change $d\\sigma$ in volatility:\n", "$$\n", "\\text{Vega} \\,=\\, \\frac{\\partial V}{\\partial \\sigma}.\n", "$$\n", "We compare `vega_euro_future` with a central difference bump in $\\sigma$ using `price_euro_future`.\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "be85140b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vega analytic : 27.85905539908261\n", "Vega numerical: 27.85905539074799\n", "abs err: 8.33462010518815e-09\n" ] } ], "source": [ "Vega_ana = vega_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hs = np.maximum(1e-4 * (np.abs(sigma) + 1.0), 1e-6)\n", "Pp = price_euro_future(F, K, T, r, sigma + hs, cp)\n", "Pm = price_euro_future(F, K, T, r, sigma - hs, cp)\n", "Vega_num = (Pp - Pm) / (2.0 * hs)\n", "\n", "print('Vega analytic :', Vega_ana)\n", "print('Vega numerical:', Vega_num)\n", "print('abs err:', abs(Vega_ana - Vega_num))" ] }, { "cell_type": "markdown", "id": "19653267", "metadata": {}, "source": [ "## Theta: sensitivity to the passage of time\n", "\n", "**Theta** is the rate at which the option price changes with a small change $dT$ in time-to-expiry $T$:\n", "$$\n", "\\Theta \\,=\\, \\frac{\\partial V}{\\partial T}.\n", "$$\n", "We compare `theta_euro_future` with a central difference bump in $T$ using `price_euro_future` (ensuring $T>0$).\n" ] }, { "cell_type": "code", "execution_count": 10, "id": "55ed52f2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.0001 5.581106724604814 5.5805606766058755 5.581652714400655\n", "Theta analytic : -5.4601889453244254\n", "Theta numerical: -5.460188973898106\n", "abs err: 2.8573680310728378e-08\n" ] } ], "source": [ "Theta_ana = theta_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hT = 1e-4\n", "Pp = price_euro_future(F, K, T+hT, r, sigma, cp)\n", "Pm = price_euro_future(F, K, T-hT, r, sigma, cp)\n", "Theta_num = -(Pp - Pm) / (2.0 * hT)\n", "\n", "print(hT, P0, Pm, Pp)\n", "print('Theta analytic :', Theta_ana)\n", "print('Theta numerical:', Theta_num)\n", "print('abs err:', abs(Theta_ana - Theta_num))" ] }, { "cell_type": "markdown", "id": "27aa206d", "metadata": {}, "source": [ "## Rho: sensitivity to the interest rate\n", "\n", "**Rho** measures how much the option price changes for a small change $dr$ in the interest rate $r$:\n", "$$\n", "\\rho \\,=\\, \\frac{\\partial V}{\\partial r}.\n", "$$\n", "We compare `rho_euro_future` with a central difference bump in $r$ using `price_euro_future`.\n" ] }, { "cell_type": "code", "execution_count": 11, "id": "fcdfe6b4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Rho analytic : -2.790553362302407\n", "Rho numerical: -2.79055336260825\n", "abs err: 3.058433506453184e-10\n" ] } ], "source": [ "Rho_ana = rho_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hr = 1e-6\n", "Pp = price_euro_future(F, K, T, r + hr, sigma, cp)\n", "Pm = price_euro_future(F, K, T, r - hr, sigma, cp)\n", "Rho_num = (Pp - Pm) / (2.0 * hr)\n", "\n", "print('Rho analytic :', Rho_ana)\n", "print('Rho numerical:', Rho_num)\n", "print('abs err:', abs(Rho_ana - Rho_num))" ] }, { "cell_type": "markdown", "id": "eb450900", "metadata": {}, "source": [ "## Dual Delta: sensitivity to the strike\n", "\n", "**Dual delta** measures how much the option price changes for a small change $dK$ in the strike $K$:\n", "$$\n", "\\text{Dual-}\\Delta \\,=\\, \\frac{\\partial V}{\\partial K}.\n", "$$\n", "We compare `dual_delta_euro_future` with a central difference bump in $K$ using `price_euro_future`.\n" ] }, { "cell_type": "code", "execution_count": 12, "id": "818214d3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DualΔ analytic : -0.46711938325156\n", "DualΔ numerical: -0.4671193903562499\n", "abs err: 7.104689891956895e-09\n" ] } ], "source": [ "Dual_ana = dual_delta_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hK = 1e-4 * (np.abs(K) + 1.0)\n", "Pp = price_euro_future(F, K + hK, T, r, sigma, cp)\n", "Pm = price_euro_future(F, K - hK, T, r, sigma, cp)\n", "Dual_num = (Pp - Pm) / (2.0 * hK)\n", "\n", "print('DualΔ analytic :', Dual_ana)\n", "print('DualΔ numerical:', Dual_num)\n", "print('abs err:', abs(Dual_ana - Dual_num))" ] }, { "cell_type": "markdown", "id": "e04f3c79", "metadata": {}, "source": [ "## Vanna: mixed sensitivity to $F$ and $\\sigma$\n", "\n", "**Vanna** is the mixed partial derivative w.r.t. the future price $F$ and volatility $\\sigma$:\n", "$$\n", "\\text{Vanna} \\,=\\, \\frac{\\partial^2 V}{\\partial F\\,\\partial \\sigma}.\n", "$$\n", "We compare `vanna_euro_future` with a **central mixed difference**: bump $F$ by $dF$ and $\\sigma$ by $d\\sigma$.\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "37c7dfe3", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vanna analytic : 0.13929527699541305\n", "Vanna numerical: 0.1392953134576165\n", "abs err: 3.646220345099316e-08\n" ] } ], "source": [ "Vanna_ana = vanna_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hF = np.maximum(1e-4 * (np.abs(F) + 1.0), 1e-6)\n", "hs = np.maximum(1e-4 * (np.abs(sigma) + 1.0), 1e-6)\n", "\n", "Ppp = price_euro_future(F + hF, K, T, r, sigma + hs, cp)\n", "Ppm = price_euro_future(F + hF, K, T, r, sigma - hs, cp)\n", "Pmp = price_euro_future(F - hF, K, T, r, sigma + hs, cp)\n", "Pmm = price_euro_future(F - hF, K, T, r, sigma - hs, cp)\n", "\n", "Vanna_num = (Ppp - Ppm - Pmp + Pmm) / (4.0 * hF * hs)\n", "\n", "print('Vanna analytic :', Vanna_ana)\n", "print('Vanna numerical:', Vanna_num)\n", "print('abs err:', abs(Vanna_ana - Vanna_num))" ] }, { "cell_type": "markdown", "id": "26e84aeb", "metadata": {}, "source": [ "## Vomma (Volga): curvature w.r.t. volatility\n", "\n", "**Vomma** (also called **Volga**) is the second derivative of price with respect to volatility $\\sigma$:\n", "$$\n", "\\text{Vomma} \\,=\\, \\frac{\\partial^2 V}{\\partial \\sigma^2}.\n", "$$\n", "We compare `vomma_euro_future` with a central second difference in $\\sigma$ using `price_euro_future`.\n" ] }, { "cell_type": "code", "execution_count": 14, "id": "2ab48f54", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vomma analytic : -0.6964763849770655\n", "Vomma numerical: -0.696476580112441\n", "abs err: 1.9513537541371306e-07\n" ] } ], "source": [ "Vomma_ana = vomma_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hs = np.maximum(1e-4 * (np.abs(sigma) + 1.0), 1e-6)\n", "P0 = price_euro_future(F, K, T, r, sigma, cp)\n", "Pp = price_euro_future(F, K, T, r, sigma + hs, cp)\n", "Pm = price_euro_future(F, K, T, r, sigma - hs, cp)\n", "Vomma_num = (Pp - 2.0 * P0 + Pm) / (hs**2)\n", "\n", "print('Vomma analytic :', Vomma_ana)\n", "print('Vomma numerical:', Vomma_num)\n", "print('abs err:', abs(Vomma_ana - Vomma_num))" ] }, { "cell_type": "markdown", "id": "a2e2e858", "metadata": {}, "source": [ "## Lambda (elasticity): percentage sensitivity to $F$\n", "\n", "**Lambda** (elasticity) scales delta by the price-to-future ratio:\n", "$$\n", "\\Lambda \\,=\\, \\frac{F}{V}\\,\\frac{\\partial V}{\\partial F}\n", "\\;=\\; \\frac{F}{V}\\,\\Delta.\n", "$$\n", "We compare `lambda_euro_future` with a numerical version built from a central $dF$ delta and the price.\n" ] }, { "cell_type": "code", "execution_count": 15, "id": "39185999", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Lambda analytic : 9.369655093535874\n", "Lambda numerical: 9.369654966233442\n", "abs err: 1.2730243170722133e-07\n" ] } ], "source": [ "Lambda_ana = lambda_euro_future(F, K, T, r, sigma, cp)\n", "\n", "hF = np.maximum(1e-4 * (np.abs(F) + 1.0), 1e-6)\n", "P0 = price_euro_future(F, K, T, r, sigma, cp)\n", "Pp = price_euro_future(F + hF, K, T, r, sigma, cp)\n", "Pm = price_euro_future(F - hF, K, T, r, sigma, cp)\n", "Delta_num = (Pp - Pm) / (2.0 * hF)\n", "\n", "Lambda_num = np.where(P0 != 0.0, (F * Delta_num) / P0, np.nan)\n", "\n", "print('Lambda analytic :', Lambda_ana)\n", "print('Lambda numerical:', Lambda_num)\n", "print('abs err:', abs(Lambda_ana - Lambda_num))" ] } ], "metadata": { "kernelspec": { "display_name": "volkit-eeDo8oXc-py3.11", "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.11.8" } }, "nbformat": 4, "nbformat_minor": 5 }