├── GBS - 2025.ipynb ├── GBS.ipynb ├── LICENSE └── README.md /GBS.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "# File Contains: Python code containing closed-form solutions for the valuation of European Options,\n", 12 | "# American Options, Asian Options, Spread Options, Heat Rate Options, and Implied Volatility\n", 13 | "#\n", 14 | "# This document demonstrates a Python implementation of some option models described in books written by Davis\n", 15 | "# Edwards: \"Energy Trading and Investing\", \"Risk Management in Trading\", \"Energy Investing Demystified\".\n", 16 | "#\n", 17 | "# for backward compatability with Python 2.7\n", 18 | "from __future__ import division\n", 19 | "\n", 20 | "# import necessary libaries\n", 21 | "import unittest\n", 22 | "import math\n", 23 | "import numpy as np\n", 24 | "from scipy.stats import norm\n", 25 | "from scipy.stats import mvn\n", 26 | "\n", 27 | "# Developer can toggle _DEBUG to True for more messages\n", 28 | "# normally this is set to False\n", 29 | "_DEBUG = False" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "MIT License\n", 37 | "\n", 38 | "Copyright (c) 2017 Davis William Edwards\n", 39 | "\n", 40 | "Permission is hereby granted, free of charge, to any person obtaining a copy\n", 41 | "of this software and associated documentation files (the \"Software\"), to deal\n", 42 | "in the Software without restriction, including without limitation the rights\n", 43 | "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n", 44 | "copies of the Software, and to permit persons to whom the Software is\n", 45 | "furnished to do so, subject to the following conditions:\n", 46 | "\n", 47 | "The above copyright notice and this permission notice shall be included in all\n", 48 | "copies or substantial portions of the Software.\n", 49 | "\n", 50 | "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", 51 | "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", 52 | "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n", 53 | "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", 54 | "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n", 55 | "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n", 56 | "SOFTWARE.\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "# Closed Form Option Pricing Formulas\n", 64 | "## Generalized Black Scholes (GBS) and similar models" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "** ChangeLog: **\n", 72 | "\n", 73 | "* 1/1/2017 Davis Edwards, Created GBS and Asian option formulas\n", 74 | "* 3/2/2017 Davis Edwards, added TeX formulas to describe calculations\n", 75 | "* 4/9/2017 Davis Edwards, added spread option (Kirk's approximation)\n", 76 | "* 5/10/2017 Davis Edwards, added graphics for sensitivity analysis\n", 77 | "* 5/18/2017 Davis Edwards, added Bjerksund-Stensland (2002) approximation for American Options\n", 78 | "* 5/19/2017 Davis Edwards, added implied volatility calculations\n", 79 | "* 6/7/2017 Davis Edwards, expanded sensitivity tests for American option approximation.\n", 80 | "* 6/21/2017 Davis Edwards, added documentation for Bjerksund-Stensland models\n", 81 | "* 7/21/2017 Davis Edwards, refactored all of the functions to match the parameter order to Haug's \"The Complete Guide to Option Pricing Formulas\".\n", 82 | "\n", 83 | "** TO DO List **\n", 84 | "1. Since the Asian Option valuation uses an approximation, need to determine the range of acceptable stresses that can be applied to the volatility input\n", 85 | "2. Sub-class the custom assertions in this module to work with \"unittest\"\n", 86 | "3. Update the greek calculations for American Options - currently the Greeks are approximated by the greeks from GBS model.\n", 87 | "4. Add a bibliography referencing the academic papers used as sources\n", 88 | "5. Finish writing documentation formulas for Close-Form approximation for American Options\n", 89 | "6. Refactor the order of parameters for the function calls to replicate the order of parameters in academic literature" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "-------------------------\n", 97 | "## Purpose:\n", 98 | "\n", 99 | "(Why these models exist)\n", 100 | "\n", 101 | "The software in this model is intended to price particular types of financial products called \"options\". These are a common type of financial product and fall into the category of \"financial derivative\". This documentation assumes that the reader is already familiar with options terminology. The models are largely variations of the Black Scholes Merton option framework (collectively called \"Black Scholes Genre\" or \"Generalized Black Scholes\") that are used to price European options (options that can only be exercised at one point in time). This library also includes approximations to value American options (options that can be exercised prior to the expiration date) and implied volatility calculators. \n", 102 | "\n", 103 | "Pricing Formulas\n", 104 | " 1. BlackScholes() Stock Options (no dividend yield)\n", 105 | " 2. Merton() Assets with continuous dividend yield (Index Options)\n", 106 | " 3. Black76() Commodity Options\n", 107 | " 4. GK() FX Options (Garman-Kohlhagen)\n", 108 | " 5. Asian76() Asian Options on Commodities\n", 109 | " 6. Kirks76() Spread Options (Kirk's Approximation)\n", 110 | " 7. American() American options\n", 111 | " 8. American76() American Commodity Options\n", 112 | "\n", 113 | "Implied Volatility Formulas\n", 114 | " 9. EuroImpliedVol Implied volatility calculator for European options\n", 115 | " 10. EuroImpliedVol76 Implied volatiltity calculator for European commodity options\n", 116 | " 11. AmerImpliedVol Implied volatiltity calculator for American options\n", 117 | " 11. AmerImpliedVol76 Implied volatility calculator for American commodity options\n", 118 | "\n", 119 | "Note:\n", 120 | "In honor of the Black76 model, the 76() on the end of functions indicates a commodity option.\n" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "-------------------------\n", 128 | "## Scope\n", 129 | "\n", 130 | "(Where this model is to be used):\n", 131 | "\n", 132 | "This model is built to price financial option contracts on a wide variety of financial commodities. These options are widely used and represent the benchmark to which other (more complicated) models are compared. While those more complicated models may outperform these models in specific areas, outperformance is relatively uncommon. By an large, these models have taken on all challengers and remain the de-facto industry standard. " 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "## Theory:\n", 140 | "\n", 141 | "### Generalized Black Scholes\n", 142 | "Black Scholes genre option models widely used to value European options. The original “Black Scholes” model was published in 1973 for non-dividend paying stocks. This created a revolution in quantitative finance and opened up option trading to the general population. Since that time, a wide variety of extensions to the original Black Scholes model have been created. Collectively, these are referred to as \"Black Scholes genre” option models. Modifications of the formula are used to price other financial instruments like dividend paying stocks, commodity futures, and FX forwards. Mathematically, these formulas are nearly identical. The primary difference between these models is whether the asset has a carrying cost (if the asset has a cost or benefit associated with holding it) and how the asset gets present valued. To illustrate this relationship, a “generalized” form of the Black Scholes equation is shown below.\n", 143 | "\n", 144 | "The Black Scholes model is based on number of assumptions about how financial markets operate. Black Scholes style models assume:\n", 145 | "\n", 146 | "1.\t**Arbitrage Free Markets**. Black Scholes formulas assume that traders try to maximize their personal profits and don’t allow arbitrage opportunities (riskless opportunities to make a profit) to persist. \n", 147 | "2.\t**Frictionless, Continuous Markets**. This assumption of frictionless markets assumes that it is possible to buy and sell any amount of the underlying at any time without transaction costs.\n", 148 | "3.\t**Risk Free Rates**. It is possible to borrow and lend money at a risk-free interest rate\n", 149 | "4.\t**Log-normally Distributed Price Movements**. Prices are log-normally distributed and described by Geometric Brownian Motion\n", 150 | "5.\t**Constant Volatility**. The Black Scholes genre options formulas assume that volatility is constant across the life of the option contract. \n", 151 | "\n", 152 | "In practice, these assumptions are not particularly limiting. The primary limitation imposed by these models is that it is possible to (reasonably) describe the dispersion of prices at some point in the future in a mathematical equation. \n", 153 | "\n", 154 | "In the traditional Black Scholes model intended to price stock options, the underlying assumption is that the stock is traded at its present value and that prices will follow a random walk diffusion style process over time. Prices are assumed to start at the spot price and, on the average, to drift upwards over time at the risk free rate. The “Merton” formula modifies the basic Black Scholes equation by introducing an additional term to incorporate dividends or holding costs. The Black 76 formula modifies the assumption so that the underlying starts at some forward price rather than a spot price. A fourth variation, the Garman Kohlhagen model, is used to value foreign exchange (FX) options. In the GK model, each currency in the currency pair is discounted based on its own interest rate.\n", 155 | " \n", 156 | "1.\t**Black Scholes (Stocks)**. In the traditional Black Scholes model, the option is based on common stock - an instrument that is traded at its present value. The stock price does not get present valued – it starts at its present value (a ‘spot price’) and drifts upwards over time at the risk free rate. \n", 157 | "2.\t**Merton (Stocks with continuous dividend yield)**. The Merton model is a variation of the Black Scholes model for assets that pay dividends to shareholders. Dividends reduce the value of the option because the option owner does not own the right to dividends until the option is exercised. \n", 158 | "3.\t**Black 76 (Commodity Futures)**. The Black 76 model is for an option where the underlying commodity is traded based on a future price rather than a spot price. Instead of dealing with a spot price that drifts upwards at the risk free rate, this model deals with a forward price that needs to be present valued. \n", 159 | "4.\t**Garman-Kohlhagen (FX Futures)**. The Garman Kohlhagen model is used to value foreign exchange (FX) options. In the GK model, each currency in the currency pair is discounted based on its own interest rate.\n", 160 | "\n", 161 | "An important concept of Black Scholes models is that the actual way that the underlying asset drifts over time isn't important to the valuation. Since European options can only be exercised when the contract expires, it is only the distribution of possible prices on that date that matters - the path that the underlying took to that point doesn't affect the value of the option. This is why the primary limitation of the model is being able to describe the dispersion of prices at some point in the future, not that the dispersion process is simplistic.\n", 162 | "\n", 163 | "The generalized Black-Scholes formula can found below (see *Figure 1 – Generalized Black Scholes Formula*). While these formulas may look complicated at first glance, most of the terms can be found as part of an options contract or are prices readily available in the market. The only term that is difficult to calculate is the implied volatility (σ). Implied volatility is typically calculated using prices of other options that have recently been traded.\n", 164 | "\n", 165 | ">*Call Price*\n", 166 | ">\\begin{equation}\n", 167 | "C = Fe^{(b-r)T} N(D_1) - Xe^{-rT} N(D_2)\n", 168 | "\\end{equation}\n", 169 | "\n", 170 | ">*Put Price*\n", 171 | ">\\begin{equation}\n", 172 | "P = Xe^{-rT} N(-D_2) - Fe^{(b-r)T} N(-D_1)\n", 173 | "\\end{equation}\n", 174 | "\n", 175 | ">*with the following intermediate calculations*\n", 176 | "\n", 177 | ">\\begin{equation}\n", 178 | "D_1 = \\frac{ln\\frac{F}{X} + (b+\\frac{V^2}{2})T}{V*\\sqrt{T}}\n", 179 | "\\end{equation}\n", 180 | "\n", 181 | ">\\begin{equation}\n", 182 | "D_2 = D_1 - V\\sqrt{T}\n", 183 | "\\end{equation}\n", 184 | "\n", 185 | ">*and the following inputs*\n", 186 | "\n", 187 | ">| Symbol | Meaning |\n", 188 | ">|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n", 189 | ">| F or S | **Underlying Price**. The price of the underlying asset on the valuation date. S is used commonly used to represent a spot price, F a forward price |\n", 190 | ">| X | **Strike Price**. The strike, or exercise, price of the option. |\n", 191 | ">| T | **Time to expiration**. The time to expiration in years. This can be calculated by comparing the time between the expiration date and the valuation date. T = (t_1 - t_0)/365 |\n", 192 | ">| t_0 | **Valuation Date**. The date on which the option is being valued. For example, it might be today’s date if the option we being valued today. |\n", 193 | ">| t_1 | **Expiration Date**. The date on which the option must be exercised. |\n", 194 | ">| V | **Volatility**. The volatility of the underlying security. This factor usually cannot be directly observed in the market. It is most often calculated by looking at the prices for recent option transactions and back-solving a Black Scholes style equation to find the volatility that would result in the observed price. This is commonly abbreviated with the greek letter sigma,σ, although V is used here for consistency with the code below. |\n", 195 | ">| q | **Continuous Yield**. Used in the Merton model, this is the continuous yield of the underlying security. Option holders are typically not paid dividends or other payments until they exercise the option. As a result, this factor decreases the value of an option. |\n", 196 | ">| r | **Risk Free Rate**. This is expected return on a risk-free investment. This is commonly a approximated by the yield on a low-risk government bond or the rate that large banks borrow between themselves (LIBOR). The rate depends on tenor of the cash flow. For example, a 10-year risk-free bond is likely to have a different rate than a 20-year risk-free bond.[DE1] |\n", 197 | ">| rf | **Foreign Risk Free Rate**. Used in the Garman Kohlhagen model, this is the risk free rate of the foreign currency. Each currency will have a risk free rate. |\n", 198 | ">*Figure 1 - Generalized Black Scholes Formula*\n", 199 | "\n", 200 | "The correction term, b, varies by formula – it differentiates the various Black Scholes formula from one another (see *Figure 2 - GBS Cost of Carry Adjustments*). The cost of carry refers to the cost of “carrying” or holding a position. For example, holding a bond may result in earnings from interest, holding a stock may result in stock dividends, or the like. Those payments are made to the owner of the underlying asset and not the owner of the option. As a result, they reduce the value of the option.\n", 201 | "\n", 202 | ">| | Model | Cost of Carry (b) |\n", 203 | ">|----|------------------|-------------------|\n", 204 | ">| 1. | BlackScholes | b = r |\n", 205 | ">| 2. | Merton | b = r - q |\n", 206 | ">| 3. | Black 1976 | b = 0 |\n", 207 | ">| 4. | Garman Kohlhagen | b = r - rf |\n", 208 | ">| 5. | Asian | b = 0, modified V |\n", 209 | ">*Figure 2 - GBS Cost of Carry Adjustment*\n" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "### Asian Volatility Adjustment\n", 217 | "\n", 218 | "An Asian option is an option whose payoff is calculated using the average price of the underlying over some period of time rather than the price on the expiration date. As a result, Asian options are also called average price options. The reason that traders use Asian options is that averaging a settlement price over a period of time reduces the affect of manipulation or unusual price movements on the expiration date on the value of the option. As a result, Asian options are often found on strategically important commodities, like crude oil or in markets with intermittent trading.\n", 219 | "\n", 220 | "The average of a set of random numbers (prices in this case) will have a lower dispersion (a lower volatility) than the dispersion of prices observed on any single day. As a result, the implied volatility used to price Asian options will usually be slightly lower than the implied volatility on a comparable European option. From a mathematical perspective, valuing an Asian option is slightly complicated since the average of a set of lognormal distributions is not itself lognormally distributed. However, a reasonably good approximation of the correct answer is not too difficult to obtain.\n", 221 | "\n", 222 | "In the case of Asian options on futures, it is possible to use a modified Black-76 formula that replaces the implied volatility term with an adjusted implied volatility of the average price. As long as the first day of the averaging period is in the future, the following formula can be used to value Asian options (see *Figure 3 – Asian Option Formula*).\n", 223 | "\n", 224 | ">*Asian Adjusted Volatility*\n", 225 | "\\begin{equation}\n", 226 | "V_a = \\sqrt{\\frac{ln(M)}{T}}\n", 227 | "\\end{equation}\n", 228 | "\n", 229 | ">*with the intermediate calculation*\n", 230 | "\\begin{equation}\n", 231 | "M = \\frac{2e^{V^2T} - 2e^{V^2T}[1+V^2(T-t)]}{V^4(T-t)^2}\n", 232 | "\\end{equation}\n", 233 | "\n", 234 | ">| Symbol | Meaning |\n", 235 | "|--------|-----------------------------------------------------------------------------------------------------------------|\n", 236 | "| Va | **Asian Adjusted Volatility**, This will replace the volatility (V) term in the GBS equations shown previously. |\n", 237 | "| T | **Time to expiration**. The time to expiration of the option (measured in years). |\n", 238 | "| t | **Time to start of averaging period**. The time to the start of the averaging period (measured in years). |\n", 239 | "\n", 240 | ">*Figure 3 - Asian Option Formula*\n" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "### Spread Option (Kirk's Approximation) Calculation\n", 248 | "\n", 249 | "Spread options are based on the spread between two commodity prices. They are commonly used to model physical investments as \"real options\" or to mark-to-market contracts that hedge physical assets. For example, a natural gas fueled electrical generation unit can be used to convert fuel (natural gas) into electricity. Whenever this conversion is profitable, it would be rational to operate the unit. This type of conversion is readily modeled by a spread option. When the spread of (electricity prices - fuel costs) is greater than the conversion cost, then the unit would operate. In this example, the conversion cost, which might be called the *Variable Operations and Maintenance* or VOM for a generation unit, would represent the strike price.\n", 250 | "\n", 251 | "Analytic formulas similar to the Black Scholes equation are commonly used to value commodity spread options. One such formula is called *Kirk’s approximation*. While an exact closed form solution does not exist to value spread options, approximate solutions can give reasonably accurate results. Kirk’s approximation uses a Black Scholes style framework to analyze the joint distribution that results from the ratio of two log-normal distributions.\n", 252 | "\n", 253 | "In a Black Scholes equation, the distribution of price returns is assumed to be normally distributed on the expiration date. Kirk’s approximation builds on the Black Scholes framework by taking advantage of the fact that the ratio of two log-normal distributions is approximately normally distributed. By modeling a ratio of two prices rather than the spread between the prices, Kirk’s approximation can use the same formulas designed for options based on a single underlying. In other words, Kirk’s approximation uses an algebraic transformation to fit the spread option into the Black Scholes framework.\n", 254 | "\n", 255 | "The payoff of a spread option is show in *Figure 4 - Spread Option Payoff*.\n", 256 | "\n", 257 | "\n", 258 | ">\\begin{equation}\n", 259 | "C = max[F_1 - F_2 - X, 0]\n", 260 | "\\end{equation}\n", 261 | "\n", 262 | ">\\begin{equation}\n", 263 | "P = max[X - (F_1 - F_2), 0]\n", 264 | "\\end{equation}\n", 265 | "\n", 266 | ">where\n", 267 | "\n", 268 | ">| Symbol | Meaning |\n", 269 | "|--------|----------------------------------------------------|\n", 270 | "| F_1 | **Price of Asset 1**, The prices of the first asset. |\n", 271 | "| F_2 | **Price of Asset 2**. The price of the second asset. |\n", 272 | "\n", 273 | "\n", 274 | ">*Figure 4 - Spread Option Payoff*\n", 275 | "\n", 276 | "This can be algebraically manipulated as shown in *Figure 5 - Spread Option Payoff, Manipulated*.\n", 277 | "\n", 278 | ">\\begin{equation}\n", 279 | "C = max \\biggl[\\frac{F_1}{F_2+X}-1,0 \\biggr](F_2 + X)\n", 280 | "\\end{equation}\n", 281 | "\n", 282 | ">\\begin{equation}\n", 283 | "P = max \\biggl[1-\\frac{F_1}{F_2+X},0 \\biggr](F_2 + X)\n", 284 | "\\end{equation}\n", 285 | ">*Figure 5 - Spread Option Payoff, Manipulated*\n", 286 | "\n", 287 | "This allows Kirk’s approximation to model the distribution of the spread as the ratio of the price of asset 1 over the price of asset 2 plus the strike price. This ratio can then be converted into a formula very similar to the Generalized Black Scholes formulas. In fact, this is the Black Scholes formula shown above with the addition of a (F_2 + X) term (See *Figure 6 – Kirk’s Approximation Ratio*).\n", 288 | "\n", 289 | ">*Ratio of prices*\n", 290 | ">\\begin{equation}\n", 291 | "F = \\frac{F_1}{F_2 + X}\n", 292 | "\\end{equation}\n", 293 | "\n", 294 | ">The ratio implies that the option is profitable to exercise (*in the money*) whenever the ratio of prices (F in the formula above) is greater than 1. This occurs the cost of the finished product (F_1) exceeds total cost of the raw materials (F_2) and the conversion cost (X). This requires a modification to the Call/Put Price formulas and to the D_1 formula. Because the option is in the money when F>1, the \"strike\" price used in inner square brackets of the Call/Put Price formulas and the D1 formula is set to 1.\n", 295 | "\n", 296 | ">*Spread Option Call Price*\n", 297 | ">\\begin{equation}\n", 298 | "C = (F_2 + X)\\biggl[Fe^{(b-r)T} N(D_1) - e^{-rT} N(D_2)\\biggr]\n", 299 | "\\end{equation}\n", 300 | "\n", 301 | ">*Spread Option Put Price*\n", 302 | ">\\begin{equation}\n", 303 | "P = (F_2 + X)\\biggl[e^{-rT} N(-D_2) - Fe^{(b-r)T} N(-D_1)\\biggr]\n", 304 | "\\end{equation}\n", 305 | "\n", 306 | ">\\begin{equation}\n", 307 | "D_1 = \\frac{ln(F) + (b+\\frac{V^2}{2})T}{V*\\sqrt{T}}\n", 308 | "\\end{equation}\n", 309 | "\n", 310 | ">\\begin{equation}\n", 311 | "D_2 = D_1 - V\\sqrt{T}\n", 312 | "\\end{equation}\n", 313 | "\n", 314 | ">*Figure 6- Kirk's Approximation Ratio*\n", 315 | "\n", 316 | "The key complexity is determining the appropriate volatility that needs to be used in the equation. The “approximation” which defines Kirk’s approximation is the assumption that the ratio of two log-normal distributions is normally distributed. That assumption makes it possible to estimate the volatility needed for the modified Black Scholes style equation. (See *Figure 7 - Kirk's Approximation (Volatility)*).\n", 317 | "\n", 318 | ">\\begin{equation}\n", 319 | "V = \\sqrt{ V_1^{2}+ \\biggl[V_2\\frac{F_2}{F_2+X}\\biggr]^2 - 2ρ V_1 V_2 \\frac{F_2}{F_2+X} }\n", 320 | "\\end{equation}\n", 321 | "\n", 322 | ">| Symbol | Meaning |\n", 323 | ">|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n", 324 | ">| V | **Volatility**. The Kirk's approximation volatility that will be placed into the formula shown in Figure 6 |\n", 325 | ">| V1 | **Volatility of Asset 1**. The strike, or exercise, price of the option. |\n", 326 | ">| V2 | **Volatility of Asset 2**. The volatility of the second asset |\n", 327 | ">| ρ | **Correlation**. The correlation between price of asset 1 and the price of asset 2. |\n", 328 | "\n", 329 | ">*Figure 7- Kirk's Approximation (Volatility)*\n", 330 | "\n", 331 | "A second complexity is that the prices of two assets (F1 and F2) have to be in the same units. For example, in a heat rate option, the option represents the ability to convert fuel (natural gas) into electricity. The price of the first asset, electricity, might be quoted in US dollars per megawatt-hour or USD/MWH. However, the price of the second asset might be quoted in USD/MMBTU. To use the approximation, it is necessary to convert the price of the second asset into the units of the first asset (See *Example 1 - a Heat Rate Option*). This conversion rate will typically be specified as part of the contract.\n", 332 | "\n", 333 | ">Example: A 10 MMBTU/MWH heat rate call option\n", 334 | ">* F1 = price of electricity = USD 35/MWH\n", 335 | ">* F2* = price of natural gas = USD 3.40/MMBTU; *This is not the price to plug into the model!*\n", 336 | ">* V1 = volatility of electricity forward prices = 35%\n", 337 | ">* V2 = volatility of natural gas forward price = 35%\n", 338 | ">* Rho = correlation between electricity and natural gas forward prices = 90%\n", 339 | ">* VOM = variable operation and maintenance cost (the conversion cost) = USD 3/MWH\n", 340 | "\n", 341 | ">Before being placed into a spread option model, the price of natural gas would need to \n", 342 | ">be converted into the correct units.\n", 343 | ">* F2 = Heat Rate * Fuel Cost = (10 MMBTU/MWH)(USD 3.40/MMBTU) = USD 34/MWH\n", 344 | "\n", 345 | ">The strike price would be set equal to the conversion cost\n", 346 | ">* X = VOM costs = USD 3/MWH\n", 347 | "\n", 348 | "> *Example 1 - a Heat Rate Call Option*\n", 349 | "\n", 350 | "Another important consideration (not discussed in this write-up) is that volatility and correlation need to be matched to the tenor of the underlying assets. This means that it is necessary to measure the volatility of forward prices rather than spot prices. It may also be necessary to match the volatility and correlation to the correct month. For example, power prices in August may behave very differently than power prices in October or May in certain regions. \n", 351 | "\n", 352 | "Like any model, spread options are subject to the \"garbage in = garbage out\" problem. However, the relative complexity of modeling commodity prices (the typical underlying for spread options) makes calibrating inputs a key part of the model.\n" 353 | ] 354 | }, 355 | { 356 | "cell_type": "markdown", 357 | "metadata": {}, 358 | "source": [ 359 | "### American Options\n", 360 | "American options differ from European options because they can be exercised at any time. If there is a possibility that it will be more profitable to exercise the option than sell it, an American option will have more value than a corresponding European option. Early exercise typically occurs only when an option is *in the money*. If an option is out of the money, there is usually no reason to exercise early - it would be better to sell the option (in the case of a put option, to sell the option and the underlying asset).\n", 361 | "\n", 362 | "The decision of whether to exercise early is primarily a question of interest rates and carrying costs. Carrying costs, or *cost of carry*, is a term that means an intermediate cash flows that is the result of holding an asset. For example, dividends on stocks are a postive cost of carry (owning the asset gives the owner a cash flow). A commodity might have a negative cost of carry. For example, a commodity that requires its owner to pay for storage would cause the owner of the physical commodity to pay cash to hold the asset. (**Note:** Commodity options are typically written on forwards or futures which have zero cost of carry instead of the actual underlying commodity). Cost of carry is cash flow that affects the owner of the underlying commodity and not the owner of the option. For example, when a stock pays a dividend, the owner of a call option does not receive the dividend - just the owner of the stock. For the perspective for the owner of a call option on a stock, the cost of carry will be the interest received from holding cash (r) less any dividends paid to owners of the stock (q).\n", 363 | "\n", 364 | "Since an option has some value (the *extrinsic value*) that would be given up by exercising the option, exercising an option prior to maturity is a trade off between the option's extrinsic value (the remaining optionality) and the relative benefit of holding cash (time value of money) versus the benefit of holding the asset (carrying costs). \n", 365 | "\n", 366 | "The early exercise feature of **American equity put options** may have value when:\n", 367 | "* The cost of carry on the asset is low - preferably zero or negative. \n", 368 | "* Interest rates are high \n", 369 | "* The option is in the money\n", 370 | "\n", 371 | "The early exercise feature of **American equity call options** may have value when:\n", 372 | "* The cost of carry on the asset is positive\n", 373 | "* Interest rates are low or negative\n", 374 | "* The option is in the money \n", 375 | "\n", 376 | "With commodities, things are a slightly different. There is typically no cost of carry since the underlying is a forward or a futures contract. It does not cost any money to enter an at-the-money commodity forward, swap, or futures contract. Also, these contracts don't have any intermediate cash flows. As a result, the primary benefit of early exercise is to get cash immediately (exercising an in-the-money option) rather than cash in the future. In high interest rate environements, the money recieved immediately from immediate execution may exceed the extrinsic value of the contract. This is due to strike price not being present valued in immediate execution (it is specified in the contract and a fixed number) but the payoff of a European option is discounted (forward price - strike price). \n", 377 | "\n", 378 | "The overall result is that early exercise is fairly uncommon for most commodity options. Typically, it only occurs when interest rates are high. Generally, interest rates have to be higher than 15%-20% for American commodity options to differ substantially in value from European options with the same terms.\n", 379 | "\n", 380 | "The early exercise feature of **American commodity options** has value when:\n", 381 | "* Interest rates are high\n", 382 | "* Volatility is low (this makes selling the option less of a good option)\n", 383 | "* The option is in the money \n", 384 | "\n", 385 | "There is no exact closed-form solution for American options. However, there are many approximations that are reasonably close to prices produced by open-form solutions (like binomial tree models). Two models are shown below, both created by Bjerksund and Stensland. The first was produced in 1993 and the second in 2002. The second model is a refinement of the first model, adding more complexity, in exchange for better accuracy. " 386 | ] 387 | }, 388 | { 389 | "cell_type": "markdown", 390 | "metadata": {}, 391 | "source": [ 392 | "#### Put-Call Parity\n", 393 | "Because of Put/Call parity, it is possible to use a call valuation formula to calculate the value of a put option.\n", 394 | "\n", 395 | ">\\begin{equation}\n", 396 | "P(S,X,T,r,b,V) = C(X,S,T,r-b,-b,V)\n", 397 | "\\end{equation}\n", 398 | "\n", 399 | "or using the order of parameters used in this library:\n", 400 | "\n", 401 | ">\\begin{equation}\n", 402 | "P(X,S,T,b,r,V) = C(S,X,T,-b,r-b,V)\n", 403 | "\\end{equation}\n" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": {}, 409 | "source": [ 410 | "#### BjerksundStensland 1993 (BS1993)\n", 411 | "There is no closed form solution for American options, and there are multiple people who have developed closed-form approximations to value American options. This is one such approximation. However, this approximation is no longer in active use by the public interface. It is primarily included as a secondary test on the BS2002 calculation. This function uses a numerical approximation to estimate the value of an American option. It does this by estimating a early exercise boundary and analytically estimating the probability of hitting that boundary. This uses the same inputs as a Generalized Black Scholes model:\n", 412 | "\n", 413 | " FS = Forward or spot price (abbreviated FS in code, F in formulas below)\n", 414 | " X = Strike Price\n", 415 | " T = time to expiration\n", 416 | " r = risk free rate \n", 417 | " b = cost of carry \n", 418 | " V = volatility\n", 419 | "\n", 420 | "_Intermediate Calculations_. To be consistent with the Bjerksund Stensland paper, this write-up uses similar notation. Please note that both a capital B (B_0 and B_Infinity), a lower case b, and the greek symbol Beta are all being used. B_0 and B_infinity represent that optimal exercise boundaries in edge cases (for call options where T=0 and T=infinity respectively), lower case b is the cost of carry (passed in as an input), and Beta is an intermediate calculations.\n", 421 | "\n", 422 | ">\\begin{array}{lcl}\n", 423 | "\\beta & = & (0.5 - \\frac{b}{V^2}) + \\sqrt{(\\frac{b}{V^2} - 0.5)^2 + 2 \\frac{r}{V^2}} \\\\\n", 424 | "B_\\infty & = & \\frac{\\beta}{\\beta-1} X \\\\\n", 425 | "B_0 & = & max\\biggl[X, (\\frac{r}{r-b}) X\\biggr] \\\\\n", 426 | "h_1 & = & - b T + 2 V \\sqrt{T} \\frac{B_0}{B_\\infty-B_0} \\\\\n", 427 | "\\end{array}\n", 428 | "\n", 429 | "_Calculate the Early Exercise Boundary (i)_. The lower case i represents the early exercise boundary. Alpha is an intermediate calculation.\n", 430 | "\n", 431 | ">\\begin{array}{lcl}\n", 432 | "i & = & B_0 + (B_\\infty-B_0)(1 - e^{h_1} ) \\\\\n", 433 | "\\alpha & = & (i-X) i^{-\\beta}\n", 434 | "\\end{array}\n", 435 | "\n", 436 | "Check for immediate exercise_.\n", 437 | "\n", 438 | ">\\begin{equation}\n", 439 | "if F >= i, then Value = F - X\n", 440 | "\\end{equation}\n", 441 | "\n", 442 | "If no immediate exercise, approximate the early exercise price.\n", 443 | "\n", 444 | ">\\begin{eqnarray}\n", 445 | "Value & = & \\alpha * F^\\beta \\\\\n", 446 | "& - & \\alpha * \\phi(F,T,\\beta,i,i,r,b,V) \\\\\n", 447 | "& + & \\phi(F,T,1,i,i,r,b,V) \\\\\n", 448 | "& - & \\phi(F,T,1,X,i,r,b,V) \\\\\n", 449 | "& - & X * \\phi(F,T,0,i,i,r,b,V) \\\\\n", 450 | "& + & X * \\phi(F,T,0,X,i,r,b,V)\n", 451 | "\\end{eqnarray}\n", 452 | "\n", 453 | "_Compare to European Value_. Due to the approximation, it is sometime possible to get a value slightly smaller than the European value. If so, set the value equal to the European value estimated using Generalized Black Scholes.\n", 454 | "\n", 455 | ">\\begin{equation}\n", 456 | "Value_{BS1993} = Max \\biggl[ Value, Value_{GBS} \\biggr]\n", 457 | "\\end{equation}" 458 | ] 459 | }, 460 | { 461 | "cell_type": "markdown", 462 | "metadata": {}, 463 | "source": [ 464 | "#### Bjerksund Stensland 2002 (BS2002)\n", 465 | "source: https://www.researchgate.net/publication/228801918\n", 466 | "\n", 467 | " FS = Forward or spot price (abbreviated FS in code, F in formulas below)\n", 468 | " X = Strike Price\n", 469 | " T = time to expiration\n", 470 | " r = risk free rate \n", 471 | " b = cost of carry \n", 472 | " V = volatility" 473 | ] 474 | }, 475 | { 476 | "cell_type": "markdown", 477 | "metadata": {}, 478 | "source": [ 479 | "#### Psi\n", 480 | "Psi is an intermediate calculation used by the Bjerksund Stensland 2002 approximation.\n", 481 | "\n", 482 | "\\begin{equation}\n", 483 | "\\psi(F, t_2, \\gamma, H, I_2, I_1, t_1, r, b, V)\n", 484 | "\\end{equation}\n", 485 | "\n", 486 | "_Intermediate calculations_.\n", 487 | "The Psi function has a large number of intermediate calculations. For clarity, these are loosely organized into groups with each group used to simplify the next set of intermediate calculations.\n", 488 | "\n", 489 | ">\\begin{array}{lcl}\n", 490 | "A_1 & = & V \\ln(t_1) \\\\\n", 491 | "A_2 & = & V \\ln(t_2) \\\\\n", 492 | "B_1 & = & \\biggl[ b+(\\gamma-0.5) V^2 \\biggr] t_1 \\\\\n", 493 | "B_2 & = & \\biggl[ b+(\\gamma-0.5) V^2 \\biggr] t_2 \n", 494 | "\\end{array} \n", 495 | "\n", 496 | "More Intermediate calculations\n", 497 | ">\\begin{array}{lcl}\n", 498 | "d_1 & = & \\frac{ln(\\frac{F}{I_1}) + B_1}{A_1} \\\\\n", 499 | "d_2 & = & \\frac{ln(\\frac{I_2^2}{F I_1}) + B_1}{A_1} \\\\\n", 500 | "d_3 & = & \\frac{ln(\\frac{F}{I_1}) - B_1}{A_1} \\\\\n", 501 | "d_4 & = & \\frac{ln(\\frac{I_2^2}{F I_1}) - B_1}{A_1} \\\\\n", 502 | "e_1 & = & \\frac{ln(\\frac{F}{H}) + B_2}{A_2} \\\\\n", 503 | "e_2 & = & \\frac{ln(\\frac{I_2^2}{F H}) + B_2}{A_2} \\\\\n", 504 | "e_3 & = & \\frac{ln(\\frac{I_1^2}{F H}) + B_2}{A_2} \\\\\n", 505 | "e_4 & = & \\frac{ln(\\frac{F I_1^2}{H I_2^2}) + B_2}{A_2}\n", 506 | "\\end{array}\n", 507 | "\n", 508 | "Even More Intermediate calculations\n", 509 | ">\\begin{array}{lcl}\n", 510 | "\\tau & = & \\sqrt{\\frac{t_1}{t_2}} \\\\\n", 511 | "\\lambda & = & -r+\\gamma b+\\frac{\\gamma}{2} (\\gamma-1) V^2 \\\\\n", 512 | "\\kappa & = & \\frac{2b}{V^2} +(2 \\gamma - 1)\n", 513 | "\\end{array}\n", 514 | "\n", 515 | "_The calculation of Psi_.\n", 516 | "This is the actual calculation of the Psi function. In the function below, M() represents the cumulative bivariate normal distribution (described a couple of paragraphs below this section). The abbreviation M() is used instead of CBND() in this section to make the equation a bit more readable and to match the naming convention used in Haug's book \"The Complete Guide to Option Pricing Formulas\".\n", 517 | "\n", 518 | ">\\begin{eqnarray}\n", 519 | "\\psi & = & e^{\\lambda t_2} F^\\gamma M(-d_1, -e_1, \\tau) \\\\\n", 520 | "& - & \\frac{I_2}{F}^\\kappa M(-d_2, -e_2, \\tau) \\\\\n", 521 | "& - & \\frac{I_1}{F}^\\kappa M(-d_3, -e_3, -\\tau) \\\\\n", 522 | "& + & \\frac{I_1}{I_2}^\\kappa M(-d_4, -e_4, -\\tau))\n", 523 | "\\end{eqnarray}\n", 524 | "\n" 525 | ] 526 | }, 527 | { 528 | "cell_type": "markdown", 529 | "metadata": {}, 530 | "source": [ 531 | "#### Phi\n", 532 | "Phi is an intermediate calculation used by both the Bjerksun Stensland 1993 and 2002 approximations. Many of the parameters are the same as the GBS model.\n", 533 | "\n", 534 | "\\begin{equation}\n", 535 | "\\phi(FS, T, \\gamma, h, I, r, b, V)\n", 536 | "\\end{equation}\n", 537 | "\n", 538 | " FS = Forward or spot price (abbreviated FS in code, F in formulas below).\n", 539 | " T = time to expiration. \n", 540 | " I = trigger price (as calculated in either BS1993 or BS2002 formulas\n", 541 | " gamma = modifier to T, calculated in BS1993 or BS2002 formula\n", 542 | " r = risk free rate. \n", 543 | " b = cost of carry. \n", 544 | " V = volatility. \n", 545 | "\n", 546 | "Internally, the Phi() function is implemented as follows:\n", 547 | "\n", 548 | ">\\begin{equation}\n", 549 | "d_1 = -\\frac{ln(\\frac{F}{h}) + \\biggl[b+(\\gamma-0.5) V^2 \\biggr] T}{V \\sqrt{T}}\n", 550 | "\\end{equation}\n", 551 | "\n", 552 | ">\\begin{equation}\n", 553 | "d_2 = d_1 - 2 \\frac{ln(I/F)}{V \\sqrt(T)}\n", 554 | "\\end{equation}\n", 555 | "\n", 556 | ">\\begin{equation}\n", 557 | "\\lambda = -r+\\gamma b+0.5 \\gamma (\\gamma-1) V^2\n", 558 | "\\end{equation}\n", 559 | "\n", 560 | ">\\begin{equation}\n", 561 | "\\kappa = \\frac{2b}{V^2}+(2\\gamma-1)\n", 562 | "\\end{equation}\n", 563 | "\n", 564 | ">\\begin{equation}\n", 565 | "\\phi = e^{\\lambda T} F^{\\gamma} \\biggl[ N(d_1)-\\frac{I}{F}^{\\kappa} N(d_2) \\biggr]\n", 566 | "\\end{equation}" 567 | ] 568 | }, 569 | { 570 | "cell_type": "markdown", 571 | "metadata": {}, 572 | "source": [ 573 | "##### Normal Cumulative Density Function (N)\n", 574 | "This is the normal cumulative density function. It can be found described in a variety of statistical textbooks and/or wikipedia. It is part of the standard scipy.stats distribution and imported using the \"from scipy.stats import norm\" command.\n", 575 | "\n", 576 | "Example:\n", 577 | "\\begin{equation}\n", 578 | " N(d_1)\n", 579 | "\\end{equation}" 580 | ] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": {}, 585 | "source": [ 586 | "#### Cumulative bivariate normal distribution (CBND)\n", 587 | "The bivariate normal density function (BNDF) is given below (See *Figure 8 - Bivariate Normal Density Function (BNDF)*):\n", 588 | ">\\begin{equation}\n", 589 | "BNDF(x, y) = \\frac{1}{2 \\pi \\sqrt{1-p^2}} exp \\biggl[-\\frac{x^2-2pxy+y^2}{2(1-p^2)}\\biggr]\n", 590 | "\\end{equation}\n", 591 | ">*Figure 8. Bivariate Normal Density Function (BNDF)*\n", 592 | "\n", 593 | "This can be integrated over x and y to calculate the joint probability that x < a and y < b. This is called the cumulative bivariate normal distribution (CBND) (See *Figure 9 - Cumulative Bivariate Normal Distribution (CBND))*: \n", 594 | ">\\begin{equation}\n", 595 | "CBND(a, b, p) = \\frac{1}{2 \\pi \\sqrt{1-p^2}} \\int_{-\\infty}^{a} \\int_{-\\infty}^{b} exp \\biggl[-\\frac{x^2-2pxy+y^2}{2(1-p^2)}\\biggr] d_x d_y\n", 596 | "\\end{equation}\n", 597 | ">*Figure 9. Cumulative Bivariate Normal Distribution (CBND)*\n", 598 | "\n", 599 | "Where\n", 600 | "* x = the first variable\n", 601 | "* y = the second variable\n", 602 | "* a = upper bound for first variable\n", 603 | "* b = upper bound for second variable\n", 604 | "* p = correlation between first and second variables\n", 605 | "\n", 606 | "There is no closed-form solution for this equation. However, several approximations have been developed and are included in the numpy library distributed with Anaconda. The Genz 2004 model was chosen for implementation. Alternative models include those developed by Drezner and Wesolowsky (1990) and Drezner (1978). The Genz model improves these other model by going to an accuracy of 14 decimal points (from approximately 8 decimal points and 6 decimal points respectively). " 607 | ] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": {}, 612 | "source": [ 613 | "-------------------------\n", 614 | "## Limitations:\n", 615 | "These functions have been tested for accuracy within an allowable range of inputs (see \"Model Input\" section below). However, general modeling advice applies to the use of the model. These models depend on a number of assumptions. In plain English, these models assume that the distribution of future prices can be described by variables like implied volatility. To get good results from the model, the model should only be used with reliable inputs.\n", 616 | "\n", 617 | "The following limitations are also in effect:\n", 618 | "\n", 619 | "1. The Asian Option approximation shouldn't be used for Asian options that are into the Asian option calculation period.\n", 620 | "2. The American and American76 approximations break down when r < -20%. The limits are set wider in this example for testing purposes, but production code should probably limit interest rates to values between -20% and 100%. In practice, negative interest rates should be extremely rare.\n", 621 | "3. No greeks are produced for spread options\n", 622 | "4. These models assume a constant volatility term structure. This has no effect on European options. However, options that are likely to be exercise early (certain American options) and Asian options may be more affected. " 623 | ] 624 | }, 625 | { 626 | "cell_type": "markdown", 627 | "metadata": {}, 628 | "source": [ 629 | "-------------------------\n", 630 | "## Model Inputs\n", 631 | "This section describes the function calls an inputs needed to call this model:\n", 632 | "\n", 633 | "These functions encapsulate the most commonly encountered option pricing formulas. These function primarily figure out the cost-of-carry term (b) and then call the generic version of the function. All of these functions return an array containg the premium and the greeks.\n", 634 | "\n", 635 | "#### Public Functions in the Library\n", 636 | "\n", 637 | "Pricing Formulas:\n", 638 | " 1. BlackScholes (OptionType, X, FS, T, r, V)\n", 639 | " 2. Merton (OptionType, X, FS, T, r, q, V)\n", 640 | " 3. Black76 (OptionType, X, FS, T, r, V)\n", 641 | " 4. GK (OptionType, X, FS, T, b, r, rf, V)\n", 642 | " 5. Asian76 (OptionType, X, FS, T, TA, r, V)\n", 643 | " 6. Kirks76\n", 644 | " 7. American (OptionType, X, FS, T, r, q, V)\n", 645 | " 8. American76 (OptionType, X, FS, T, r, V)\n", 646 | "\n", 647 | "Implied Volatility Formulas\n", 648 | " 9. GBS_ImpliedVol(OptionType, X, FS, T, r, q, CP)\n", 649 | " 10. GBS_ImpliedVol76(OptionType, X, FS, T, r, CP)\n", 650 | " 11. American_ImpliedVol(OptionType, X, FS, T, r, q, CP)\n", 651 | " 11. American_ImpliedVol76(OptionType, X, FS, T, r, CP)\n", 652 | " \n", 653 | "#### Inputs used by all models\n", 654 | "| **Parameter** | **Description** |\n", 655 | "|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n", 656 | "| option_type | **Put/Call Indicator** Single character, \"c\" indicates a call; \"p\" a put|\n", 657 | "| fs | **Price of Underlying** FS is generically used, but for specific models, the following abbreviations may be used: F = Forward Price, S = Spot Price) |\n", 658 | "| x | **Strike Price** |\n", 659 | "| t | **Time to Maturity** This is in years (1.0 = 1 year, 0.5 = six months, etc)|\n", 660 | "| r | **Risk Free Interest Rate** Interest rates (0.10 = 10% interest rate |\n", 661 | "| v | **Implied Volatility** Annualized implied volatility (1=100% annual volatility, 0.34 = 34% annual volatility|\n", 662 | "\n", 663 | "#### Inputs used by some models\n", 664 | "| **Parameter** | **Description** |\n", 665 | "|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n", 666 | "| b | **Cost of Carry** This is only found in internal implementations, but is identical to the cost of carry (b) term commonly found in academic option pricing literature|\n", 667 | "| q | **Continuous Dividend** Used in Merton and American models; Internally, this is converted into cost of carry, b, with formula b = r-q |\n", 668 | "| rf | **Foreign Interest Rate** Only used GK model; this functions similarly to q |\n", 669 | "| t_a | **Asian Start** Used for Asian options; This is the time that starts the averaging period (TA=0 means that averaging starts immediately). As TA approaches T, the Asian value should become very close to the Black76 Value |\n", 670 | "| cp | **Option Price** Used in the implied vol calculations; This is the price of the call or put observed in the market |\n", 671 | "\n", 672 | "\n", 673 | "#### Outputs\n", 674 | "All of the option pricing functions return an array. The first element of the array is the value of the option, the other elements are the greeks which measure the sensitivity of the option to changes in inputs. The greeks are used primarily for risk-management purposes.\n", 675 | "\n", 676 | "| **Output** | **Description** |\n", 677 | "|------------|-------------------------------------------------------------------------------------------------------------------|\n", 678 | "| [0] | **Value** |\n", 679 | "| [1] | **Delta** Sensitivity of Value to changes in price |\n", 680 | "| [2] | **Gamma** Sensitivity of Delta to changes in price |\n", 681 | "| [3] | **Theta** Sensitivity of Value to changes in time to expiration (annualized). To get a daily Theta, divide by 365 |\n", 682 | "| [4] | **Vega** Sensitivity of Value to changes in Volatility |\n", 683 | "| [5] | **Rho** Sensitivity of Value to changes in risk-free rates. |\n", 684 | "\n", 685 | "The implied volatility functions return a single value (the implied volatility).\n", 686 | "\n", 687 | "#### Acceptable Range for inputs\n", 688 | "All of the inputs are bounded. While many of these functions will work with inputs outside of these bounds, they haven't been tested and are generally believed to be uncommon. The pricer will return an exception to the caller if an out-of-bounds input is used. If that was a valid input, the code below will need to be modified to allow wider inputs and the testing section updated to test that the models work under the widened inputs." 689 | ] 690 | }, 691 | { 692 | "cell_type": "code", 693 | "execution_count": 2, 694 | "metadata": { 695 | "collapsed": true 696 | }, 697 | "outputs": [], 698 | "source": [ 699 | "# This class contains the limits on inputs for GBS models\n", 700 | "# It is not intended to be part of this module's public interface\n", 701 | "class _GBS_Limits:\n", 702 | " # An GBS model will return an error if an out-of-bound input is input\n", 703 | " MAX32 = 2147483248.0\n", 704 | "\n", 705 | " MIN_T = 1.0 / 1000.0 # requires some time left before expiration\n", 706 | " MIN_X = 0.01\n", 707 | " MIN_FS = 0.01\n", 708 | "\n", 709 | " # Volatility smaller than 0.5% causes American Options calculations\n", 710 | " # to fail (Number to large errors).\n", 711 | " # GBS() should be OK with any positive number. Since vols less\n", 712 | " # than 0.5% are expected to be extremely rare, and most likely bad inputs,\n", 713 | " # _gbs() is assigned this limit too\n", 714 | " MIN_V = 0.005\n", 715 | "\n", 716 | " MAX_T = 100\n", 717 | " MAX_X = MAX32\n", 718 | " MAX_FS = MAX32\n", 719 | "\n", 720 | " # Asian Option limits\n", 721 | " # maximum TA is time to expiration for the option\n", 722 | " MIN_TA = 0\n", 723 | "\n", 724 | " # This model will work with higher values for b, r, and V. However, such values are extremely uncommon. \n", 725 | " # To catch some common errors, interest rates and volatility is capped to 100%\n", 726 | " # This reason for 1 (100%) is mostly to cause the library to throw an exceptions \n", 727 | " # if a value like 15% is entered as 15 rather than 0.15)\n", 728 | " MIN_b = -1\n", 729 | " MIN_r = -1\n", 730 | "\n", 731 | " MAX_b = 1\n", 732 | " MAX_r = 1\n", 733 | " MAX_V = 1\n", 734 | " \n", 735 | " " 736 | ] 737 | }, 738 | { 739 | "cell_type": "markdown", 740 | "metadata": {}, 741 | "source": [ 742 | "------------------------\n", 743 | "## Model Implementation\n", 744 | "These functions encapsulate a generic version of the pricing formulas. They are primarily intended to be called by the other functions within this libary. The following functions will have a fixed interface so that they can be called directly for academic applicaitons that use the cost-of-carry (b) notation:\n", 745 | "\n", 746 | " _GBS() A generalized European option model \n", 747 | " _American() A generalized American option model \n", 748 | " _GBS_ImpliedVol() A generalized European option implied vol calculator\n", 749 | " _American_ImpliedVol() A generalized American option implied vol calculator\n", 750 | " \n", 751 | "The other functions in this libary are called by the four main functions and are not expected to be interface safe (the implementation and interface may change over time)." 752 | ] 753 | }, 754 | { 755 | "cell_type": "markdown", 756 | "metadata": {}, 757 | "source": [ 758 | "### Implementation: European Options\n", 759 | "These functions implement the generalized Black Scholes (GBS) formula for European options. The main function is _gbs()." 760 | ] 761 | }, 762 | { 763 | "cell_type": "code", 764 | "execution_count": 3, 765 | "metadata": { 766 | "collapsed": true 767 | }, 768 | "outputs": [], 769 | "source": [ 770 | "# ------------------------------\n", 771 | "# This function verifies that the Call/Put indicator is correctly entered\n", 772 | "def _test_option_type(option_type):\n", 773 | " if (option_type != \"c\") and (option_type != \"p\"):\n", 774 | " raise GBS_InputError(\"Invalid Input option_type ({0}). Acceptable value are: c, p\".format(option_type))" 775 | ] 776 | }, 777 | { 778 | "cell_type": "code", 779 | "execution_count": 4, 780 | "metadata": { 781 | "collapsed": true 782 | }, 783 | "outputs": [], 784 | "source": [ 785 | "# ------------------------------\n", 786 | "# This function makes sure inputs are OK\n", 787 | "# It throws an exception if there is a failure\n", 788 | "def _gbs_test_inputs(option_type, fs, x, t, r, b, v):\n", 789 | " # -----------\n", 790 | " # Test inputs are reasonable\n", 791 | " _test_option_type(option_type)\n", 792 | "\n", 793 | " if (x < _GBS_Limits.MIN_X) or (x > _GBS_Limits.MAX_X):\n", 794 | " raise GBS_InputError(\n", 795 | " \"Invalid Input Strike Price (X). Acceptable range for inputs is {1} to {2}\".format(x, _GBS_Limits.MIN_X,\n", 796 | " _GBS_Limits.MAX_X))\n", 797 | "\n", 798 | " if (fs < _GBS_Limits.MIN_FS) or (fs > _GBS_Limits.MAX_FS):\n", 799 | " raise GBS_InputError(\n", 800 | " \"Invalid Input Forward/Spot Price (FS). Acceptable range for inputs is {1} to {2}\".format(fs,\n", 801 | " _GBS_Limits.MIN_FS,\n", 802 | " _GBS_Limits.MAX_FS))\n", 803 | "\n", 804 | " if (t < _GBS_Limits.MIN_T) or (t > _GBS_Limits.MAX_T):\n", 805 | " raise GBS_InputError(\n", 806 | " \"Invalid Input Time (T = {0}). Acceptable range for inputs is {1} to {2}\".format(t, _GBS_Limits.MIN_T,\n", 807 | " _GBS_Limits.MAX_T))\n", 808 | "\n", 809 | " if (b < _GBS_Limits.MIN_b) or (b > _GBS_Limits.MAX_b):\n", 810 | " raise GBS_InputError(\n", 811 | " \"Invalid Input Cost of Carry (b = {0}). Acceptable range for inputs is {1} to {2}\".format(b,\n", 812 | " _GBS_Limits.MIN_b,\n", 813 | " _GBS_Limits.MAX_b))\n", 814 | "\n", 815 | " if (r < _GBS_Limits.MIN_r) or (r > _GBS_Limits.MAX_r):\n", 816 | " raise GBS_InputError(\n", 817 | " \"Invalid Input Risk Free Rate (r = {0}). Acceptable range for inputs is {1} to {2}\".format(r,\n", 818 | " _GBS_Limits.MIN_r,\n", 819 | " _GBS_Limits.MAX_r))\n", 820 | "\n", 821 | " if (v < _GBS_Limits.MIN_V) or (v > _GBS_Limits.MAX_V):\n", 822 | " raise GBS_InputError(\n", 823 | " \"Invalid Input Implied Volatility (V = {0}). Acceptable range for inputs is {1} to {2}\".format(v,\n", 824 | " _GBS_Limits.MIN_V,\n", 825 | " _GBS_Limits.MAX_V))\n", 826 | "\n" 827 | ] 828 | }, 829 | { 830 | "cell_type": "code", 831 | "execution_count": 5, 832 | "metadata": { 833 | "collapsed": false 834 | }, 835 | "outputs": [], 836 | "source": [ 837 | "\n", 838 | "# The primary class for calculating Generalized Black Scholes option prices and deltas\n", 839 | "# It is not intended to be part of this module's public interface\n", 840 | "\n", 841 | "# Inputs: option_type = \"p\" or \"c\", fs = price of underlying, x = strike, t = time to expiration, r = risk free rate\n", 842 | "# b = cost of carry, v = implied volatility\n", 843 | "# Outputs: value, delta, gamma, theta, vega, rho\n", 844 | "def _gbs(option_type, fs, x, t, r, b, v):\n", 845 | " _debug(\"Debugging Information: _gbs()\")\n", 846 | " # -----------\n", 847 | " # Test Inputs (throwing an exception on failure)\n", 848 | " _gbs_test_inputs(option_type, fs, x, t, r, b, v)\n", 849 | "\n", 850 | " # -----------\n", 851 | " # Create preliminary calculations\n", 852 | " t__sqrt = math.sqrt(t)\n", 853 | " d1 = (math.log(fs / x) + (b + (v * v) / 2) * t) / (v * t__sqrt)\n", 854 | " d2 = d1 - v * t__sqrt\n", 855 | "\n", 856 | " if option_type == \"c\":\n", 857 | " # it's a call\n", 858 | " _debug(\" Call Option\")\n", 859 | " value = fs * math.exp((b - r) * t) * norm.cdf(d1) - x * math.exp(-r * t) * norm.cdf(d2)\n", 860 | " delta = math.exp((b - r) * t) * norm.cdf(d1)\n", 861 | " gamma = math.exp((b - r) * t) * norm.pdf(d1) / (fs * v * t__sqrt)\n", 862 | " theta = -(fs * v * math.exp((b - r) * t) * norm.pdf(d1)) / (2 * t__sqrt) - (b - r) * fs * math.exp(\n", 863 | " (b - r) * t) * norm.cdf(d1) - r * x * math.exp(-r * t) * norm.cdf(d2)\n", 864 | " vega = math.exp((b - r) * t) * fs * t__sqrt * norm.pdf(d1)\n", 865 | " rho = x * t * math.exp(-r * t) * norm.cdf(d2)\n", 866 | " else:\n", 867 | " # it's a put\n", 868 | " _debug(\" Put Option\")\n", 869 | " value = x * math.exp(-r * t) * norm.cdf(-d2) - (fs * math.exp((b - r) * t) * norm.cdf(-d1))\n", 870 | " delta = -math.exp((b - r) * t) * norm.cdf(-d1)\n", 871 | " gamma = math.exp((b - r) * t) * norm.pdf(d1) / (fs * v * t__sqrt)\n", 872 | " theta = -(fs * v * math.exp((b - r) * t) * norm.pdf(d1)) / (2 * t__sqrt) + (b - r) * fs * math.exp(\n", 873 | " (b - r) * t) * norm.cdf(-d1) + r * x * math.exp(-r * t) * norm.cdf(-d2)\n", 874 | " vega = math.exp((b - r) * t) * fs * t__sqrt * norm.pdf(d1)\n", 875 | " rho = -x * t * math.exp(-r * t) * norm.cdf(-d2)\n", 876 | "\n", 877 | " _debug(\" d1= {0}\\n d2 = {1}\".format(d1, d2))\n", 878 | " _debug(\" delta = {0}\\n gamma = {1}\\n theta = {2}\\n vega = {3}\\n rho={4}\".format(delta, gamma,\n", 879 | " theta, vega,\n", 880 | " rho))\n", 881 | " \n", 882 | " return value, delta, gamma, theta, vega, rho " 883 | ] 884 | }, 885 | { 886 | "cell_type": "markdown", 887 | "metadata": {}, 888 | "source": [ 889 | "### Implementation: American Options\n", 890 | "This section contains the code necessary to price American options. The main function is _American(). The other functions are called from the main function." 891 | ] 892 | }, 893 | { 894 | "cell_type": "code", 895 | "execution_count": 6, 896 | "metadata": { 897 | "collapsed": true 898 | }, 899 | "outputs": [], 900 | "source": [ 901 | "# -----------\n", 902 | "# Generalized American Option Pricer\n", 903 | "# This is a wrapper to check inputs and route to the current \"best\" American option model\n", 904 | "def _american_option(option_type, fs, x, t, r, b, v):\n", 905 | " # -----------\n", 906 | " # Test Inputs (throwing an exception on failure)\n", 907 | " _debug(\"Debugging Information: _american_option()\")\n", 908 | " _gbs_test_inputs(option_type, fs, x, t, r, b, v)\n", 909 | "\n", 910 | " # -----------\n", 911 | " if option_type == \"c\":\n", 912 | " # Call Option\n", 913 | " _debug(\" Call Option\")\n", 914 | " return _bjerksund_stensland_2002(fs, x, t, r, b, v)\n", 915 | " else:\n", 916 | " # Put Option\n", 917 | " _debug(\" Put Option\")\n", 918 | "\n", 919 | " # Using the put-call transformation: P(X, FS, T, r, b, V) = C(FS, X, T, -b, r-b, V)\n", 920 | " # WARNING - When reconciling this code back to the B&S paper, the order of variables is different\n", 921 | "\n", 922 | " put__x = fs\n", 923 | " put_fs = x\n", 924 | " put_b = -b\n", 925 | " put_r = r - b\n", 926 | "\n", 927 | " # pass updated values into the Call Valuation formula\n", 928 | " return _bjerksund_stensland_2002(put_fs, put__x, t, put_r, put_b, v)" 929 | ] 930 | }, 931 | { 932 | "cell_type": "code", 933 | "execution_count": 7, 934 | "metadata": { 935 | "collapsed": true 936 | }, 937 | "outputs": [], 938 | "source": [ 939 | "# -----------\n", 940 | "# American Call Option (Bjerksund Stensland 1993 approximation)\n", 941 | "# This is primarily here for testing purposes; 2002 model has superseded this one\n", 942 | "def _bjerksund_stensland_1993(fs, x, t, r, b, v):\n", 943 | " # -----------\n", 944 | " # initialize output\n", 945 | " # using GBS greeks (TO DO: update greek calculations)\n", 946 | " my_output = _gbs(\"c\", fs, x, t, r, b, v)\n", 947 | "\n", 948 | " e_value = my_output[0]\n", 949 | " delta = my_output[1]\n", 950 | " gamma = my_output[2]\n", 951 | " theta = my_output[3]\n", 952 | " vega = my_output[4]\n", 953 | " rho = my_output[5]\n", 954 | "\n", 955 | " # debugging for calculations\n", 956 | " _debug(\"-----\")\n", 957 | " _debug(\"Debug Information: _Bjerksund_Stensland_1993())\")\n", 958 | "\n", 959 | " # if b >= r, it is never optimal to exercise before maturity\n", 960 | " # so we can return the GBS value\n", 961 | " if b >= r:\n", 962 | " _debug(\" b >= r, early exercise never optimal, returning GBS value\")\n", 963 | " return e_value, delta, gamma, theta, vega, rho\n", 964 | "\n", 965 | " # Intermediate Calculations\n", 966 | " v2 = v ** 2\n", 967 | " sqrt_t = math.sqrt(t)\n", 968 | "\n", 969 | " beta = (0.5 - b / v2) + math.sqrt(((b / v2 - 0.5) ** 2) + 2 * r / v2)\n", 970 | " b_infinity = (beta / (beta - 1)) * x\n", 971 | " b_zero = max(x, (r / (r - b)) * x)\n", 972 | "\n", 973 | " h1 = -(b * t + 2 * v * sqrt_t) * (b_zero / (b_infinity - b_zero))\n", 974 | " i = b_zero + (b_infinity - b_zero) * (1 - math.exp(h1))\n", 975 | " alpha = (i - x) * (i ** (-beta))\n", 976 | "\n", 977 | " # debugging for calculations\n", 978 | " _debug(\" b = {0}\".format(b))\n", 979 | " _debug(\" v2 = {0}\".format(v2))\n", 980 | " _debug(\" beta = {0}\".format(beta))\n", 981 | " _debug(\" b_infinity = {0}\".format(b_infinity))\n", 982 | " _debug(\" b_zero = {0}\".format(b_zero))\n", 983 | " _debug(\" h1 = {0}\".format(h1))\n", 984 | " _debug(\" i = {0}\".format(i))\n", 985 | " _debug(\" alpha = {0}\".format(alpha))\n", 986 | "\n", 987 | " # Check for immediate exercise\n", 988 | " if fs >= i:\n", 989 | " _debug(\" Immediate Exercise\")\n", 990 | " value = fs - x\n", 991 | " else:\n", 992 | " _debug(\" American Exercise\")\n", 993 | " value = (alpha * (fs ** beta)\n", 994 | " - alpha * _phi(fs, t, beta, i, i, r, b, v)\n", 995 | " + _phi(fs, t, 1, i, i, r, b, v)\n", 996 | " - _phi(fs, t, 1, x, i, r, b, v)\n", 997 | " - x * _phi(fs, t, 0, i, i, r, b, v)\n", 998 | " + x * _phi(fs, t, 0, x, i, r, b, v))\n", 999 | "\n", 1000 | " # The approximation can break down in boundary conditions\n", 1001 | " # make sure the value is at least equal to the European value\n", 1002 | " value = max(value, e_value)\n", 1003 | " return value, delta, gamma, theta, vega, rho\n" 1004 | ] 1005 | }, 1006 | { 1007 | "cell_type": "code", 1008 | "execution_count": 8, 1009 | "metadata": { 1010 | "collapsed": true 1011 | }, 1012 | "outputs": [], 1013 | "source": [ 1014 | "# -----------\n", 1015 | "# American Call Option (Bjerksund Stensland 2002 approximation)\n", 1016 | "def _bjerksund_stensland_2002(fs, x, t, r, b, v):\n", 1017 | " # -----------\n", 1018 | " # initialize output\n", 1019 | " # using GBS greeks (TO DO: update greek calculations)\n", 1020 | " my_output = _gbs(\"c\", fs, x, t, r, b, v)\n", 1021 | "\n", 1022 | " e_value = my_output[0]\n", 1023 | " delta = my_output[1]\n", 1024 | " gamma = my_output[2]\n", 1025 | " theta = my_output[3]\n", 1026 | " vega = my_output[4]\n", 1027 | " rho = my_output[5]\n", 1028 | "\n", 1029 | " # debugging for calculations\n", 1030 | " _debug(\"-----\")\n", 1031 | " _debug(\"Debug Information: _Bjerksund_Stensland_2002())\")\n", 1032 | "\n", 1033 | " # If b >= r, it is never optimal to exercise before maturity\n", 1034 | " # so we can return the GBS value\n", 1035 | " if b >= r:\n", 1036 | " _debug(\" Returning GBS value\")\n", 1037 | " return e_value, delta, gamma, theta, vega, rho\n", 1038 | "\n", 1039 | " # -----------\n", 1040 | " # Create preliminary calculations\n", 1041 | " v2 = v ** 2\n", 1042 | " t1 = 0.5 * (math.sqrt(5) - 1) * t\n", 1043 | " t2 = t\n", 1044 | "\n", 1045 | " beta_inside = ((b / v2 - 0.5) ** 2) + 2 * r / v2\n", 1046 | " # forcing the inside of the sqrt to be a positive number\n", 1047 | " beta_inside = abs(beta_inside)\n", 1048 | " beta = (0.5 - b / v2) + math.sqrt(beta_inside)\n", 1049 | " b_infinity = (beta / (beta - 1)) * x\n", 1050 | " b_zero = max(x, (r / (r - b)) * x)\n", 1051 | "\n", 1052 | " h1 = -(b * t1 + 2 * v * math.sqrt(t1)) * ((x ** 2) / ((b_infinity - b_zero) * b_zero))\n", 1053 | " h2 = -(b * t2 + 2 * v * math.sqrt(t2)) * ((x ** 2) / ((b_infinity - b_zero) * b_zero))\n", 1054 | "\n", 1055 | " i1 = b_zero + (b_infinity - b_zero) * (1 - math.exp(h1))\n", 1056 | " i2 = b_zero + (b_infinity - b_zero) * (1 - math.exp(h2))\n", 1057 | "\n", 1058 | " alpha1 = (i1 - x) * (i1 ** (-beta))\n", 1059 | " alpha2 = (i2 - x) * (i2 ** (-beta))\n", 1060 | "\n", 1061 | " # debugging for calculations\n", 1062 | " _debug(\" t1 = {0}\".format(t1))\n", 1063 | " _debug(\" beta = {0}\".format(beta))\n", 1064 | " _debug(\" b_infinity = {0}\".format(b_infinity))\n", 1065 | " _debug(\" b_zero = {0}\".format(b_zero))\n", 1066 | " _debug(\" h1 = {0}\".format(h1))\n", 1067 | " _debug(\" h2 = {0}\".format(h2))\n", 1068 | " _debug(\" i1 = {0}\".format(i1))\n", 1069 | " _debug(\" i2 = {0}\".format(i2))\n", 1070 | " _debug(\" alpha1 = {0}\".format(alpha1))\n", 1071 | " _debug(\" alpha2 = {0}\".format(alpha2))\n", 1072 | "\n", 1073 | " # check for immediate exercise\n", 1074 | " if fs >= i2:\n", 1075 | " value = fs - x\n", 1076 | " else:\n", 1077 | " # Perform the main calculation \n", 1078 | " value = (alpha2 * (fs ** beta)\n", 1079 | " - alpha2 * _phi(fs, t1, beta, i2, i2, r, b, v)\n", 1080 | " + _phi(fs, t1, 1, i2, i2, r, b, v)\n", 1081 | " - _phi(fs, t1, 1, i1, i2, r, b, v)\n", 1082 | " - x * _phi(fs, t1, 0, i2, i2, r, b, v)\n", 1083 | " + x * _phi(fs, t1, 0, i1, i2, r, b, v)\n", 1084 | " + alpha1 * _phi(fs, t1, beta, i1, i2, r, b, v)\n", 1085 | " - alpha1 * _psi(fs, t2, beta, i1, i2, i1, t1, r, b, v)\n", 1086 | " + _psi(fs, t2, 1, i1, i2, i1, t1, r, b, v)\n", 1087 | " - _psi(fs, t2, 1, x, i2, i1, t1, r, b, v)\n", 1088 | " - x * _psi(fs, t2, 0, i1, i2, i1, t1, r, b, v)\n", 1089 | " + x * _psi(fs, t2, 0, x, i2, i1, t1, r, b, v))\n", 1090 | "\n", 1091 | " # in boundary conditions, this approximation can break down\n", 1092 | " # Make sure option value is greater than or equal to European value\n", 1093 | " value = max(value, e_value)\n", 1094 | "\n", 1095 | " # -----------\n", 1096 | " # Return Data\n", 1097 | " return value, delta, gamma, theta, vega, rho\n" 1098 | ] 1099 | }, 1100 | { 1101 | "cell_type": "code", 1102 | "execution_count": 9, 1103 | "metadata": { 1104 | "collapsed": true 1105 | }, 1106 | "outputs": [], 1107 | "source": [ 1108 | "# ---------------------------\n", 1109 | "# American Option Intermediate Calculations\n", 1110 | "\n", 1111 | "# -----------\n", 1112 | "# The Psi() function used by _Bjerksund_Stensland_2002 model\n", 1113 | "def _psi(fs, t2, gamma, h, i2, i1, t1, r, b, v):\n", 1114 | " vsqrt_t1 = v * math.sqrt(t1)\n", 1115 | " vsqrt_t2 = v * math.sqrt(t2)\n", 1116 | "\n", 1117 | " bgamma_t1 = (b + (gamma - 0.5) * (v ** 2)) * t1\n", 1118 | " bgamma_t2 = (b + (gamma - 0.5) * (v ** 2)) * t2\n", 1119 | "\n", 1120 | " d1 = (math.log(fs / i1) + bgamma_t1) / vsqrt_t1\n", 1121 | " d3 = (math.log(fs / i1) - bgamma_t1) / vsqrt_t1\n", 1122 | "\n", 1123 | " d2 = (math.log((i2 ** 2) / (fs * i1)) + bgamma_t1) / vsqrt_t1\n", 1124 | " d4 = (math.log((i2 ** 2) / (fs * i1)) - bgamma_t1) / vsqrt_t1\n", 1125 | "\n", 1126 | " e1 = (math.log(fs / h) + bgamma_t2) / vsqrt_t2\n", 1127 | " e2 = (math.log((i2 ** 2) / (fs * h)) + bgamma_t2) / vsqrt_t2\n", 1128 | " e3 = (math.log((i1 ** 2) / (fs * h)) + bgamma_t2) / vsqrt_t2\n", 1129 | " e4 = (math.log((fs * (i1 ** 2)) / (h * (i2 ** 2))) + bgamma_t2) / vsqrt_t2\n", 1130 | "\n", 1131 | " tau = math.sqrt(t1 / t2)\n", 1132 | " lambda1 = (-r + gamma * b + 0.5 * gamma * (gamma - 1) * (v ** 2))\n", 1133 | " kappa = (2 * b) / (v ** 2) + (2 * gamma - 1)\n", 1134 | "\n", 1135 | " psi = math.exp(lambda1 * t2) * (fs ** gamma) * (_cbnd(-d1, -e1, tau)\n", 1136 | " - ((i2 / fs) ** kappa) * _cbnd(-d2, -e2, tau)\n", 1137 | " - ((i1 / fs) ** kappa) * _cbnd(-d3, -e3, -tau)\n", 1138 | " + ((i1 / i2) ** kappa) * _cbnd(-d4, -e4, -tau))\n", 1139 | " return psi\n" 1140 | ] 1141 | }, 1142 | { 1143 | "cell_type": "code", 1144 | "execution_count": 10, 1145 | "metadata": { 1146 | "collapsed": true 1147 | }, 1148 | "outputs": [], 1149 | "source": [ 1150 | "# -----------\n", 1151 | "# The Phi() function used by _Bjerksund_Stensland_2002 model and the _Bjerksund_Stensland_1993 model\n", 1152 | "def _phi(fs, t, gamma, h, i, r, b, v):\n", 1153 | " d1 = -(math.log(fs / h) + (b + (gamma - 0.5) * (v ** 2)) * t) / (v * math.sqrt(t))\n", 1154 | " d2 = d1 - 2 * math.log(i / fs) / (v * math.sqrt(t))\n", 1155 | "\n", 1156 | " lambda1 = (-r + gamma * b + 0.5 * gamma * (gamma - 1) * (v ** 2))\n", 1157 | " kappa = (2 * b) / (v ** 2) + (2 * gamma - 1)\n", 1158 | "\n", 1159 | " phi = math.exp(lambda1 * t) * (fs ** gamma) * (norm.cdf(d1) - ((i / fs) ** kappa) * norm.cdf(d2))\n", 1160 | "\n", 1161 | " _debug(\"-----\")\n", 1162 | " _debug(\"Debug info for: _phi()\")\n", 1163 | " _debug(\" d1={0}\".format(d1))\n", 1164 | " _debug(\" d2={0}\".format(d2))\n", 1165 | " _debug(\" lambda={0}\".format(lambda1))\n", 1166 | " _debug(\" kappa={0}\".format(kappa))\n", 1167 | " _debug(\" phi={0}\".format(phi))\n", 1168 | " return phi" 1169 | ] 1170 | }, 1171 | { 1172 | "cell_type": "code", 1173 | "execution_count": 11, 1174 | "metadata": { 1175 | "collapsed": true 1176 | }, 1177 | "outputs": [], 1178 | "source": [ 1179 | "# -----------\n", 1180 | "# Cumulative Bivariate Normal Distribution\n", 1181 | "# Primarily called by Psi() function, part of the _Bjerksund_Stensland_2002 model\n", 1182 | "def _cbnd(a, b, rho):\n", 1183 | " # This distribution uses the Genz multi-variate normal distribution \n", 1184 | " # code found as part of the standard SciPy distribution\n", 1185 | " lower = np.array([0, 0])\n", 1186 | " upper = np.array([a, b])\n", 1187 | " infin = np.array([0, 0])\n", 1188 | " correl = rho\n", 1189 | " error, value, inform = mvn.mvndst(lower, upper, infin, correl)\n", 1190 | " return value" 1191 | ] 1192 | }, 1193 | { 1194 | "cell_type": "markdown", 1195 | "metadata": {}, 1196 | "source": [ 1197 | "### Implementation: Implied Vol\n", 1198 | "This section implements implied volatility calculations. It contains 3 main models:\n", 1199 | "1. **At-the-Money approximation.** This is a very fast approximation for implied volatility. It is used to estimate a starting point for the search functions.\n", 1200 | "2. **Newton-Raphson Search.** This is a fast implied volatility search that can be used when there is a reliable estimate of Vega (i.e., European options) \n", 1201 | "3. **Bisection Search.** An implied volatility search (not quite as fast as a Newton search) that can be used where there is no reliable Vega estimate (i.e., American options).\n" 1202 | ] 1203 | }, 1204 | { 1205 | "cell_type": "code", 1206 | "execution_count": 12, 1207 | "metadata": { 1208 | "collapsed": true 1209 | }, 1210 | "outputs": [], 1211 | "source": [ 1212 | "# ----------\n", 1213 | "# Inputs (not all functions use all inputs)\n", 1214 | "# fs = forward/spot price\n", 1215 | "# x = Strike\n", 1216 | "# t = Time (in years)\n", 1217 | "# r = risk free rate\n", 1218 | "# b = cost of carry\n", 1219 | "# cp = Call or Put price\n", 1220 | "# precision = (optional) precision at stopping point\n", 1221 | "# max_steps = (optional) maximum number of steps\n", 1222 | "\n", 1223 | "# ----------\n", 1224 | "# Approximate Implied Volatility\n", 1225 | "#\n", 1226 | "# This function is used to choose a starting point for the\n", 1227 | "# search functions (Newton and bisection searches). \n", 1228 | "# Brenner & Subrahmanyam (1988), Feinstein (1988)\n", 1229 | "\n", 1230 | "def _approx_implied_vol(option_type, fs, x, t, r, b, cp):\n", 1231 | " _test_option_type(option_type)\n", 1232 | "\n", 1233 | " ebrt = math.exp((b - r) * t)\n", 1234 | " ert = math.exp(-r * t)\n", 1235 | "\n", 1236 | " a = math.sqrt(2 * math.pi) / (fs * ebrt + x * ert)\n", 1237 | "\n", 1238 | " if option_type == \"c\":\n", 1239 | " payoff = fs * ebrt - x * ert\n", 1240 | " else:\n", 1241 | " payoff = x * ert - fs * ebrt\n", 1242 | "\n", 1243 | " b = cp - payoff / 2\n", 1244 | " c = (payoff ** 2) / math.pi\n", 1245 | "\n", 1246 | " v = (a * (b + math.sqrt(b ** 2 + c))) / math.sqrt(t)\n", 1247 | "\n", 1248 | " return v" 1249 | ] 1250 | }, 1251 | { 1252 | "cell_type": "code", 1253 | "execution_count": 13, 1254 | "metadata": { 1255 | "collapsed": true 1256 | }, 1257 | "outputs": [], 1258 | "source": [ 1259 | "# ----------\n", 1260 | "# Find the Implied Volatility of an European (GBS) Option given a price\n", 1261 | "# using Newton-Raphson method for greater speed since Vega is available\n", 1262 | "\n", 1263 | "def _gbs_implied_vol(option_type, fs, x, t, r, b, cp, precision=.00001, max_steps=100):\n", 1264 | " return _newton_implied_vol(_gbs, option_type, x, fs, t, b, r, cp, precision, max_steps)" 1265 | ] 1266 | }, 1267 | { 1268 | "cell_type": "code", 1269 | "execution_count": 14, 1270 | "metadata": { 1271 | "collapsed": true 1272 | }, 1273 | "outputs": [], 1274 | "source": [ 1275 | "# ----------\n", 1276 | "# Find the Implied Volatility of an American Option given a price\n", 1277 | "# Using bisection method since Vega is difficult to estimate for Americans\n", 1278 | "def _american_implied_vol(option_type, fs, x, t, r, b, cp, precision=.00001, max_steps=100):\n", 1279 | " return _bisection_implied_vol(_american_option, option_type, fs, x, t, r, b, cp, precision, max_steps)\n" 1280 | ] 1281 | }, 1282 | { 1283 | "cell_type": "code", 1284 | "execution_count": 15, 1285 | "metadata": { 1286 | "collapsed": true 1287 | }, 1288 | "outputs": [], 1289 | "source": [ 1290 | "# ----------\n", 1291 | "# Calculate Implied Volatility with a Newton Raphson search\n", 1292 | "def _newton_implied_vol(val_fn, option_type, x, fs, t, b, r, cp, precision=.00001, max_steps=100):\n", 1293 | " # make sure a valid option type was entered\n", 1294 | " _test_option_type(option_type)\n", 1295 | "\n", 1296 | " # Estimate starting Vol, making sure it is allowable range\n", 1297 | " v = _approx_implied_vol(option_type, fs, x, t, r, b, cp)\n", 1298 | " v = max(_GBS_Limits.MIN_V, v)\n", 1299 | " v = min(_GBS_Limits.MAX_V, v)\n", 1300 | "\n", 1301 | " # Calculate the value at the estimated vol\n", 1302 | " value, delta, gamma, theta, vega, rho = val_fn(option_type, fs, x, t, r, b, v)\n", 1303 | " min_diff = abs(cp - value)\n", 1304 | "\n", 1305 | " _debug(\"-----\")\n", 1306 | " _debug(\"Debug info for: _Newton_ImpliedVol()\")\n", 1307 | " _debug(\" Vinitial={0}\".format(v))\n", 1308 | "\n", 1309 | " # Newton-Raphson Search\n", 1310 | " countr = 0\n", 1311 | " while precision <= abs(cp - value) <= min_diff and countr < max_steps:\n", 1312 | "\n", 1313 | " v = v - (value - cp) / vega\n", 1314 | " if (v > _GBS_Limits.MAX_V) or (v < _GBS_Limits.MIN_V):\n", 1315 | " _debug(\" Volatility out of bounds\")\n", 1316 | " break\n", 1317 | "\n", 1318 | " value, delta, gamma, theta, vega, rho = val_fn(option_type, fs, x, t, r, b, v)\n", 1319 | " min_diff = min(abs(cp - value), min_diff)\n", 1320 | "\n", 1321 | " # keep track of how many loops\n", 1322 | " countr += 1\n", 1323 | " _debug(\" IVOL STEP {0}. v={1}\".format(countr, v))\n", 1324 | "\n", 1325 | " \n", 1326 | " # check if function converged and return a value\n", 1327 | " if abs(cp - value) < precision:\n", 1328 | " # the search function converged\n", 1329 | " return v\n", 1330 | " else:\n", 1331 | " # if the search function didn't converge, try a bisection search\n", 1332 | " return _bisection_implied_vol(val_fn, option_type, fs, x, t, r, b, cp, precision, max_steps)\n" 1333 | ] 1334 | }, 1335 | { 1336 | "cell_type": "code", 1337 | "execution_count": 16, 1338 | "metadata": { 1339 | "collapsed": true 1340 | }, 1341 | "outputs": [], 1342 | "source": [ 1343 | "# ----------\n", 1344 | "# Find the Implied Volatility using a Bisection search\n", 1345 | "def _bisection_implied_vol(val_fn, option_type, fs, x, t, r, b, cp, precision=.00001, max_steps=100):\n", 1346 | " _debug(\"-----\")\n", 1347 | " _debug(\"Debug info for: _bisection_implied_vol()\")\n", 1348 | "\n", 1349 | " # Estimate Upper and Lower bounds on volatility\n", 1350 | " # Assume American Implied vol is within +/- 50% of the GBS Implied Vol\n", 1351 | " v_mid = _approx_implied_vol(option_type, fs, x, t, r, b, cp)\n", 1352 | "\n", 1353 | " if (v_mid <= _GBS_Limits.MIN_V) or (v_mid >= _GBS_Limits.MAX_V):\n", 1354 | " # if the volatility estimate is out of bounds, search entire allowed vol space\n", 1355 | " v_low = _GBS_Limits.MIN_V\n", 1356 | " v_high = _GBS_Limits.MAX_V\n", 1357 | " v_mid = (v_low + v_high) / 2\n", 1358 | " else:\n", 1359 | " # reduce the size of the vol space\n", 1360 | " v_low = max(_GBS_Limits.MIN_V, v_mid * .5)\n", 1361 | " v_high = min(_GBS_Limits.MAX_V, v_mid * 1.5)\n", 1362 | "\n", 1363 | " # Estimate the high/low bounds on price\n", 1364 | " cp_mid = val_fn(option_type, fs, x, t, r, b, v_mid)[0]\n", 1365 | " \n", 1366 | " # initialize bisection loop\n", 1367 | " current_step = 0\n", 1368 | " diff = abs(cp - cp_mid)\n", 1369 | "\n", 1370 | " _debug(\" American IVOL starting conditions: CP={0} cp_mid={1}\".format(cp, cp_mid))\n", 1371 | " _debug(\" IVOL {0}. V[{1},{2},{3}]\".format(current_step, v_low, v_mid, v_high))\n", 1372 | "\n", 1373 | " # Keep bisection volatility until correct price is found\n", 1374 | " while (diff > precision) and (current_step < max_steps):\n", 1375 | " current_step += 1\n", 1376 | "\n", 1377 | " # Cut the search area in half\n", 1378 | " if cp_mid < cp:\n", 1379 | " v_low = v_mid\n", 1380 | " else:\n", 1381 | " v_high = v_mid\n", 1382 | "\n", 1383 | " cp_low = val_fn(option_type, fs, x, t, r, b, v_low)[0]\n", 1384 | " cp_high = val_fn(option_type, fs, x, t, r, b, v_high)[0]\n", 1385 | "\n", 1386 | " v_mid = v_low + (cp - cp_low) * (v_high - v_low) / (cp_high - cp_low)\n", 1387 | " v_mid = max(_GBS_Limits.MIN_V, v_mid) # enforce high/low bounds\n", 1388 | " v_mid = min(_GBS_Limits.MAX_V, v_mid) # enforce high/low bounds\n", 1389 | "\n", 1390 | " cp_mid = val_fn(option_type, fs, x, t, r, b, v_mid)[0]\n", 1391 | " diff = abs(cp - cp_mid)\n", 1392 | "\n", 1393 | " _debug(\" IVOL {0}. V[{1},{2},{3}]\".format(current_step, v_low, v_mid, v_high))\n", 1394 | "\n", 1395 | " # return output\n", 1396 | " if abs(cp - cp_mid) < precision:\n", 1397 | " return v_mid\n", 1398 | " else:\n", 1399 | " raise GBS_CalculationError(\n", 1400 | " \"Implied Vol did not converge. Best Guess={0}, Price diff={1}, Required Precision={2}\".format(v_mid, diff,\n", 1401 | " precision))\n" 1402 | ] 1403 | }, 1404 | { 1405 | "cell_type": "markdown", 1406 | "metadata": {}, 1407 | "source": [ 1408 | "--------------------\n", 1409 | "### Public Interface for valuation functions\n", 1410 | "This section encapsulates the functions that user will call to value certain options. These function primarily figure out the cost-of-carry term (b) and then call the generic version of the function (like _GBS() or _American). All of these functions return an array containg the premium and the greeks.\n" 1411 | ] 1412 | }, 1413 | { 1414 | "cell_type": "code", 1415 | "execution_count": 17, 1416 | "metadata": { 1417 | "collapsed": true 1418 | }, 1419 | "outputs": [], 1420 | "source": [ 1421 | "# This is the public interface for European Options\n", 1422 | "# Each call does a little bit of processing and then calls the calculations located in the _gbs module\n", 1423 | "\n", 1424 | "# Inputs: \n", 1425 | "# option_type = \"p\" or \"c\"\n", 1426 | "# fs = price of underlying\n", 1427 | "# x = strike\n", 1428 | "# t = time to expiration\n", 1429 | "# v = implied volatility\n", 1430 | "# r = risk free rate\n", 1431 | "# q = dividend payment\n", 1432 | "# b = cost of carry\n", 1433 | "# Outputs: \n", 1434 | "# value = price of the option\n", 1435 | "# delta = first derivative of value with respect to price of underlying\n", 1436 | "# gamma = second derivative of value w.r.t price of underlying\n", 1437 | "# theta = first derivative of value w.r.t. time to expiration\n", 1438 | "# vega = first derivative of value w.r.t. implied volatility\n", 1439 | "# rho = first derivative of value w.r.t. risk free rates" 1440 | ] 1441 | }, 1442 | { 1443 | "cell_type": "code", 1444 | "execution_count": 18, 1445 | "metadata": { 1446 | "collapsed": true 1447 | }, 1448 | "outputs": [], 1449 | "source": [ 1450 | "# ---------------------------\n", 1451 | "# Black Scholes: stock Options (no dividend yield)\n", 1452 | "def black_scholes(option_type, fs, x, t, r, v):\n", 1453 | " b = r\n", 1454 | " return _gbs(option_type, fs, x, t, r, b, v)" 1455 | ] 1456 | }, 1457 | { 1458 | "cell_type": "code", 1459 | "execution_count": 19, 1460 | "metadata": { 1461 | "collapsed": true 1462 | }, 1463 | "outputs": [], 1464 | "source": [ 1465 | "# ---------------------------\n", 1466 | "# Merton Model: Stocks Index, stocks with a continuous dividend yields\n", 1467 | "def merton(option_type, fs, x, t, r, q, v):\n", 1468 | " b = r - q\n", 1469 | " return _gbs(option_type, fs, x, t, r, b, v)" 1470 | ] 1471 | }, 1472 | { 1473 | "cell_type": "code", 1474 | "execution_count": 20, 1475 | "metadata": { 1476 | "collapsed": true 1477 | }, 1478 | "outputs": [], 1479 | "source": [ 1480 | "# ---------------------------\n", 1481 | "# Commodities\n", 1482 | "def black_76(option_type, fs, x, t, r, v):\n", 1483 | " b = 0\n", 1484 | " return _gbs(option_type, fs, x, t, r, b, v)" 1485 | ] 1486 | }, 1487 | { 1488 | "cell_type": "code", 1489 | "execution_count": 21, 1490 | "metadata": { 1491 | "collapsed": true 1492 | }, 1493 | "outputs": [], 1494 | "source": [ 1495 | "# ---------------------------\n", 1496 | "# FX Options\n", 1497 | "def garman_kohlhagen(option_type, fs, x, t, r, rf, v):\n", 1498 | " b = r - rf\n", 1499 | " return _gbs(option_type, fs, x, t, r, b, v)" 1500 | ] 1501 | }, 1502 | { 1503 | "cell_type": "code", 1504 | "execution_count": 22, 1505 | "metadata": { 1506 | "collapsed": true 1507 | }, 1508 | "outputs": [], 1509 | "source": [ 1510 | "# ---------------------------\n", 1511 | "# Average Price option on commodities\n", 1512 | "def asian_76(option_type, fs, x, t, t_a, r, v):\n", 1513 | " # Check that TA is reasonable\n", 1514 | " if (t_a < _GBS_Limits.MIN_TA) or (t_a > t):\n", 1515 | " raise GBS_InputError(\n", 1516 | " \"Invalid Input Averaging Time (TA = {0}). Acceptable range for inputs is {1} to