├── S&P 500 Dartboard.xlsm ├── README.md ├── Black Scholes European Option.ipynb ├── CDS Spread.ipynb ├── Merton Structural Debt Valuation Model.ipynb ├── CDS Obtain Market Implied Probability of Default.ipynb ├── Black Scholes Merton European Options with Greeks.ipynb ├── Binomial Model - American Calls & Puts.ipynb ├── Barrier Option Valuation - Monte Carlo.ipynb ├── Random Number Generation.ipynb ├── Milstein Scheme - Monte Carlo Simulation.ipynb ├── Asian Option Valuation.ipynb ├── Binomial Model - European Calls & Puts.ipynb ├── Newton Raphson Root finding for Implied Volatility.ipynb ├── Explicit Finite Differences Method - Barrier Options.ipynb └── Explicit Finite Differences Method - Option Valuation.ipynb /S&P 500 Dartboard.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoryMagnuson/QUANT-TRAINING/HEAD/S&P 500 Dartboard.xlsm -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QUANT-TRAINING 2 | Quantitative Finance Training 3 | 4 | The goal is to create a repository of code for those who, like me, are leaning quantitative finance 5 | and writing python. 6 | 7 | Since I'm new to python, there will be opportunities to imporve the code and add to it. 8 | 9 | For those learning, hopefully these files will provide some tools. 10 | 11 | Best 12 | -------------------------------------------------------------------------------- /Black Scholes European Option.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import math\n", 13 | "import scipy as sp\n", 14 | "from scipy import stats" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "'''DEFINITION OF VARIABLES\n", 26 | " S0 - Stock Price at T=0\n", 27 | " E - Strike Price\n", 28 | " T - Time in Years\n", 29 | " R - Risk Free Rate\n", 30 | " SIGMA - Volatility\n", 31 | " DT - Time Step = T/N\n", 32 | " DF - Discount Factor = e^-RT\n", 33 | "'''\n", 34 | "\n", 35 | "S0 = 100\n", 36 | "E=100\n", 37 | "T=1\n", 38 | "R=0.05\n", 39 | "SIGMA=0.20" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": { 46 | "collapsed": true 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "'''BSM VANILLA EUROPEAN OPTION VALUE CALCULATION'''\n", 51 | "def bsm_option_value(S0, E, T, R, SIGMA): \n", 52 | " S0 = float(S0)\n", 53 | " d1 = (log(S0/E)+(R+0.05*SIGMA**2)*T)/(SIGMA*sqrt(T))\n", 54 | " d2 = d1-(SIGMA*sqrt(T))\n", 55 | " \n", 56 | " call_value = S0*stats.norm.cdf(d1,0,1) - E*exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 57 | " \n", 58 | " put_value = E*exp(-R*T)*stats.norm.cdf(-d2,0,1) - (S0*stats.norm.cdf(-d1,0,1))\n", 59 | "\n", 60 | " print(\"Value of Call Option BSM = \" + str(call_value))\n", 61 | " print(\"Value of Put Option BSM = \" + str(put_value))\n", 62 | " return" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": { 69 | "collapsed": false 70 | }, 71 | "outputs": [], 72 | "source": [ 73 | "bsm_option_value(S0, E, T, R, SIGMA)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": { 80 | "collapsed": true 81 | }, 82 | "outputs": [], 83 | "source": [] 84 | } 85 | ], 86 | "metadata": { 87 | "anaconda-cloud": {}, 88 | "kernelspec": { 89 | "display_name": "Python [Root]", 90 | "language": "python", 91 | "name": "Python [Root]" 92 | }, 93 | "language_info": { 94 | "codemirror_mode": { 95 | "name": "ipython", 96 | "version": 3 97 | }, 98 | "file_extension": ".py", 99 | "mimetype": "text/x-python", 100 | "name": "python", 101 | "nbconvert_exporter": "python", 102 | "pygments_lexer": "ipython3", 103 | "version": "3.5.2" 104 | } 105 | }, 106 | "nbformat": 4, 107 | "nbformat_minor": 0 108 | } 109 | -------------------------------------------------------------------------------- /CDS Spread.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "import math" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "Determine CDS Spread" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 2, 26 | "metadata": { 27 | "collapsed": true 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "'''\n", 32 | "R = Risk Free Rate\n", 33 | "T = Time to maturity in years\n", 34 | "dt = Payment Frequency in fraction of a year .25 = quarterly, .5 = semiannually\n", 35 | "N = Notional Amount\n", 36 | "Lam = Hazard Rate\n", 37 | "RR = Recovery Rate\n", 38 | "'''\n", 39 | "R = .05\n", 40 | "T = 5\n", 41 | "dt = .25\n", 42 | "N = 1000000\n", 43 | "Lam = .03\n", 44 | "RR = .40" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 4, 50 | "metadata": { 51 | "collapsed": false 52 | }, 53 | "outputs": [ 54 | { 55 | "name": "stdout", 56 | "output_type": "stream", 57 | "text": [ 58 | "PV of Premium Leg = 4,079,926.80\n", 59 | "PV of Default Leg = 73,714.77\n", 60 | "Premium - CDS Spread = 0.0181\n" 61 | ] 62 | } 63 | ], 64 | "source": [ 65 | "#Create empty data frame and create index by period\n", 66 | "df = pd.DataFrame()\n", 67 | "index = np.arange(0,T+dt,dt)\n", 68 | "df['period'] = index\n", 69 | "df = df.set_index(index)\n", 70 | "\n", 71 | "#Set initial variables in the dataframe\n", 72 | "df['lambda'] = Lam\n", 73 | "df['Notional'] = N\n", 74 | "\n", 75 | "#Calculate probability of survival and probability of default for given hazard rate\n", 76 | "df['P_Survival'] = np.exp(df['period']*-df['lambda'])\n", 77 | "df['P_Default'] = df['P_Survival'].shift(1) - df['P_Survival']\n", 78 | "df.loc[0,'P_Default'] = 0\n", 79 | "\n", 80 | "#Calculate discount factors for cash flows\n", 81 | "df['disc_factor'] = np.exp(-R * df['period'])\n", 82 | "\n", 83 | "#Calculate the cash flows related to the premium and default legs of the CDS\n", 84 | "df['premium_leg'] = df['Notional'] * df['disc_factor'] * df['P_Survival'] * dt\n", 85 | "df.loc[0,'premium_leg'] = 0\n", 86 | "df['default_leg'] = df['Notional'] * (1-RR) * df['P_Default'] * df['disc_factor']\n", 87 | "\n", 88 | "pv_premium_leg = df['premium_leg'].sum()\n", 89 | "print(\"PV of Premium Leg = {:,.2f}\".format(pv_premium_leg))\n", 90 | "pv_default_leg = df['default_leg'].sum()\n", 91 | "print(\"PV of Default Leg = {:,.2f}\".format(pv_default_leg))\n", 92 | "\n", 93 | "premium = pv_default_leg / pv_premium_leg\n", 94 | "print(\"Premium - CDS Spread = %.4f\" %premium)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": { 101 | "collapsed": true 102 | }, 103 | "outputs": [], 104 | "source": [] 105 | } 106 | ], 107 | "metadata": { 108 | "kernelspec": { 109 | "display_name": "Python [Root]", 110 | "language": "python", 111 | "name": "Python [Root]" 112 | }, 113 | "language_info": { 114 | "codemirror_mode": { 115 | "name": "ipython", 116 | "version": 3 117 | }, 118 | "file_extension": ".py", 119 | "mimetype": "text/x-python", 120 | "name": "python", 121 | "nbconvert_exporter": "python", 122 | "pygments_lexer": "ipython3", 123 | "version": "3.5.2" 124 | } 125 | }, 126 | "nbformat": 4, 127 | "nbformat_minor": 0 128 | } 129 | -------------------------------------------------------------------------------- /Merton Structural Debt Valuation Model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import math\n", 13 | "import scipy as sp\n", 14 | "from scipy import stats\n", 15 | "import numpy as np\n", 16 | "import matplotlib.pyplot as plt" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": { 23 | "collapsed": true 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "'''\n", 28 | "DEFINITION OF VARIABLES\n", 29 | " V0 - Total Firm Value (Equity + Debt) at T=0\n", 30 | " D - Value of Debt at time T\n", 31 | " T - Time in Years\n", 32 | " R - Risk Free Rate\n", 33 | " SIGMA - Volatility of Total Firm Value\n", 34 | "'''\n", 35 | "\n", 36 | "V0 = 100\n", 37 | "D = 70\n", 38 | "T = 4\n", 39 | "R = 0.05\n", 40 | "SIGMA = 0.20" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 7, 46 | "metadata": { 47 | "collapsed": false 48 | }, 49 | "outputs": [], 50 | "source": [ 51 | "'''Calculation of Debt and Equity Value'''\n", 52 | "def merton_structural_valuation(V0, D, T, R, SIGMA): \n", 53 | " V0 = float(V0)\n", 54 | " d1 = (math.log(V0/D)+(R+(0.5*SIGMA**2))*T)/(SIGMA*math.sqrt(T))\n", 55 | " d2 = d1-(SIGMA*math.sqrt(T))\n", 56 | " Equity_value = V0*stats.norm.cdf(d1,0,1) - D*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 57 | " Debt_value = V0 - Equity_value\n", 58 | " return Equity_value, Debt_value, d2" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 8, 64 | "metadata": { 65 | "collapsed": false 66 | }, 67 | "outputs": [ 68 | { 69 | "name": "stdout", 70 | "output_type": "stream", 71 | "text": [ 72 | "Value of Equity = 43.8038 \n", 73 | "Value of Debt = 56.1962\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "Equity_value, Debt_value, d2 = merton_structural_valuation(V0, D, T, R, SIGMA)\n", 79 | "print(\"Value of Equity = %.4f \" %Equity_value)\n", 80 | "print(\"Value of Debt = %.4f\" %Debt_value)" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 10, 86 | "metadata": { 87 | "collapsed": false 88 | }, 89 | "outputs": [ 90 | { 91 | "name": "stdout", 92 | "output_type": "stream", 93 | "text": [ 94 | "Yield on Firm's Debt = 0.0549 \n", 95 | "Risk Premium = 0.0049\n", 96 | "Default Probability = 0.1167\n", 97 | "Survival Probability = 0.8833\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "#Calculation of yeild on the firm's debt\n", 103 | "debt_yield = math.log(Debt_value/D) * (-1/T)\n", 104 | "risk_premium = debt_yield - R\n", 105 | "default_probability = stats.norm.cdf(-d2,0,1)\n", 106 | "survival_probability = 1 - default_probability\n", 107 | "\n", 108 | "print(\"Yield on Firm's Debt = %.4f \" %debt_yield)\n", 109 | "print(\"Risk Premium = %.4f\" %risk_premium)\n", 110 | "print(\"Default Probability = %.4f\" %default_probability)\n", 111 | "print(\"Survival Probability = %.4f\" %survival_probability)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": { 118 | "collapsed": true 119 | }, 120 | "outputs": [], 121 | "source": [] 122 | } 123 | ], 124 | "metadata": { 125 | "anaconda-cloud": {}, 126 | "kernelspec": { 127 | "display_name": "Python [Root]", 128 | "language": "python", 129 | "name": "Python [Root]" 130 | }, 131 | "language_info": { 132 | "codemirror_mode": { 133 | "name": "ipython", 134 | "version": 3 135 | }, 136 | "file_extension": ".py", 137 | "mimetype": "text/x-python", 138 | "name": "python", 139 | "nbconvert_exporter": "python", 140 | "pygments_lexer": "ipython3", 141 | "version": "3.5.2" 142 | } 143 | }, 144 | "nbformat": 4, 145 | "nbformat_minor": 0 146 | } 147 | -------------------------------------------------------------------------------- /CDS Obtain Market Implied Probability of Default.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 5, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "import math" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "Calculate implied probability of default from CDS market spreads" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 6, 26 | "metadata": { 27 | "collapsed": true 28 | }, 29 | "outputs": [], 30 | "source": [ 31 | "#Determining the implied probability of default in CDS from observed market spreads\n", 32 | "def CDS_Implied_PD(R, T, dt, N, RR, Lam, Mkt_Spread):\n", 33 | " #Create empty data frame and create index by period\n", 34 | " df= pd.DataFrame()\n", 35 | " index = np.arange(0,T+dt,dt)\n", 36 | " df['period'] = index\n", 37 | " df = df.set_index(index)\n", 38 | " df['Notional'] = N\n", 39 | " df['disc_factor'] = np.exp(-R * df['period'])\n", 40 | " \n", 41 | " #Create column to hold the guess for the implied hazard rate\n", 42 | " df['lambda'] = Lam\n", 43 | " \n", 44 | " #Calculate probability of survival and probability of default based on hazard rate guess\n", 45 | " df['P_Survival'] = np.exp(df['period']*-df['lambda'])\n", 46 | " df['P_Default'] = df['P_Survival'].shift(1) - df['P_Survival']\n", 47 | " df.loc[0,'P_Default'] = 0\n", 48 | "\n", 49 | " df['premium_leg'] = df['Notional'] * df['disc_factor'] * Mkt_Spread * dt *df['P_Survival']\n", 50 | " df.loc[0,'premium_leg'] = 0\n", 51 | " df['default_leg'] = df['Notional'] * (1-RR) * df['P_Default'] * df['disc_factor']\n", 52 | " pv_premium_leg = df['premium_leg'].sum()\n", 53 | " pv_default_leg = df['default_leg'].sum()\n", 54 | " mtm = pv_default_leg - pv_premium_leg\n", 55 | " return mtm, Lam, df" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 7, 61 | "metadata": { 62 | "collapsed": true 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "def CDS_root_find(R, T, dt, N, RR, Mkt_Spread): \n", 67 | " #Calculation of implied hazard rate\n", 68 | " Lam = 0.1 #Initial Estimate for Lambda\n", 69 | " count = 100000 #number of attempts to find value\n", 70 | " mtm, Lam, df = CDS_Implied_PD(R, T, dt, N, RR, Lam, Mkt_Spread)\n", 71 | " while abs(mtm) > (.0001*N) and count > 0:\n", 72 | " if mtm > (.0001 * N):\n", 73 | " Lam = Lam -.0001\n", 74 | " else:\n", 75 | " Lam = Lam + .0001\n", 76 | " mtm, Lam, df1 = CDS_Implied_PD(R, T, dt, N, RR, Lam, Mkt_Spread) \n", 77 | " count -= 1 \n", 78 | " return mtm, Lam, count" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 8, 84 | "metadata": { 85 | "collapsed": false 86 | }, 87 | "outputs": [ 88 | { 89 | "name": "stdout", 90 | "output_type": "stream", 91 | "text": [ 92 | "The implied survival probability is 0.8954\n", 93 | "The implied default probability is 0.1046\n" 94 | ] 95 | } 96 | ], 97 | "source": [ 98 | "#Run functions to determine implied probability of default\n", 99 | "#input contract variables and CDS market spread\n", 100 | "'''\n", 101 | "R = Risk Free Rate\n", 102 | "T = Time to maturity in years\n", 103 | "dt = Payment Frequency in fraction of a year .25 = quarterly, .5 = semiannually\n", 104 | "N = Notional Amount\n", 105 | "RR = Recovery Rate\n", 106 | "Mkt_Spread = Observed CDS Spread in %\n", 107 | "'''\n", 108 | "R = .05\n", 109 | "T = 5\n", 110 | "dt = .25\n", 111 | "N = 1000000\n", 112 | "RR = .40\n", 113 | "Mkt_Spread = .0133\n", 114 | "\n", 115 | "mtm, Lam, count = CDS_root_find(R, T, dt, N, RR, Mkt_Spread)\n", 116 | "implied_survival_probability = math.exp(-Lam * T)\n", 117 | "\n", 118 | "print(\"The implied survival probability is %.4f\" %implied_survival_probability)\n", 119 | "print(\"The implied default probability is %.4f\" %(1-implied_survival_probability))" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": { 126 | "collapsed": true 127 | }, 128 | "outputs": [], 129 | "source": [] 130 | } 131 | ], 132 | "metadata": { 133 | "anaconda-cloud": {}, 134 | "kernelspec": { 135 | "display_name": "Python [Root]", 136 | "language": "python", 137 | "name": "Python [Root]" 138 | }, 139 | "language_info": { 140 | "codemirror_mode": { 141 | "name": "ipython", 142 | "version": 3 143 | }, 144 | "file_extension": ".py", 145 | "mimetype": "text/x-python", 146 | "name": "python", 147 | "nbconvert_exporter": "python", 148 | "pygments_lexer": "ipython3", 149 | "version": "3.5.2" 150 | } 151 | }, 152 | "nbformat": 4, 153 | "nbformat_minor": 0 154 | } 155 | -------------------------------------------------------------------------------- /Black Scholes Merton European Options with Greeks.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import math\n", 13 | "import scipy as sp\n", 14 | "from scipy import stats" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "'''DEFINITION OF VARIABLES\n", 26 | " S0 - Stock Price at T=0\n", 27 | " E - Strike Price\n", 28 | " T - Time in Years\n", 29 | " R - Risk Free Rate\n", 30 | " SIGMA - Volatility\n", 31 | " DT - Time Step = T/N\n", 32 | " DF - Discount Factor = e^-RT\n", 33 | "'''\n", 34 | "\n", 35 | "S0 = 100\n", 36 | "E=100\n", 37 | "T=1\n", 38 | "R=0.05\n", 39 | "SIGMA=0.20" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 3, 45 | "metadata": { 46 | "collapsed": false 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "'''BSM VANILLA EUROPEAN OPTION VALUE CALCULATION'''\n", 51 | "def bsm_option_value(S0, E, T, R, SIGMA): \n", 52 | " S0 = float(S0)\n", 53 | " d1 = (math.log(S0/E)+(R+(0.5*SIGMA**2))*T)/(SIGMA*math.sqrt(T))\n", 54 | " d2 = d1-(SIGMA*math.sqrt(T))\n", 55 | " call_value = S0*stats.norm.cdf(d1,0,1) - E*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 56 | " delta_call = stats.norm.cdf(d1,0,1)\n", 57 | " gamma_call = stats.norm.pdf(d1,0,1)/(S0*SIGMA*math.sqrt(T))\n", 58 | " theta_call = -(R*E*math.exp(-R*T)*stats.norm.cdf(d2,0,1))-(SIGMA*S0*stats.norm.pdf(d1,0,1)/(2*math.sqrt(T)))\n", 59 | " rho_call = T*E*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 60 | " vega_call = math.sqrt(T)*S0*stats.norm.pdf(d1,0,1)\n", 61 | " \n", 62 | " put_value = E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1) - (S0*stats.norm.cdf(-d1,0,1))\n", 63 | " delta_put = -stats.norm.cdf(-d1,0,1)\n", 64 | " gamma_put = stats.norm.pdf(d1,0,1)/(S0*SIGMA*math.sqrt(T))\n", 65 | " theta_put = (R*E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1))-(SIGMA*S0*stats.norm.pdf(d1,0,1)/(2*math.sqrt(T)))\n", 66 | " rho_put = -T*E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1)\n", 67 | " vega_put = math.sqrt(T)*S0*stats.norm.pdf(d1,0,1)\n", 68 | " \n", 69 | " return call_value, delta_call, gamma_call, theta_call, rho_call, vega_call, put_value, delta_put, gamma_put, theta_put, rho_put, vega_put" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 4, 75 | "metadata": { 76 | "collapsed": false 77 | }, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "Value of Call Option BSM = 10.4506 \n", 84 | "Value of Put Option BSM = 5.5735\n" 85 | ] 86 | } 87 | ], 88 | "source": [ 89 | "call_value, delta_call, gamma_call, theta_call, rho_call, vega_call, put_value, delta_put, gamma_put, theta_put, rho_put, vega_put = bsm_option_value(S0, E, T, R, SIGMA)\n", 90 | "print(\"Value of Call Option BSM = %.4f \" %call_value)\n", 91 | "print(\"Value of Put Option BSM = %.4f\" %put_value)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 5, 97 | "metadata": { 98 | "collapsed": false 99 | }, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "Call Delta = 0.6368 \n", 106 | "Call Gamma = 0.0188 \n", 107 | "Call Theta = -6.4140 \n", 108 | "Call Rho = 53.2325 \n", 109 | "Call Vega = 37.5240 \n", 110 | "Put Delta = -0.3632 \n", 111 | "Put Gamma = 0.0188 \n", 112 | "Put Theta = -1.6579 \n", 113 | "Put Rho = -41.8905 \n", 114 | "Put Vega = 37.5240 \n" 115 | ] 116 | } 117 | ], 118 | "source": [ 119 | "#Calculation of Greeks for Call\n", 120 | "call_value, delta_call, gamma_call, theta_call, rho_call, vega_call, put_value, delta_put, gamma_put, theta_put, rho_put, vega_put = bsm_option_value(S0, E, T, R, SIGMA)\n", 121 | "\n", 122 | "print(\"Call Delta = %.4f \" %delta_call)\n", 123 | "print(\"Call Gamma = %.4f \" %gamma_call)\n", 124 | "print(\"Call Theta = %.4f \" %theta_call)\n", 125 | "print(\"Call Rho = %.4f \" %rho_call)\n", 126 | "print(\"Call Vega = %.4f \" %vega_call)\n", 127 | "print(\"Put Delta = %.4f \" %delta_put)\n", 128 | "print(\"Put Gamma = %.4f \" %gamma_put)\n", 129 | "print(\"Put Theta = %.4f \" %theta_put)\n", 130 | "print(\"Put Rho = %.4f \" %rho_put)\n", 131 | "print(\"Put Vega = %.4f \" %vega_put)\n" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": { 138 | "collapsed": true 139 | }, 140 | "outputs": [], 141 | "source": [] 142 | } 143 | ], 144 | "metadata": { 145 | "anaconda-cloud": {}, 146 | "kernelspec": { 147 | "display_name": "Python [Root]", 148 | "language": "python", 149 | "name": "Python [Root]" 150 | }, 151 | "language_info": { 152 | "codemirror_mode": { 153 | "name": "ipython", 154 | "version": 3 155 | }, 156 | "file_extension": ".py", 157 | "mimetype": "text/x-python", 158 | "name": "python", 159 | "nbconvert_exporter": "python", 160 | "pygments_lexer": "ipython3", 161 | "version": "3.5.2" 162 | } 163 | }, 164 | "nbformat": 4, 165 | "nbformat_minor": 0 166 | } 167 | -------------------------------------------------------------------------------- /Binomial Model - American Calls & Puts.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import math\n", 13 | "import xlwings as xw" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": { 20 | "collapsed": false 21 | }, 22 | "outputs": [ 23 | { 24 | "data": { 25 | "text/plain": [ 26 | "(1.0253151205244289, 0.9753099120283326, 1.0005001250208359, 0.503751784066049)" 27 | ] 28 | }, 29 | "execution_count": 2, 30 | "metadata": {}, 31 | "output_type": "execute_result" 32 | } 33 | ], 34 | "source": [ 35 | "#Variables\n", 36 | "S = 100 #Stock price at t=0\n", 37 | "r = 0.05 #Risk free rate\n", 38 | "dt = .01 #Timestep size\n", 39 | "T = 1 #time to expiry in years\n", 40 | "sigma = .250\n", 41 | "E = 100 #Strike Price\n", 42 | "\n", 43 | "length = T/dt\n", 44 | "u = math.exp( sigma * math.sqrt(dt))\n", 45 | "d = math.exp(- sigma * math.sqrt(dt))\n", 46 | "a = math.exp( r * dt)\n", 47 | "p = (a - d) / (u - d)\n", 48 | "\n", 49 | "u,d,a,p" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 3, 55 | "metadata": { 56 | "collapsed": false 57 | }, 58 | "outputs": [], 59 | "source": [ 60 | "#Create Empty Numpy Arrays to hold the data\n", 61 | "step_matrix = np.zeros((int(length), int(length+1)))\n", 62 | "call_option_value_matrix = np.zeros((int(length+1), int(length+1)))\n", 63 | "put_option_value_matrix = np.zeros((int(length+1), int(length+1)))\n", 64 | "\n", 65 | "stock_value_matrix = np.zeros((int(length+1), int(length+1)))\n", 66 | "#Set initial value for the stock\n", 67 | "stock_value_matrix[0,0] = S" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 4, 73 | "metadata": { 74 | "collapsed": false 75 | }, 76 | "outputs": [], 77 | "source": [ 78 | "#Create binomial tree of the pricing of the underlying\n", 79 | "for x in range(1,int(length+1)):\n", 80 | " stock_value_matrix[x,0] = stock_value_matrix[x-1,0]*(d)\n", 81 | " for y in range(1,int(length+1)):\n", 82 | " stock_value_matrix[x,y] = stock_value_matrix[x-1,y-1]*(u)\n", 83 | "#xw.view(value_matrix)\n", 84 | "#stock_value_matrix" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 5, 90 | "metadata": { 91 | "collapsed": false 92 | }, 93 | "outputs": [], 94 | "source": [ 95 | "#Create Option Valuation Tree Call Option\n", 96 | "for x in range(1, int(length+1)):\n", 97 | " for y in range(0, int(length+1)):\n", 98 | " if stock_value_matrix[-x , y] - E > 0:\n", 99 | " call_option_value_matrix[-x, y] = stock_value_matrix[-x , y] - E\n", 100 | " else:\n", 101 | " call_option_value_matrix[-x , y] = 0\n", 102 | "#xw.view(call_option_value_matrix)\n", 103 | " \n", 104 | "#Create Option Valuation Tree Put Option\n", 105 | "for x in range(1, int(length+1)):\n", 106 | " for y in range(0, int(length+1)):\n", 107 | " if E - stock_value_matrix[-x , y] > 0:\n", 108 | " put_option_value_matrix[-x, y] = E - stock_value_matrix[-x , y]\n", 109 | " else:\n", 110 | " put_option_value_matrix[-x , y] = 0" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 6, 116 | "metadata": { 117 | "collapsed": false 118 | }, 119 | "outputs": [ 120 | { 121 | "name": "stdout", 122 | "output_type": "stream", 123 | "text": [ 124 | "Value of Call Option Binomial = 12.3113 \n", 125 | "Value of Put Option Binomial = 7.9636 \n" 126 | ] 127 | } 128 | ], 129 | "source": [ 130 | "call_payoff_matrix = np.zeros((int(length+1), int(length+1)))\n", 131 | "call_payoff_matrix[-1] = call_option_value_matrix[-1]\n", 132 | "count = 0\n", 133 | "for x in range(1,int(length+1)):\n", 134 | " count += 1\n", 135 | " for y in range(count, int(length+1)):\n", 136 | " call_payoff_matrix[-x-1,-y-1] = math.exp(-r*dt)*((p*call_payoff_matrix[-x,-y])+((1-p)*call_payoff_matrix[-x,-y-1]))\n", 137 | " if call_payoff_matrix[-x-1,-y-1] < call_option_value_matrix[-x-1,-y-1]:\n", 138 | " call_payoff_matrix[-x-1,-y-1] = call_option_value_matrix[-x-1,-y-1]\n", 139 | " \n", 140 | "put_payoff_matrix = np.zeros((int(length+1), int(length+1)))\n", 141 | "put_payoff_matrix[-1] = put_option_value_matrix[-1]\n", 142 | "count = 0\n", 143 | "for x in range(1,int(length+1)):\n", 144 | " count += 1\n", 145 | " for y in range(count, int(length+1)):\n", 146 | " put_payoff_matrix[-x-1,-y-1] = math.exp(-r*dt)*((p*put_payoff_matrix[-x,-y])+((1-p)*put_payoff_matrix[-x,-y-1]))\n", 147 | " if put_payoff_matrix[-x-1,-y-1] < put_option_value_matrix[-x-1,-y-1]:\n", 148 | " put_payoff_matrix[-x-1,-y-1] = put_option_value_matrix[-x-1,-y-1]\n", 149 | "#xw.view(put_payoff_matrix)\n", 150 | "xw.view(call_payoff_matrix)\n", 151 | "call_value = call_payoff_matrix[0,0]\n", 152 | "put_value = put_payoff_matrix[0,0]\n", 153 | "\n", 154 | "print(\"Value of Call Option Binomial = %.4f \" %call_value)\n", 155 | "print(\"Value of Put Option Binomial = %.4f \" %put_value)" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": { 162 | "collapsed": true 163 | }, 164 | "outputs": [], 165 | "source": [] 166 | } 167 | ], 168 | "metadata": { 169 | "anaconda-cloud": {}, 170 | "kernelspec": { 171 | "display_name": "Python [Root]", 172 | "language": "python", 173 | "name": "Python [Root]" 174 | }, 175 | "language_info": { 176 | "codemirror_mode": { 177 | "name": "ipython", 178 | "version": 3 179 | }, 180 | "file_extension": ".py", 181 | "mimetype": "text/x-python", 182 | "name": "python", 183 | "nbconvert_exporter": "python", 184 | "pygments_lexer": "ipython3", 185 | "version": "3.5.2" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 0 190 | } 191 | -------------------------------------------------------------------------------- /Barrier Option Valuation - Monte Carlo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import numpy as np\n", 13 | "import numpy.random as npr\n", 14 | "import math\n", 15 | "import matplotlib.pyplot as plt\n", 16 | "import scipy as sp\n", 17 | "from scipy import stats\n", 18 | "import xlwings as xw" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": { 25 | "collapsed": true 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "'''DEFINITION OF VARIABLES\n", 30 | " S0 - Stock Price at T=0\n", 31 | " E - Strike Price\n", 32 | " T - Time in Years\n", 33 | " R - Risk Free Rate\n", 34 | " SIGMA - Volatility\n", 35 | " DT - Time Step = T/N\n", 36 | " DF - Discount Factor = e^-RT\n", 37 | " I - Number of Simulations\n", 38 | "'''\n", 39 | "\n", 40 | "S0 = 100\n", 41 | "Barrier = 80\n", 42 | "Optionality = \"In\" #Out = Knock Out Option In = Knock In Option\n", 43 | "Type = \"P\" #C = Call P = Put\n", 44 | "\n", 45 | "E=100\n", 46 | "T=1\n", 47 | "R=0.05\n", 48 | "SIGMA=0.20\n", 49 | "I = 1000\n", 50 | "N=252" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": { 57 | "collapsed": true 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "'''OPTION VALUATION - MONTE CARLO SIMULATION W/ ANTITHETIC VARIANCE REDUCTION W/ MILSTEIN SCHEME '''\n", 62 | "def option_valuation(S0, E, T, N, SIGMA, R, I, P): \n", 63 | " DT = T/N #Time Step \n", 64 | "#GENERATE RANDOM NUMBERS - ANTITHETIC VARIANCE REDUCTION\n", 65 | " PHI = npr.standard_normal((N,int(I/2))) \n", 66 | " PHI = np.concatenate((PHI, -PHI), axis=1) \n", 67 | "#SET UP EMPTY ARRAYS AND SET INITIAL VALUES \n", 68 | " S = np.zeros_like(PHI) #Array to Capture Asset Value Path\n", 69 | " S[0] = S0\n", 70 | " \n", 71 | "#CREATE FOR LOOP TO GENERATE SIMULATION PATHS - MILSTEIN METHOD\n", 72 | " for t in range (1,N):\n", 73 | " S[t]=S[t-1]*(1+R*DT+(SIGMA*PHI[t]*np.sqrt(DT))+(SIGMA**2)*(0.5*(((PHI[t]**2)-1)*DT)))\n", 74 | " \n", 75 | " return S" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": { 82 | "collapsed": false 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "#Run full simulation\n", 87 | "S = option_valuation(S0, E, T, N, SIGMA, R, I, P)\n", 88 | "S[1,1]\n", 89 | "#You can un-comment the xw.view to get the sim results in excel\n", 90 | "#xw.view(S)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": { 97 | "collapsed": false 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "#Evaluation of up and out call\n", 102 | "#If at any point in the simulation path the underlying value does reach the barrier, the value of the path is zero.\n", 103 | "if Type == \"C\" and Optionality == \"Out\":\n", 104 | " for t in range(0,I-1):\n", 105 | " max = np.max(S[:,t])\n", 106 | " if max > Barrier:\n", 107 | " S[:,t]=0\n", 108 | "\n", 109 | "#Evaluation of down and out put\n", 110 | "#If at any point in the simulation path the underlying value does reach the barrier, the value of the path is zero.\n", 111 | "if Type == \"P\" and Optionality == \"Out\":\n", 112 | " for t in range(0,I-1):\n", 113 | " min = np.min(S[:,t])\n", 114 | " if min < Barrier:\n", 115 | " S[:,t]=0\n", 116 | "\n", 117 | "#Evaluation of up and in call\n", 118 | "#If at any point in the simulation path the underlying value does NOT reach the barrier, the value of the path is zero.\n", 119 | "if Type == \"C\" and Optionality == \"In\":\n", 120 | " for t in range(0,I-1):\n", 121 | " max = np.max(S[:,t])\n", 122 | " if max < Barrier:\n", 123 | " S[:,t]=0\n", 124 | "\n", 125 | "#Evaluation of down and in put\n", 126 | "#If at any point in the simulation path the underlying value does NOT reach the barrier, the value of the path is zero.\n", 127 | "if Type == \"P\" and Optionality == \"In\":\n", 128 | " for t in range(0,I-1):\n", 129 | " min = np.min(S[:,t])\n", 130 | " if min > Barrier:\n", 131 | " S[:,t]= 0 \n", 132 | "\n", 133 | "#You can un-comment the xw.view to get the sim results (after adjusting for barrier) in excel\n", 134 | "#xw.view(S)" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "metadata": { 141 | "collapsed": false 142 | }, 143 | "outputs": [], 144 | "source": [ 145 | "%matplotlib inline\n", 146 | "#Calculation of Discounted Expected Payoff for Options\n", 147 | "\n", 148 | "DF = math.exp(-R*T) #Discount Factor\n", 149 | "\n", 150 | "print(\"Number of Simulations %.d\" %I)\n", 151 | "\n", 152 | "if Type == \"C\":\n", 153 | " Call_Value = DF * np.sum(np.maximum(S[-1] - E, 0)) / I\n", 154 | " print( \"Value of Call Option Monte Carlo = %.3f\" %Call_Value)\n", 155 | "\n", 156 | "if Type == \"P\":\n", 157 | " Put_Value = np.maximum(E - S[-1], 0)\n", 158 | " Put_Value[Put_Value == E] = 0\n", 159 | " Put_Value = DF * np.sum(Put_Value) / I\n", 160 | " \n", 161 | " print( \"Value of Put Option Monte Carlo = %.3f\" %Put_Value)\n", 162 | "\n", 163 | "#Create Graph of Monte Carlo Simulation\n", 164 | "plt.plot(S)\n", 165 | "plt.show()" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": { 172 | "collapsed": true 173 | }, 174 | "outputs": [], 175 | "source": [] 176 | } 177 | ], 178 | "metadata": { 179 | "anaconda-cloud": {}, 180 | "kernelspec": { 181 | "display_name": "Python [Root]", 182 | "language": "python", 183 | "name": "Python [Root]" 184 | }, 185 | "language_info": { 186 | "codemirror_mode": { 187 | "name": "ipython", 188 | "version": 3 189 | }, 190 | "file_extension": ".py", 191 | "mimetype": "text/x-python", 192 | "name": "python", 193 | "nbconvert_exporter": "python", 194 | "pygments_lexer": "ipython3", 195 | "version": "3.5.2" 196 | } 197 | }, 198 | "nbformat": 4, 199 | "nbformat_minor": 0 200 | } 201 | -------------------------------------------------------------------------------- /Random Number Generation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 39, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import numpy as np\n", 13 | "import numpy.random as npr\n", 14 | "import time" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 40, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "'''DEFINITION OF VARIABLES\n", 26 | " I - Number of Simulations\n", 27 | " N - Number of Timesteps\n", 28 | "'''\n", 29 | "\n", 30 | "I = 100000\n", 31 | "N = 252" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "Antithetic Variance Reduction\n", 39 | "\n", 40 | "Generates only half of the random numbers needed, then appends the negative values to the array\n", 41 | "Decreases the amount of time needed to generate the random numbers for simulations" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 41, 47 | "metadata": { 48 | "collapsed": false 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "def rand_num_anti(N, I): #Random Numbers using Antithetic Variance Reduction Technique\n", 53 | " PHI = npr.standard_normal((N+1,int(I/2))) #RANDOM NUMBER\n", 54 | " PHI = np.concatenate((PHI, -PHI), axis=1)\n", 55 | " return PHI" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "Moment Matching\n", 63 | "\n", 64 | "Adjusts the random numbers to normalize them to exactly mean = 0, standard deviation = 1" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 42, 70 | "metadata": { 71 | "collapsed": true 72 | }, 73 | "outputs": [], 74 | "source": [ 75 | "def rand_num_mm(N, I): #Random Numbers with Moment Matching\n", 76 | " PHI = npr.standard_normal((N,I))\n", 77 | " PHI = (PHI - PHI.mean()) / PHI.std()\n", 78 | " return PHI" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "Numpy random number generation w/o adjustment" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 43, 91 | "metadata": { 92 | "collapsed": true 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "def rand_num(N, I): #Random Numbers w/o Antithetic Variance Reduction\n", 97 | " PHI = npr.standard_normal((N,I)) #RANDOM NUMBER \n", 98 | " return PHI" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "Comparison" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 44, 111 | "metadata": { 112 | "collapsed": false 113 | }, 114 | "outputs": [ 115 | { 116 | "name": "stdout", 117 | "output_type": "stream", 118 | "text": [ 119 | "mean is -0.00000\n", 120 | "standard deviation is 1.00012\n", 121 | "mean is -0.00000\n", 122 | "standard deviation is 0.99996\n", 123 | "mean is -0.00000\n", 124 | "standard deviation is 1.00007\n", 125 | "mean is 0.00000\n", 126 | "standard deviation is 0.99997\n", 127 | "1 loop, best of 3: 1.02 s per loop\n" 128 | ] 129 | } 130 | ], 131 | "source": [ 132 | "%%timeit\n", 133 | "PHI = rand_num_anti(N, I)\n", 134 | "print(\"mean is %.5f\" %PHI.mean())\n", 135 | "print(\"standard deviation is %.5f\" %PHI.std())" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 45, 141 | "metadata": { 142 | "collapsed": false 143 | }, 144 | "outputs": [ 145 | { 146 | "name": "stdout", 147 | "output_type": "stream", 148 | "text": [ 149 | "mean is -0.00000\n", 150 | "standard deviation is 1.00000\n", 151 | "mean is 0.00000\n", 152 | "standard deviation is 1.00000\n", 153 | "mean is -0.00000\n", 154 | "standard deviation is 1.00000\n", 155 | "mean is -0.00000\n", 156 | "standard deviation is 1.00000\n", 157 | "1 loop, best of 3: 2.12 s per loop\n" 158 | ] 159 | } 160 | ], 161 | "source": [ 162 | "%%timeit\n", 163 | "PHI = rand_num_mm(N, I)\n", 164 | "print(\"mean is %.5f\" %PHI.mean())\n", 165 | "print(\"standard deviation is %.5f\" %PHI.std())" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 46, 171 | "metadata": { 172 | "collapsed": false 173 | }, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "mean is -0.00001\n", 180 | "standard deviation is 1.00012\n", 181 | "mean is 0.00022\n", 182 | "standard deviation is 1.00026\n", 183 | "mean is 0.00009\n", 184 | "standard deviation is 0.99988\n", 185 | "mean is -0.00007\n", 186 | "standard deviation is 1.00002\n", 187 | "1 loop, best of 3: 1.45 s per loop\n" 188 | ] 189 | } 190 | ], 191 | "source": [ 192 | "%%timeit\n", 193 | "PHI = rand_num(N, I)\n", 194 | "print(\"mean is %.5f\" %PHI.mean())\n", 195 | "print(\"standard deviation is %.5f\" %PHI.std())" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "Result:\n", 203 | "\n", 204 | "Moment matching - mean and std dev always (0,1), but adds run time\n", 205 | "\n", 206 | "Antithetic variance reduction - makes mean always 0, with less error in std dev than the normal numpy random number generation, run time is lowest of the three methods\n", 207 | "\n", 208 | "Numpy random number generation without adjustment - larger error in mean and std dev with a run time in between the other two methods" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "collapsed": true 216 | }, 217 | "outputs": [], 218 | "source": [] 219 | } 220 | ], 221 | "metadata": { 222 | "kernelspec": { 223 | "display_name": "Python [Root]", 224 | "language": "python", 225 | "name": "Python [Root]" 226 | }, 227 | "language_info": { 228 | "codemirror_mode": { 229 | "name": "ipython", 230 | "version": 3 231 | }, 232 | "file_extension": ".py", 233 | "mimetype": "text/x-python", 234 | "name": "python", 235 | "nbconvert_exporter": "python", 236 | "pygments_lexer": "ipython3", 237 | "version": "3.5.2" 238 | } 239 | }, 240 | "nbformat": 4, 241 | "nbformat_minor": 0 242 | } 243 | -------------------------------------------------------------------------------- /Milstein Scheme - Monte Carlo Simulation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 9, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import numpy as np\n", 13 | "import numpy.random as npr\n", 14 | "import math\n", 15 | "from pylab import *\n", 16 | "import matplotlib.pyplot as plt\n", 17 | "import scipy as sp\n", 18 | "from scipy import stats\n", 19 | "import time" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 10, 25 | "metadata": { 26 | "collapsed": true 27 | }, 28 | "outputs": [], 29 | "source": [ 30 | "'''DEFINITION OF VARIABLES\n", 31 | " S0 - Stock Price at T=0\n", 32 | " E - Strike Price\n", 33 | " T - Time in Years\n", 34 | " R - Risk Free Rate\n", 35 | " SIGMA - Volatility\n", 36 | " DT - Time Step = T/N\n", 37 | " DF - Discount Factor = e^-RT\n", 38 | " I - Number of Simulations\n", 39 | " P - Discrete Sampling Frequency for Asian Options \n", 40 | " 252/Annual, 126/SemiAnnual, 63/Quarterly, 21/Monthly, 1/Continuous\n", 41 | "'''\n", 42 | "\n", 43 | "S0 = 100\n", 44 | "E=100\n", 45 | "T=1\n", 46 | "R=0.05\n", 47 | "SIGMA=0.20\n", 48 | "I=100000\n", 49 | "P= 21 #Discrete Sampling Frequency 252/Annual, 126/SemiAnnual, 63/Quarterly, 21/Monthly, 1/Continuous\n", 50 | "N=252" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 11, 56 | "metadata": { 57 | "collapsed": true 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "'''OPTION VALUATION - MONTE CARLO SIMULATION W/ ANTITHETIC VARIANCE REDUCTION W/ MILSTEIN SCHEME '''\n", 62 | "def option_valuation_Milstein(S0, E, T, N, SIGMA, R, I, P): \n", 63 | " DT = T/N #Time Step \n", 64 | "#GENERATE RANDOM NUMBERS - ANTITHETIC VARIANCE REDUCTION\n", 65 | " PHI = npr.standard_normal((N,int(I/2))) \n", 66 | " PHI = np.concatenate((PHI, -PHI), axis=1) \n", 67 | "#SET UP EMPTY ARRAYS AND SET INITIAL VALUES \n", 68 | " S = np.zeros_like(PHI) #Array to Capture Asset Value Path\n", 69 | " S[0] = S0\n", 70 | " \n", 71 | "#CREATE FOR LOOP TO GENERATE SIMULATION PATHS - MILSTEIN METHOD\n", 72 | " for t in range (1,N):\n", 73 | " S[t]=S[t-1]*(1+R*DT+(SIGMA*PHI[t]*np.sqrt(DT))+(SIGMA**2)*(0.5*(((PHI[t]**2)-1)*DT)))\n", 74 | " \n", 75 | " return S" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 12, 81 | "metadata": { 82 | "collapsed": false 83 | }, 84 | "outputs": [ 85 | { 86 | "name": "stdout", 87 | "output_type": "stream", 88 | "text": [ 89 | "Number of Simulations 100000\n", 90 | "Value of Call Option Monte Carlo W/ Milstein = 10.422\n", 91 | "Value of Put Option Monte Carlo W/ Milstein = 5.567\n", 92 | "Number of Simulations 100000\n", 93 | "Value of Call Option Monte Carlo W/ Milstein = 10.387\n", 94 | "Value of Put Option Monte Carlo W/ Milstein = 5.546\n", 95 | "Number of Simulations 100000\n", 96 | "Value of Call Option Monte Carlo W/ Milstein = 10.341\n", 97 | "Value of Put Option Monte Carlo W/ Milstein = 5.515\n", 98 | "1 loop, best of 3: 1.51 s per loop\n" 99 | ] 100 | } 101 | ], 102 | "source": [ 103 | "%%timeit -n1\n", 104 | "#Calculation of Discounted Expected Payoff\n", 105 | "S = option_valuation_Milstein(S0, E, T, N, SIGMA, R, I, P)\n", 106 | "\n", 107 | "DF = math.exp(-R*T) #Discount Factor\n", 108 | "\n", 109 | "print(\"Number of Simulations %.d\" %I)\n", 110 | "\n", 111 | "Call_Value = DF * np.sum(np.maximum(S[-1] - E, 0)) / I\n", 112 | "print( \"Value of Call Option Monte Carlo W/ Milstein = %.3f\" %Call_Value)\n", 113 | "Put_Value = DF * np.sum(np.maximum(E - S[-1], 0)) / I\n", 114 | "print( \"Value of Put Option Monte Carlo W/ Milstein = %.3f\" %Put_Value)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 13, 120 | "metadata": { 121 | "collapsed": true 122 | }, 123 | "outputs": [], 124 | "source": [ 125 | "'''OPTION VALUATION - MONTE CARLO SIMULATION W/ ANTITHETIC VARIANCE REDUCTION W/O MILSTEIN SCHEME '''\n", 126 | "def option_valuation(S0, E, T, N, SIGMA, R, I, P): \n", 127 | " DT = T/N #Time Step \n", 128 | "#GENERATE RANDOM NUMBERS - ANTITHETIC VARIANCE REDUCTION\n", 129 | " PHI = npr.standard_normal((N,int(I/2))) \n", 130 | " PHI = np.concatenate((PHI, -PHI), axis=1) \n", 131 | "#SET UP EMPTY ARRAYS AND SET INITIAL VALUES \n", 132 | " S = np.zeros_like(PHI) #Array to Capture Asset Value Path\n", 133 | " S[0] = S0\n", 134 | " \n", 135 | "#CREATE FOR LOOP TO GENERATE SIMULATION PATHS - EULER-MARUYAMA METHOD\n", 136 | " for t in range (1,N):\n", 137 | " S[t]=S[t-1]*(1+R*DT+(SIGMA*PHI[t]*np.sqrt(DT)))\n", 138 | " \n", 139 | " return S" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 14, 145 | "metadata": { 146 | "collapsed": false 147 | }, 148 | "outputs": [ 149 | { 150 | "name": "stdout", 151 | "output_type": "stream", 152 | "text": [ 153 | "Number of Simulations 100000\n", 154 | "Value of Call Option Euler-Maruyama Method Monte Carlo = 10.407\n", 155 | "Value of Put Option Euler-Maruyama Method Monte Carlo = 5.563\n", 156 | "Number of Simulations 100000\n", 157 | "Value of Call Option Euler-Maruyama Method Monte Carlo = 10.447\n", 158 | "Value of Put Option Euler-Maruyama Method Monte Carlo = 5.579\n", 159 | "Number of Simulations 100000\n", 160 | "Value of Call Option Euler-Maruyama Method Monte Carlo = 10.456\n", 161 | "Value of Put Option Euler-Maruyama Method Monte Carlo = 5.583\n", 162 | "1 loop, best of 3: 1.08 s per loop\n" 163 | ] 164 | } 165 | ], 166 | "source": [ 167 | "%%timeit -n1\n", 168 | "#Calculation of Discounted Expected Payoff\n", 169 | "S = option_valuation(S0, E, T, N, SIGMA, R, I, P)\n", 170 | "\n", 171 | "DF = math.exp(-R*T) #Discount Factor\n", 172 | "\n", 173 | "print(\"Number of Simulations %.d\" %I)\n", 174 | "\n", 175 | "Call_Value = DF * np.sum(np.maximum(S[-1] - E, 0)) / I\n", 176 | "print( \"Value of Call Option Euler-Maruyama Method Monte Carlo = %.3f\" %Call_Value)\n", 177 | "Put_Value = DF * np.sum(np.maximum(E - S[-1], 0)) / I\n", 178 | "print( \"Value of Put Option Euler-Maruyama Method Monte Carlo = %.3f\" %Put_Value)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": { 185 | "collapsed": true 186 | }, 187 | "outputs": [], 188 | "source": [] 189 | } 190 | ], 191 | "metadata": { 192 | "anaconda-cloud": {}, 193 | "kernelspec": { 194 | "display_name": "Python [Root]", 195 | "language": "python", 196 | "name": "Python [Root]" 197 | }, 198 | "language_info": { 199 | "codemirror_mode": { 200 | "name": "ipython", 201 | "version": 3 202 | }, 203 | "file_extension": ".py", 204 | "mimetype": "text/x-python", 205 | "name": "python", 206 | "nbconvert_exporter": "python", 207 | "pygments_lexer": "ipython3", 208 | "version": "3.5.2" 209 | } 210 | }, 211 | "nbformat": 4, 212 | "nbformat_minor": 0 213 | } 214 | -------------------------------------------------------------------------------- /Asian Option Valuation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Asian Option Valuation\n", 8 | "\n", 9 | "Asian Options Types:\n", 10 | "\n", 11 | "Fixed Strike\n", 12 | " \n", 13 | " Pays the difference between a fixed strike value and the average value of the underlying\n", 14 | "\n", 15 | "Floating Strike\n", 16 | " \n", 17 | " Pays the difference between the average value (the strike) and the ending value\n", 18 | " \n", 19 | "The necessity of calculating the average makes asian options path dependent.\n", 20 | "\n", 21 | "The calculation of the average is also variable and will be specified in the contract\n", 22 | " \n", 23 | " The sampling period - how often the average is taken ranging from daily to once at the end of the contract\n", 24 | " \n", 25 | " Geometric vs Arithmetic mean" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 9, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "#Module Imports\n", 37 | "import numpy as np\n", 38 | "import numpy.random as npr\n", 39 | "import math\n", 40 | "import scipy as sp\n", 41 | "from scipy import stats" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 10, 47 | "metadata": { 48 | "collapsed": true 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "'''DEFINITION OF VARIABLES\n", 53 | " S0 - Stock Price at T=0\n", 54 | " E - Strike Price\n", 55 | " T - Time in Years\n", 56 | " R - Risk Free Rate\n", 57 | " SIGMA - Volatility\n", 58 | " DT - Time Step = T/N\n", 59 | " DF - Discount Factor = e^-RT\n", 60 | " I - Number of Simulations\n", 61 | " P - Discrete Sampling Frequency for Asian Options \n", 62 | " 252/Annual, 126/SemiAnnual, 63/Quarterly, 21/Monthly, 1/Continuous\n", 63 | "'''\n", 64 | "\n", 65 | "S0 = 100\n", 66 | "E=100\n", 67 | "T=1\n", 68 | "R=0.05\n", 69 | "SIGMA=0.20\n", 70 | "I=10000\n", 71 | "P= 21 #Discrete Sampling Frequency 252/Annual, 126/SemiAnnual, 63/Quarterly, 21/Monthly, 1/Continuous\n", 72 | "N=252\n" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 11, 78 | "metadata": { 79 | "collapsed": true 80 | }, 81 | "outputs": [], 82 | "source": [ 83 | "'''OPTION VALUATION - W/ ANTITHETIC VARIANCE REDUCTION W/ MILSTEIN SCHEME - \n", 84 | "ASIAN OPTIONS - FIXED AND FLOATING STRIKE'''\n", 85 | "def option_valuation(S0, E, T, N, SIGMA, R, I, P): \n", 86 | " DT = T/N #Time Step\n", 87 | " DF = math.exp(-R*T) #Discount Factor \n", 88 | "#GENERATE RANDOM NUMBERS - ANTITHETIC VARIANCE REDUCTION\n", 89 | " PHI = npr.standard_normal((N,int(I/2))) \n", 90 | " PHI = np.concatenate((PHI, -PHI), axis=1) \n", 91 | "#SET UP EMPTY ARRAYS AND SET INITIAL VALUES \n", 92 | " S = np.zeros_like(PHI) #Array to Capture Asset Value Path\n", 93 | " S[0] = S0\n", 94 | " P_AVG=np.zeros_like((S)) #Array to Capture Arithmetic Average Sample\n", 95 | " G_AVG=np.zeros_like((S)) #Array to Capture Geometric Average Sample\n", 96 | "#CREATE FOR LOOP TO GENERATE SIMULATION PATHS - MILSTEIN METHOD\n", 97 | " for t in range (1,N):\n", 98 | " S[t]=S[t-1]*(1+R*DT+(SIGMA*PHI[t]*np.sqrt(DT))+(SIGMA**2)*(0.5*(((PHI[t]**2)-1)*DT)))\n", 99 | "#Heaviside Function to Determine When to Take an Average\n", 100 | "#On sample date the average is taken and stored in the appropriate array\n", 101 | " Mod = int(t) % P \n", 102 | " if Mod == 0:\n", 103 | " P_AVG [t-1] = np.mean(S[(t-(P)):t], axis=0)\n", 104 | " G_AVG [t-1] = sp.stats.gmean(S[(t-P):t], axis=0)\n", 105 | " \n", 106 | " P_AVG[-1] = np.mean(S[(-P):N], axis=0)\n", 107 | " P_AVG_Payoff = np.sum(P_AVG[0:N], axis=0) / (N/P)\n", 108 | " \n", 109 | " G_AVG[-1] = sp.stats.gmean(S[(-P):N], axis=0)\n", 110 | " G_AVG_Payoff = np.sum(G_AVG[0:N], axis=0) / (N/P) \n", 111 | " \n", 112 | "#Calculation of Discounted Expected Payoff for Asian Options - Arithmetic Mean \n", 113 | " Call_Value_Asian = DF * np.sum(np.maximum((P_AVG_Payoff) - E, 0)) / I\n", 114 | " print( \"Value of Fixed Strike Asian Call Option - Arithmetic Average = %.3f\" %Call_Value_Asian)\n", 115 | " Put_Value_Asian = DF * np.sum(np.maximum(E - (P_AVG_Payoff), 0)) / I\n", 116 | " print( \"Value of Fixed Strike Asian Put Option - Arithmetic Average = %.3f\" %Put_Value_Asian) \n", 117 | "\n", 118 | "#Calculation of Discounted Expected Payoff for Asian Options - Geometric Mean\n", 119 | " Call_Value_Asian_GEO = DF * np.sum(np.maximum((G_AVG_Payoff) - E, 0)) / I\n", 120 | " print( \"Value of Asian Fixed Strike Call Option - Geometric Average = %.3f\" %Call_Value_Asian_GEO)\n", 121 | " Put_Value_Asian_GEO = DF * np.sum(np.maximum(E - (G_AVG_Payoff), 0)) / I\n", 122 | " print( \"Value of Asian Fixed Strike Put Option - Geometric Average = %.3f\" %Put_Value_Asian_GEO)\n", 123 | "\n", 124 | "#Calculation of Discounted Expected Payoff for Asian Options - Geometric Mean - Floating Strike\n", 125 | " Call_Value_Asian_GEO_Float_Strike = DF * np.sum(np.maximum(S[-1] - (G_AVG_Payoff), 0)) / I\n", 126 | " print( \"Value of Asian Floating Strike Call Option - Geometric Average = %.3f\" %Call_Value_Asian_GEO_Float_Strike)\n", 127 | " Put_Value_Asian_GEO_Float_Strike = DF * np.sum(np.maximum((G_AVG_Payoff) - S[-1], 0)) / I\n", 128 | " print( \"Value of Asian Floating Strike Put Option - Geometric Average = %.3f\" %Put_Value_Asian_GEO_Float_Strike)\n", 129 | "\n", 130 | "#Calculation of Discounted Expected Payoff for Asian Options - Arithmetic Mean - Floating Strike \n", 131 | " Call_Value_Asian_Float_Strike = DF * np.sum(np.maximum(S[-1] - (P_AVG_Payoff), 0)) / I\n", 132 | " print( \"Value of Asian Floating Strike Call Option - Arithmetic Average = %.3f\" %Call_Value_Asian_Float_Strike)\n", 133 | " Put_Value_Asian_Float_Strike = DF * np.sum(np.maximum((P_AVG_Payoff) - S[-1], 0)) / I\n", 134 | " print( \"Value of Asian Floating Strike Put Option - Arithmetic Average = %.3f\" %Put_Value_Asian_Float_Strike) " 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 12, 140 | "metadata": { 141 | "collapsed": false 142 | }, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "Value of Fixed Strike Asian Call Option - Arithmetic Average = 5.789\n", 149 | "Value of Fixed Strike Asian Put Option - Arithmetic Average = 3.344\n", 150 | "Value of Asian Fixed Strike Call Option - Geometric Average = 5.772\n", 151 | "Value of Asian Fixed Strike Put Option - Geometric Average = 3.354\n", 152 | "Value of Asian Floating Strike Call Option - Geometric Average = 6.216\n", 153 | "Value of Asian Floating Strike Put Option - Geometric Average = 3.564\n", 154 | "Value of Asian Floating Strike Call Option - Arithmetic Average = 6.200\n", 155 | "Value of Asian Floating Strike Put Option - Arithmetic Average = 3.576\n" 156 | ] 157 | } 158 | ], 159 | "source": [ 160 | "option_valuation(S0, E, T, N, SIGMA, R, I, P)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": { 167 | "collapsed": true 168 | }, 169 | "outputs": [], 170 | "source": [] 171 | } 172 | ], 173 | "metadata": { 174 | "anaconda-cloud": {}, 175 | "kernelspec": { 176 | "display_name": "Python [Root]", 177 | "language": "python", 178 | "name": "Python [Root]" 179 | }, 180 | "language_info": { 181 | "codemirror_mode": { 182 | "name": "ipython", 183 | "version": 3 184 | }, 185 | "file_extension": ".py", 186 | "mimetype": "text/x-python", 187 | "name": "python", 188 | "nbconvert_exporter": "python", 189 | "pygments_lexer": "ipython3", 190 | "version": "3.5.2" 191 | } 192 | }, 193 | "nbformat": 4, 194 | "nbformat_minor": 0 195 | } 196 | -------------------------------------------------------------------------------- /Binomial Model - European Calls & Puts.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 87, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import math\n", 13 | "import xlwings as xw" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 88, 19 | "metadata": { 20 | "collapsed": false 21 | }, 22 | "outputs": [ 23 | { 24 | "data": { 25 | "text/plain": [ 26 | "(1.0079370266644199,\n", 27 | " 0.9921254736611017,\n", 28 | " 1.0000500012500209,\n", 29 | " 0.5011859105336545)" 30 | ] 31 | }, 32 | "execution_count": 88, 33 | "metadata": {}, 34 | "output_type": "execute_result" 35 | } 36 | ], 37 | "source": [ 38 | "#Variables\n", 39 | "S = 100 #Stock price at t=0\n", 40 | "r = 0.05 #Risk free rate\n", 41 | "dt = .001 #Timestep size\n", 42 | "T = 1 #time to expiry in years\n", 43 | "sigma = .250\n", 44 | "E = 110 #Strike Price\n", 45 | "\n", 46 | "\n", 47 | "length = T/dt\n", 48 | "u = math.exp( sigma * math.sqrt(dt))\n", 49 | "d = math.exp(- sigma * math.sqrt(dt))\n", 50 | "a = math.exp( r * dt)\n", 51 | "p = (a - d) / (u - d)\n", 52 | "\n", 53 | "u,d,a,p" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 89, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [], 63 | "source": [ 64 | "#Create Empty Numpy Arrays to hold the data\n", 65 | "step_matrix = np.zeros((int(length), int(length+1)))\n", 66 | "call_option_value_matrix = np.zeros((int(length+1), int(length+1)))\n", 67 | "put_option_value_matrix = np.zeros((int(length+1), int(length+1)))\n", 68 | "\n", 69 | "stock_value_matrix = np.zeros((int(length+1), int(length+1)))\n", 70 | "#Set initial value for the stock\n", 71 | "stock_value_matrix[0,0] = S" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 90, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "#Create binomial tree of the pricing of the underlying\n", 83 | "for x in range(1,int(length+1)):\n", 84 | " stock_value_matrix[x,0] = stock_value_matrix[x-1,0]*(d)\n", 85 | " for y in range(1,int(length+1)):\n", 86 | " stock_value_matrix[x,y] = stock_value_matrix[x-1,y-1]*(u)\n", 87 | "#xw.view(value_matrix)\n", 88 | "#stock_value_matrix" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 91, 94 | "metadata": { 95 | "collapsed": false 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "#Create Option Valuation Tree Call Option\n", 100 | "for x in range(1, int(length+1)):\n", 101 | " for y in range(0, int(length+1)):\n", 102 | " if stock_value_matrix[-x , y]-E > 0:\n", 103 | " call_option_value_matrix[-x, y] = stock_value_matrix[-x , y] - E\n", 104 | " else:\n", 105 | " call_option_value_matrix[-x , y] = 0\n", 106 | " \n", 107 | "#Create Option Valuation Tree Put Option\n", 108 | "for x in range(1, int(length+1)):\n", 109 | " for y in range(0, int(length+1)):\n", 110 | " if E - stock_value_matrix[-x , y] > 0:\n", 111 | " put_option_value_matrix[-x, y] = E - stock_value_matrix[-x , y]\n", 112 | " else:\n", 113 | " put_option_value_matrix[-x , y] = 0" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 92, 119 | "metadata": { 120 | "collapsed": false 121 | }, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "Value of Call Option Binomial = 8.0241 \n", 128 | "Value of Put Option Binomial = 12.6593 \n" 129 | ] 130 | } 131 | ], 132 | "source": [ 133 | "call_payoff_matrix = np.zeros((int(length+1), int(length+1)))\n", 134 | "call_payoff_matrix[-1] = call_option_value_matrix[-1]\n", 135 | "count = 0\n", 136 | "for x in range(1,int(length+1)):\n", 137 | " count += 1\n", 138 | " for y in range(count, int(length+1)):\n", 139 | " call_payoff_matrix[-x-1,-y-1] = math.exp(-r*dt)*((p*call_payoff_matrix[-x,-y])+((1-p)*call_payoff_matrix[-x,-y-1]))\n", 140 | "\n", 141 | "put_payoff_matrix = np.zeros((int(length+1), int(length+1)))\n", 142 | "put_payoff_matrix[-1] = put_option_value_matrix[-1]\n", 143 | "count = 0\n", 144 | "for x in range(1,int(length+1)):\n", 145 | " count += 1\n", 146 | " for y in range(count, int(length+1)):\n", 147 | " put_payoff_matrix[-x-1,-y-1] = math.exp(-r*dt)*((p*put_payoff_matrix[-x,-y])+((1-p)*put_payoff_matrix[-x,-y-1]))\n", 148 | "\n", 149 | "#xw.view(put_payoff_matrix)\n", 150 | "#xw.view(call_payoff_matrix)\n", 151 | "call_value = call_payoff_matrix[0,0]\n", 152 | "put_value = put_payoff_matrix[0,0]\n", 153 | "\n", 154 | "print(\"Value of Call Option Binomial = %.4f \" %call_value)\n", 155 | "print(\"Value of Put Option Binomial = %.4f \" %put_value)" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 93, 161 | "metadata": { 162 | "collapsed": true 163 | }, 164 | "outputs": [], 165 | "source": [ 166 | "#Module Imports\n", 167 | "import scipy as sp\n", 168 | "from scipy import stats" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 94, 174 | "metadata": { 175 | "collapsed": true 176 | }, 177 | "outputs": [], 178 | "source": [ 179 | "'''DEFINITION OF VARIABLES\n", 180 | " S0 - Stock Price at T=0\n", 181 | " E - Strike Price\n", 182 | " T - Time in Years\n", 183 | " R - Risk Free Rate\n", 184 | " SIGMA - Volatility\n", 185 | " DT - Time Step = T/N\n", 186 | " DF - Discount Factor = e^-RT\n", 187 | "'''\n", 188 | "\n", 189 | "S0 = S\n", 190 | "E = E\n", 191 | "T = T\n", 192 | "R = r\n", 193 | "SIGMA = sigma" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 95, 199 | "metadata": { 200 | "collapsed": true 201 | }, 202 | "outputs": [], 203 | "source": [ 204 | "'''BSM VANILLA EUROPEAN OPTION VALUE CALCULATION'''\n", 205 | "def bsm_option_value(S0, E, T, R, SIGMA): \n", 206 | " S0 = float(S0)\n", 207 | " d1 = (math.log(S0/E)+(R+0.5*SIGMA**2)*T)/(SIGMA*math.sqrt(T))\n", 208 | " d2 = d1-(SIGMA*math.sqrt(T))\n", 209 | " \n", 210 | " call_value = S0*stats.norm.cdf(d1,0,1) - E*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 211 | " \n", 212 | " put_value = E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1) - (S0*stats.norm.cdf(-d1,0,1))\n", 213 | "\n", 214 | " print(\"Value of Call Option BSM = \" + str(call_value))\n", 215 | " print(\"Value of Put Option BSM = \" + str(put_value))\n", 216 | " return" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 96, 222 | "metadata": { 223 | "collapsed": false 224 | }, 225 | "outputs": [ 226 | { 227 | "name": "stdout", 228 | "output_type": "stream", 229 | "text": [ 230 | "Value of Call Option BSM = 8.02638469385\n", 231 | "Value of Put Option BSM = 12.6616213889\n" 232 | ] 233 | } 234 | ], 235 | "source": [ 236 | "bsm_option_value(S0, E, T, R, SIGMA)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": null, 242 | "metadata": { 243 | "collapsed": true 244 | }, 245 | "outputs": [], 246 | "source": [] 247 | } 248 | ], 249 | "metadata": { 250 | "anaconda-cloud": {}, 251 | "kernelspec": { 252 | "display_name": "Python [Root]", 253 | "language": "python", 254 | "name": "Python [Root]" 255 | }, 256 | "language_info": { 257 | "codemirror_mode": { 258 | "name": "ipython", 259 | "version": 3 260 | }, 261 | "file_extension": ".py", 262 | "mimetype": "text/x-python", 263 | "name": "python", 264 | "nbconvert_exporter": "python", 265 | "pygments_lexer": "ipython3", 266 | "version": "3.5.2" 267 | } 268 | }, 269 | "nbformat": 4, 270 | "nbformat_minor": 0 271 | } 272 | -------------------------------------------------------------------------------- /Newton Raphson Root finding for Implied Volatility.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "#Module Imports\n", 12 | "import math\n", 13 | "import scipy as sp\n", 14 | "from scipy import stats" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "'''\n", 26 | " DEFINITION OF VARIABLES\n", 27 | " S0 - Stock Price at T=0\n", 28 | " E - Strike Price\n", 29 | " T - Time in Years\n", 30 | " R - Risk Free Rate\n", 31 | "'''\n", 32 | "S0 = 100\n", 33 | "E=100\n", 34 | "T=1\n", 35 | "R=0.005276" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 3, 41 | "metadata": { 42 | "collapsed": true 43 | }, 44 | "outputs": [], 45 | "source": [ 46 | "'''BSM VANILLA EUROPEAN OPTION VALUE CALCULATION'''\n", 47 | "def bsm_option_value(S0, E, T, R, SIGMA): \n", 48 | " S0 = float(S0)\n", 49 | " d1 = (math.log(S0/E)+(R+0.05*SIGMA**2)*T)/(SIGMA*math.sqrt(T))\n", 50 | " d2 = d1-(SIGMA*math.sqrt(T))\n", 51 | " \n", 52 | " call_value = S0*stats.norm.cdf(d1,0,1) - E*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 53 | " put_value = E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1) - (S0*stats.norm.cdf(-d1,0,1))\n", 54 | " return d1, call_value, put_value" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 4, 60 | "metadata": { 61 | "collapsed": false 62 | }, 63 | "outputs": [], 64 | "source": [ 65 | "def call_implied_vol(call_price):\n", 66 | " CALL_SIGMA = 1 #initial estimate for sigma\n", 67 | " count = 10000 #number of attempts to find value\n", 68 | "\n", 69 | " #first attempt to set parameters\n", 70 | " d1, call_value, put_value = bsm_option_value(S0, E, T, R, CALL_SIGMA)\n", 71 | " var = abs(call_value - call_price)\n", 72 | "\n", 73 | " while var > .003 and count > 0:\n", 74 | " VEGA = math.sqrt(T)*S0*stats.norm.pdf(d1,0,1)\n", 75 | " CALL_SIGMA = CALL_SIGMA - ((call_value / VEGA)/100)\n", 76 | " d1, call_value, put_value = bsm_option_value(S0, E, T, R, CALL_SIGMA)\n", 77 | " var = abs((call_value - call_price)/call_value)\n", 78 | " count = count - 1 \n", 79 | " return CALL_SIGMA" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 5, 85 | "metadata": { 86 | "collapsed": false 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "def put_implied_vol(put_price):\n", 91 | " PUT_SIGMA = 1 #initial estimate for sigma\n", 92 | " count = 10000 #number of attempts to find value\n", 93 | "\n", 94 | " #first attempt to set parameters\n", 95 | " d1, call_value, put_value = bsm_option_value(S0, E, T, R, PUT_SIGMA)\n", 96 | " var = abs(put_value - put_price)\n", 97 | "\n", 98 | " while var > .003 and count > 0:\n", 99 | " VEGA = math.sqrt(T)*S0*stats.norm.pdf(d1,0,1)\n", 100 | " PUT_SIGMA = PUT_SIGMA - ((put_value / VEGA)/100)\n", 101 | " d1, call_value, put_value = bsm_option_value(S0, E, T, R, PUT_SIGMA)\n", 102 | " var = abs((put_value - put_price)/put_value)\n", 103 | " count = count - 1\n", 104 | " return PUT_SIGMA" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 6, 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "outputs": [ 114 | { 115 | "name": "stdout", 116 | "output_type": "stream", 117 | "text": [ 118 | "Implied Volatility 0.7540\n" 119 | ] 120 | } 121 | ], 122 | "source": [ 123 | "#input observed call option price to calculate implied volatility\n", 124 | "call_price = 28.00\n", 125 | "CALL_SIGMA = call_implied_vol(call_price)\n", 126 | "\n", 127 | "print(\"Implied Volatility %.4f\" %CALL_SIGMA)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 7, 133 | "metadata": { 134 | "collapsed": false 135 | }, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "Implied Volatility 0.4470\n" 142 | ] 143 | } 144 | ], 145 | "source": [ 146 | "#input observed put option price to calculate implied volatility\n", 147 | "put_price = 17.00\n", 148 | "PUT_SIGMA = put_implied_vol(put_price)\n", 149 | "\n", 150 | "print(\"Implied Volatility %.4f\" %PUT_SIGMA)" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 8, 156 | "metadata": { 157 | "collapsed": false 158 | }, 159 | "outputs": [ 160 | { 161 | "name": "stdout", 162 | "output_type": "stream", 163 | "text": [ 164 | "BSM Call Value 28.00\n", 165 | "Variance to Root Finding 0.002\n", 166 | "BSM Put Value 17.03\n", 167 | "Variance to Root Finding 0.028\n" 168 | ] 169 | } 170 | ], 171 | "source": [ 172 | "#Validation - Calculates BSM Values using the calculated implied volatility\n", 173 | "d1, call_value, put_value = bsm_option_value(S0, E, T, R, CALL_SIGMA)\n", 174 | "print(\"BSM Call Value %.2f\" %call_value)\n", 175 | "call_diff = call_value - call_price\n", 176 | "print(\"Variance to Root Finding %.3f\" %call_diff)\n", 177 | "d1, call_value, put_value = bsm_option_value(S0, E, T, R, PUT_SIGMA)\n", 178 | "print(\"BSM Put Value %.2f\" %put_value)\n", 179 | "put_diff = put_value - put_price\n", 180 | "print(\"Variance to Root Finding %.3f\" %put_diff)\n" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 9, 186 | "metadata": { 187 | "collapsed": true 188 | }, 189 | "outputs": [], 190 | "source": [ 191 | "#scipy root finding function\n", 192 | "from scipy import optimize" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 10, 198 | "metadata": { 199 | "collapsed": true 200 | }, 201 | "outputs": [], 202 | "source": [ 203 | "def optimize_function_call(CALL_SIGMA):\n", 204 | " d1, call_value, put_value = bsm_option_value(S0, E, T, R, CALL_SIGMA)\n", 205 | " var = abs(call_value - call_price)\n", 206 | " return var" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 11, 212 | "metadata": { 213 | "collapsed": true 214 | }, 215 | "outputs": [], 216 | "source": [ 217 | "def optimize_function_put(PUT_SIGMA):\n", 218 | " d1, call_value, put_value = bsm_option_value(S0, E, T, R, PUT_SIGMA)\n", 219 | " var = abs(put_value - put_price)\n", 220 | " return var" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 12, 226 | "metadata": { 227 | "collapsed": false 228 | }, 229 | "outputs": [ 230 | { 231 | "name": "stdout", 232 | "output_type": "stream", 233 | "text": [ 234 | "Implied Volatility 0.7540\n" 235 | ] 236 | } 237 | ], 238 | "source": [ 239 | "opt_call = sp.optimize.fsolve(optimize_function_call, 1)\n", 240 | "print(\"Implied Volatility %.4f\" %opt_call[0])" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 13, 246 | "metadata": { 247 | "collapsed": false 248 | }, 249 | "outputs": [ 250 | { 251 | "name": "stdout", 252 | "output_type": "stream", 253 | "text": [ 254 | "Implied Volatility 0.4462\n" 255 | ] 256 | } 257 | ], 258 | "source": [ 259 | "opt_put = sp.optimize.fsolve(optimize_function_put, 1)\n", 260 | "print(\"Implied Volatility %.4f\" %opt_put[0])" 261 | ] 262 | } 263 | ], 264 | "metadata": { 265 | "anaconda-cloud": {}, 266 | "kernelspec": { 267 | "display_name": "Python [Root]", 268 | "language": "python", 269 | "name": "Python [Root]" 270 | }, 271 | "language_info": { 272 | "codemirror_mode": { 273 | "name": "ipython", 274 | "version": 3 275 | }, 276 | "file_extension": ".py", 277 | "mimetype": "text/x-python", 278 | "name": "python", 279 | "nbconvert_exporter": "python", 280 | "pygments_lexer": "ipython3", 281 | "version": "3.5.2" 282 | } 283 | }, 284 | "nbformat": 4, 285 | "nbformat_minor": 0 286 | } 287 | -------------------------------------------------------------------------------- /Explicit Finite Differences Method - Barrier Options.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Explicit Finite Differences Method for Option Valuation\n", 8 | "\n", 9 | "Includes Call and Puts / American and European" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "import math\n", 21 | "import numpy as np\n", 22 | "import pandas as pd\n", 23 | "import xlwings as xw\n", 24 | "import scipy as sp\n", 25 | "from scipy import stats\n", 26 | "import matplotlib as plt" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 2, 32 | "metadata": { 33 | "collapsed": true 34 | }, 35 | "outputs": [], 36 | "source": [ 37 | "T = 1 #Time to Expiry in Years\n", 38 | "E = 100 #Strike\n", 39 | "r = .05 #Risk Free Rate\n", 40 | "SIGMA = .20 #Volatility\n", 41 | "\n", 42 | "#Up out Out Call - Set Barrier_Call to barrier value and set Type to True\n", 43 | "#Down and Out Call - Set Barrier_Put to barrier value and set Type to True\n", 44 | "\n", 45 | "#Down and Out Put - Set Barrier_Put to barrier value and set Type to False\n", 46 | "#Up and Out Put - Set Barrier_Call to barrier value and set Type to False\n", 47 | "\n", 48 | "\n", 49 | "Type = True #Type of Option True=Call False=Put\n", 50 | "Ex = False #Early Exercise True=Yes False=No \n", 51 | "NAS = 100 #Number of Asset Steps - Higher is more accurate, but more time consuming\n", 52 | "Barrier_Call = 120 #Ceiling value to Up and Out Barrier Options- Set to zero if not using\n", 53 | "Barrier_Put = 0 #Floor value for Down and Out Barrier Options - Set to zero if not using" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 3, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "Asset Step Size 1.20 Time Step Size 0.00 Number of Time Steps 445.00 Number of Asset Steps 100.00\n" 68 | ] 69 | } 70 | ], 71 | "source": [ 72 | "#Up and Out Call\n", 73 | "if Barrier_Call > 0 and Type ==True:\n", 74 | " ds = Barrier_Call / NAS #Asset Value Step Size\n", 75 | "#Down and Out Call\n", 76 | "if Barrier_Put > 0 and Type ==True:\n", 77 | " ds = ds = 2 * E / NAS #Asset Value Step Size\n", 78 | "#Up and Out Put\n", 79 | "if Barrier_Call > 0 and Type ==False:\n", 80 | " ds = Barrier_Call / NAS #Asset Value Step Size\n", 81 | "#Down and Out Put\n", 82 | "if Barrier_Put > 0 and Type == False:\n", 83 | " ds = ds = 2 * E / NAS #Asset Value Step Size\n", 84 | "\n", 85 | "dt = (0.9/NAS/NAS/SIGMA/SIGMA) #Time Step Size\n", 86 | "NTS = int(T / dt) + 1 #Number of Time Steps\n", 87 | "dt = T / NTS #Time Step Size\n", 88 | "print(\"Asset Step Size %.2f Time Step Size %.2f Number of Time Steps %.2f Number of Asset Steps %.2f\" %(ds, dt, NTS, NAS))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 4, 94 | "metadata": { 95 | "collapsed": false 96 | }, 97 | "outputs": [], 98 | "source": [ 99 | "#Setup Empty numpy Arrays\n", 100 | "value_matrix = np.zeros((int(NAS+1), int(NTS)))\n", 101 | "asset_price = np.arange(NAS*ds,-1,-ds)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 5, 107 | "metadata": { 108 | "collapsed": false 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "#Evaluate Terminal Value for Calls or Puts\n", 113 | "if Type == True:\n", 114 | " value_matrix[:,-1]= np.maximum(asset_price - E,0)\n", 115 | "else:\n", 116 | " value_matrix[:,-1]= np.maximum(E - asset_price,0)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 6, 122 | "metadata": { 123 | "collapsed": false 124 | }, 125 | "outputs": [], 126 | "source": [ 127 | "#Set Lower Boundry in Grid\n", 128 | "if Barrier_Put > 0:\n", 129 | " for x in range(1,NTS):\n", 130 | " value_matrix[-1,-x-1] = 0\n", 131 | "else:\n", 132 | " for x in range(1,NTS):\n", 133 | " value_matrix[-1,-x-1] = value_matrix[-1,-x]* math.exp(-r*dt)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 7, 139 | "metadata": { 140 | "collapsed": false 141 | }, 142 | "outputs": [], 143 | "source": [ 144 | "#Set Mid and Ceiling Values in Grid\n", 145 | "for x in range(1,int(NTS)):\n", 146 | "\n", 147 | " for y in range(1,int(NAS)):\n", 148 | " #Evaluate Option Greeks\n", 149 | " Delta = (value_matrix[y-1,-x] - value_matrix[y+1,-x]) / 2 / ds\n", 150 | " Gamma = (value_matrix[y-1,-x] - (2 * value_matrix[y,-x]) + value_matrix[y+1,-x]) / ds / ds\n", 151 | " Theta = (-.5 * SIGMA**2 * asset_price[y]**2 * Gamma) - (r * asset_price[y] * Delta) + (r * value_matrix[y,-x])\n", 152 | " \n", 153 | " #Set Mid Values\n", 154 | " value_matrix[y,-x-1] = value_matrix[y,-x] - Theta * dt\n", 155 | " if Ex == True:\n", 156 | " value_matrix[y,-x-1] = np.maximum(value_matrix[y,-x-1], value_matrix[y,-1])\n", 157 | " if Barrier_Put > 0:\n", 158 | " if asset_price[y] < Barrier_Put:\n", 159 | " value_matrix[y,-x-1] = 0\n", 160 | " \n", 161 | "\n", 162 | " #Set Ceiling Value\n", 163 | " if Barrier_Call > 0:\n", 164 | " value_matrix[0,-x-1] = 0\n", 165 | " else:\n", 166 | " value_matrix[0,-x-1] = 2 * value_matrix[1,-x-1] - value_matrix[2,-x-1] \n", 167 | " \n", 168 | "#Export Value Grid to Excel via xlWings\n", 169 | "#xw.view(value_matrix)" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 8, 175 | "metadata": { 176 | "collapsed": false 177 | }, 178 | "outputs": [], 179 | "source": [ 180 | "#Option Valuation Profile in pandas - Index is Strike Price, column 0 is the option price\n", 181 | "value_df = pd.DataFrame(value_matrix)\n", 182 | "value_df = value_df.set_index(asset_price)\n", 183 | "\n", 184 | "#Export Value Grid to Excel via xlWings\n", 185 | "#xw.view(value_df)" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 9, 191 | "metadata": { 192 | "collapsed": false 193 | }, 194 | "outputs": [ 195 | { 196 | "data": { 197 | "text/plain": [ 198 | "" 199 | ] 200 | }, 201 | "execution_count": 9, 202 | "metadata": {}, 203 | "output_type": "execute_result" 204 | }, 205 | { 206 | "data": { 207 | "image/png": "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\n", 208 | "text/plain": [ 209 | "" 210 | ] 211 | }, 212 | "metadata": {}, 213 | "output_type": "display_data" 214 | } 215 | ], 216 | "source": [ 217 | "%matplotlib inline\n", 218 | "#Payoff Plot\n", 219 | "plot_df = value_df.sort_index(ascending=True)\n", 220 | "plot_df[0].plot()" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": null, 226 | "metadata": { 227 | "collapsed": true 228 | }, 229 | "outputs": [], 230 | "source": [] 231 | } 232 | ], 233 | "metadata": { 234 | "anaconda-cloud": {}, 235 | "kernelspec": { 236 | "display_name": "Python [Root]", 237 | "language": "python", 238 | "name": "Python [Root]" 239 | }, 240 | "language_info": { 241 | "codemirror_mode": { 242 | "name": "ipython", 243 | "version": 3 244 | }, 245 | "file_extension": ".py", 246 | "mimetype": "text/x-python", 247 | "name": "python", 248 | "nbconvert_exporter": "python", 249 | "pygments_lexer": "ipython3", 250 | "version": "3.5.2" 251 | } 252 | }, 253 | "nbformat": 4, 254 | "nbformat_minor": 0 255 | } 256 | -------------------------------------------------------------------------------- /Explicit Finite Differences Method - Option Valuation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Explicit Finite Differences Method for Option Valuation\n", 8 | "\n", 9 | "Includes Call and Puts / American and European" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 13, 15 | "metadata": { 16 | "collapsed": true 17 | }, 18 | "outputs": [], 19 | "source": [ 20 | "import math\n", 21 | "import numpy as np\n", 22 | "import pandas as pd\n", 23 | "import xlwings as xw\n", 24 | "import scipy as sp\n", 25 | "from scipy import stats\n", 26 | "import matplotlib as plt" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 14, 32 | "metadata": { 33 | "collapsed": false 34 | }, 35 | "outputs": [ 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "Asset Step Size 5.00 Time Step Size 0.01 Number of Time Steps 72.00 Number of Asset Steps 40.00\n" 41 | ] 42 | } 43 | ], 44 | "source": [ 45 | "T = 1 #Time to Expiry in Years\n", 46 | "E = 100 #Strike\n", 47 | "r = .05 #Risk Free Rate\n", 48 | "SIGMA = .20 #Volatility\n", 49 | "Type = False #Type of Option True=Call False=Put\n", 50 | "Ex = False #Early Exercise True=Yes False=No \n", 51 | "NAS = 40 #Number of Asset Steps - Higher is more accurate, but more time consuming\n", 52 | "\n", 53 | "ds = 2 * E / NAS #Asset Value Step Size\n", 54 | "dt = (0.9/NAS/NAS/SIGMA/SIGMA) #Time Step Size\n", 55 | "NTS = int(T / dt) + 1 #Number of Time Steps\n", 56 | "dt = T / NTS #Time Step Size\n", 57 | "print(\"Asset Step Size %.2f Time Step Size %.2f Number of Time Steps %.2f Number of Asset Steps %.2f\" %(ds, dt, NTS, NAS))" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 15, 63 | "metadata": { 64 | "collapsed": false 65 | }, 66 | "outputs": [], 67 | "source": [ 68 | "#Setup Empty numpy Arrays\n", 69 | "value_matrix = np.zeros((int(NAS+1), int(NTS)))\n", 70 | "asset_price = np.arange(NAS*ds,-1,-ds)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 16, 76 | "metadata": { 77 | "collapsed": false 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "#Evaluate Terminal Value for Calls or Puts\n", 82 | "if Type == True:\n", 83 | " value_matrix[:,-1]= np.maximum(asset_price - E,0)\n", 84 | "else:\n", 85 | " value_matrix[:,-1]= np.maximum(E - asset_price,0)" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 17, 91 | "metadata": { 92 | "collapsed": false 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "#Set Lower Boundry in Grid\n", 97 | "for x in range(1,NTS):\n", 98 | " value_matrix[-1,-x-1] = value_matrix[-1,-x]* math.exp(-r*dt)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 18, 104 | "metadata": { 105 | "collapsed": false 106 | }, 107 | "outputs": [], 108 | "source": [ 109 | "#Set Mid and Ceiling Values in Grid\n", 110 | "for x in range(1,int(NTS)):\n", 111 | "\n", 112 | " for y in range(1,int(NAS)):\n", 113 | " #Evaluate Option Greeks\n", 114 | " Delta = (value_matrix[y-1,-x] - value_matrix[y+1,-x]) / 2 / ds\n", 115 | " Gamma = (value_matrix[y-1,-x] - (2 * value_matrix[y,-x]) + value_matrix[y+1,-x]) / ds / ds\n", 116 | " Theta = (-.5 * SIGMA**2 * asset_price[y]**2 * Gamma) - (r * asset_price[y] * Delta) + (r * value_matrix[y,-x])\n", 117 | " \n", 118 | " #Set Mid Values\n", 119 | " value_matrix[y,-x-1] = value_matrix[y,-x] - Theta * dt\n", 120 | " if Ex == True:\n", 121 | " value_matrix[y,-x-1] = np.maximum(value_matrix[y,-x-1], value_matrix[y,-1])\n", 122 | " \n", 123 | "\n", 124 | " #Set Ceiling Value\n", 125 | " value_matrix[0,-x-1] = 2 * value_matrix[1,-x-1] - value_matrix[2,-x-1] \n", 126 | "\n", 127 | "#Export Value Grid to Excel via xlWings\n", 128 | "xw.view(value_matrix)" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 19, 134 | "metadata": { 135 | "collapsed": false 136 | }, 137 | "outputs": [], 138 | "source": [ 139 | "#Option Valuation Profile in pandas - Index is Strike Price, column 0 is the option price\n", 140 | "value_df = pd.DataFrame(value_matrix)\n", 141 | "value_df = value_df.set_index(asset_price)\n", 142 | "\n", 143 | "#Export Value Grid to Excel via xlWings\n", 144 | "xw.view(value_df)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 25, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "data": { 156 | "text/plain": [ 157 | "" 158 | ] 159 | }, 160 | "execution_count": 25, 161 | "metadata": {}, 162 | "output_type": "execute_result" 163 | }, 164 | { 165 | "data": { 166 | "image/png": "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\n", 167 | "text/plain": [ 168 | "" 169 | ] 170 | }, 171 | "metadata": {}, 172 | "output_type": "display_data" 173 | } 174 | ], 175 | "source": [ 176 | "%matplotlib inline\n", 177 | "#Payoff Plot\n", 178 | "plot_df = value_df.sort_index(ascending=True)\n", 179 | "plot_df[0].plot()\n", 180 | "plot_df[NTS-1].plot()" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "Calculation of Option Value using BSM for Comparison - Works for European Options Only" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 21, 193 | "metadata": { 194 | "collapsed": true 195 | }, 196 | "outputs": [], 197 | "source": [ 198 | "'''BSM VANILLA EUROPEAN OPTION VALUE CALCULATION'''\n", 199 | "def bsm_option_value(S0, E, T, R, SIGMA): \n", 200 | " S0 = float(S0)\n", 201 | " d1 = (math.log(S0/E)+(R+(0.5*SIGMA**2))*T)/(SIGMA*math.sqrt(T))\n", 202 | " d2 = d1-(SIGMA*math.sqrt(T))\n", 203 | " call_value = S0*stats.norm.cdf(d1,0,1) - E*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 204 | " delta_call = stats.norm.cdf(d1,0,1)\n", 205 | " gamma_call = stats.norm.pdf(d1,0,1)/(S0*SIGMA*math.sqrt(T))\n", 206 | " theta_call = -(R*E*math.exp(-R*T)*stats.norm.cdf(d2,0,1))-(SIGMA*S0*stats.norm.pdf(d1,0,1)/(2*math.sqrt(T)))\n", 207 | " rho_call = T*E*math.exp(-R*T)*stats.norm.cdf(d2,0,1)\n", 208 | " vega_call = math.sqrt(T)*S0*stats.norm.pdf(d1,0,1)\n", 209 | " \n", 210 | " put_value = E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1) - (S0*stats.norm.cdf(-d1,0,1))\n", 211 | " delta_put = -stats.norm.cdf(-d1,0,1)\n", 212 | " gamma_put = stats.norm.pdf(d1,0,1)/(S0*SIGMA*math.sqrt(T))\n", 213 | " theta_put = (R*E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1))-(SIGMA*S0*stats.norm.pdf(d1,0,1)/(2*math.sqrt(T)))\n", 214 | " rho_put = -T*E*math.exp(-R*T)*stats.norm.cdf(-d2,0,1)\n", 215 | " vega_put = math.sqrt(T)*S0*stats.norm.pdf(d1,0,1)\n", 216 | " \n", 217 | " return call_value, delta_call, gamma_call, theta_call, rho_call, vega_call, put_value, delta_put, gamma_put, theta_put, rho_put, vega_put" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 22, 223 | "metadata": { 224 | "collapsed": false 225 | }, 226 | "outputs": [ 227 | { 228 | "name": "stdout", 229 | "output_type": "stream", 230 | "text": [ 231 | "5.57352602226\n" 232 | ] 233 | } 234 | ], 235 | "source": [ 236 | "#Run BSM Calculation for values and greeks\n", 237 | "S0 = 100 #Current Value\n", 238 | "R=r\n", 239 | "#BSM function call and output assignment\n", 240 | "call_value, delta_call, gamma_call, theta_call, rho_call, vega_call, put_value, delta_put, gamma_put, theta_put, rho_put, vega_put = bsm_option_value(S0, E, T, R, SIGMA)\n", 241 | "\n", 242 | "if Type == False:\n", 243 | " BSM_val = put_value\n", 244 | "if Type == True:\n", 245 | " BSM_val = call_value\n", 246 | "print(BSM_val)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 23, 252 | "metadata": { 253 | "collapsed": false 254 | }, 255 | "outputs": [ 256 | { 257 | "name": "stdout", 258 | "output_type": "stream", 259 | "text": [ 260 | "5.47684205529\n" 261 | ] 262 | } 263 | ], 264 | "source": [ 265 | "#Finite Differences Method Value at S0\n", 266 | "fd_value = value_df.ix[S0,1]\n", 267 | "print(fd_value)" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 24, 273 | "metadata": { 274 | "collapsed": false 275 | }, 276 | "outputs": [ 277 | { 278 | "name": "stdout", 279 | "output_type": "stream", 280 | "text": [ 281 | "Nominal Difference is 0.0967\n", 282 | "Percent Difference is 1.7347%\n" 283 | ] 284 | } 285 | ], 286 | "source": [ 287 | "#Difference\n", 288 | "diff = put_value - fd_value\n", 289 | "print(\"Nominal Difference is %.4f\" %diff)\n", 290 | "\n", 291 | "pct_diff = abs(diff / put_value)\n", 292 | "print(\"Percent Difference is {percent:.4%}\".format(percent=pct_diff))\n", 293 | "\n", 294 | "#If you want higher accuracy you can increase the number of asset steps" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "metadata": { 301 | "collapsed": false 302 | }, 303 | "outputs": [], 304 | "source": [] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": { 310 | "collapsed": false 311 | }, 312 | "outputs": [], 313 | "source": [] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": { 319 | "collapsed": false 320 | }, 321 | "outputs": [], 322 | "source": [] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "metadata": { 328 | "collapsed": true 329 | }, 330 | "outputs": [], 331 | "source": [] 332 | } 333 | ], 334 | "metadata": { 335 | "anaconda-cloud": {}, 336 | "kernelspec": { 337 | "display_name": "Python [Root]", 338 | "language": "python", 339 | "name": "Python [Root]" 340 | }, 341 | "language_info": { 342 | "codemirror_mode": { 343 | "name": "ipython", 344 | "version": 3 345 | }, 346 | "file_extension": ".py", 347 | "mimetype": "text/x-python", 348 | "name": "python", 349 | "nbconvert_exporter": "python", 350 | "pygments_lexer": "ipython3", 351 | "version": "3.5.2" 352 | } 353 | }, 354 | "nbformat": 4, 355 | "nbformat_minor": 0 356 | } 357 | --------------------------------------------------------------------------------