{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "# QuantLab: Stochastics\n", "### [(Go to Quant Lab)](https://israeldi.github.io/quantlab/)\n", "\n", "© Dr. Yves J. Hilpisch | The Python Quants GmbH\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Initially import all the modules we will be using for our notebook" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import math\n", "import numpy as np\n", "import numpy.random as npr \n", "\n", "# COMMAND PROMPT: pip install matplotlib\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "import os" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.3 Pricing European Options by Monte Carlo\n", "\n", "The payoff of a European call option on an index at maturity is given by $h(S_T)\\equiv max\\{S_T-K, 0\\}$, where $S_T$ is the index level at maturity date $T$ and $K$ is the strike price. Given a risk-neutral measure for the relevant stochastic process (e.g., geometric Brownian motion), the price of such an option is given by the formula:\n", "\n", "$$C_{0}=e^{-rT}\\mathbb{E}_{0}^{Q}[h(S_{T})]=e^{-rT}\\intop_{0}^{\\infty}h(s)q(s)ds$$\n", "\n", "The equation below provides the respective Monte Carlo estimator for the European option, where $\\tilde{S}_{T}^{i}$ is the $T$-th simulated index level at maturity.\n", "\n", "$$\\tilde{C}_{0}=e^{-rT}\\frac{1}{I}\\sum_{i=1}^{I}h(\\tilde{S}_{T}^{i})$$\n", "\n", "Consider the following parameterization for the geometric Brownian motion and the valuation function `gbm_mcs_stat()`, taking as a parameter only the strike price. Here, only the index level at maturity is simulated. As a reference, consider the case with a strike price of $K = 105$:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "def bsm_call_value(S0, K, T, r, sigma):\n", " ''' Valuation of European call option in BSM model.\n", " Analytical formula.\n", "\n", " Parameters\n", " ==========\n", " S0: float\n", " initial stock/index level\n", " K: float\n", " strike price\n", " T: float\n", " maturity date (in year fractions)\n", " r: float\n", " constant risk-free short rate\n", " sigma: float\n", " volatility factor in diffusion term\n", "\n", " Returns\n", " =======\n", " value: float\n", " present value of the European call option\n", " '''\n", " from math import log, sqrt, exp\n", " from scipy import stats\n", "\n", " S0 = float(S0)\n", " d1 = (log(S0 / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrt(T))\n", " d2 = (log(S0 / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * sqrt(T))\n", " # stats.norm.cdf --> cumulative distribution function\n", " # for normal distribution\n", " value = (S0 * stats.norm.cdf(d1, 0.0, 1.0) -\n", " K * exp(-r * T) * stats.norm.cdf(d2, 0.0, 1.0))\n", " return value" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "def gen_sn(M, I):\n", " ''' Function to generate random numbers for simulation.\n", " \n", " Parameters\n", " ==========\n", " M: int\n", " number of time intervals for discretization\n", " I: int\n", " number of paths to be simulated\n", " '''\n", " sn = npr.standard_normal((M + 1, I))\n", " return sn" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [], "source": [ "S0 = 100.\n", "r = 0.05\n", "sigma = 0.25\n", "T = 1.0\n", "I = 50000\n", "K = 105" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "uuid": "693f44be-b3dd-4820-9610-a127f0e9b31b" }, "outputs": [], "source": [ "def gbm_mcs_stat(K):\n", " ''' Valuation of European call option in Black-Scholes-Merton\n", " by Monte Carlo simulation (of index level at maturity)\n", " \n", " Parameters\n", " ==========\n", " K: float\n", " (positive) strike price of the option\n", " \n", " Returns\n", " =======\n", " C0: float\n", " estimated present value of European call option\n", " '''\n", " sn = gen_sn(1, I)\n", " \n", " # Simulate Prices at Maturity\n", " \n", " # calculate payoff at maturity\n", " \n", " # calculate discounted price\n", "\n", " return C0" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "uuid": "f325da52-3e45-4e9e-a4a2-067efb1c3bb7" }, "outputs": [ { "data": { "text/plain": [ "9.962393419194543" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The Monte Carlo estimator value for the European call option.\n", "gbm_mcs_stat(K)" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10.00220211715488" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The analytical solution is\n", "bsm_call_value(S0, K, T, r, sigma)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, consider the dynamic simulation approach and allow for European put options in addition to the call option. The function `gbm_mcs_dyna()` implements the algorithm. The code also compares option price estimates for a call and a put stroke at the same level:" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "uuid": "511974d5-5ceb-4b68-bf7f-e01eaa43f7c6" }, "outputs": [], "source": [ "def gbm_mcs_dyna(K):\n", " ''' Valuation of European options in Black-Scholes-Merton\n", " by Monte Carlo simulation (of index level paths)\n", " \n", " Parameters\n", " ==========\n", " K: float\n", " (positive) strike price of the option\n", " option : string\n", " type of the option to be valued ('call', 'put')\n", " \n", " Returns\n", " =======\n", " C0: float\n", " estimated present value of European call option\n", " '''\n", " dt = T / M\n", " \n", " # simulation of index level paths\n", " S = np.zeros((M + 1, I))\n", " S[0] = S0\n", " sn = gen_sn(M, I)\n", " \n", " for t in range(1, M + 1):\n", " S[t] = S[t - 1] * np.exp((r - 0.5 * sigma ** 2) * dt \n", " + sigma * math.sqrt(dt) * sn[t])\n", " \n", " # calculation of payoff\n", " hT = np.maximum(S[-1] - K, 0)\n", " \n", " # calculation of MCS estimator\n", " C0 = math.exp(-r * T) * np.mean(hT)\n", " \n", " return C0" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "uuid": "44ae2961-ec7c-4e69-b6ff-17b8093a894b" }, "outputs": [ { "data": { "text/plain": [ "10.088345691009375" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The Monte Carlo estimator value for the European call option (Dynamic Simulation Approach)\n", "gbm_mcs_dyna(K) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Euler Discretization\n", "\n", "Consider the stochastic differential equation $\\mathrm{d} X_{t}=a\\left(X_{t}\\right) \\mathrm{d} t+b\\left(X_{t}\\right) \\mathrm{d} W_{t}$ with initial condition $X_{0}=x_{0},$ where $W_{t}$ stands for the Wiener process, and suppose that we wish to solve this SDE on some internal of time $[0, T]$. Then the Euler-Maruyama approximation to the true solution $X$ is the Markov chain $Y$ defined as follows:\n", "\n", "- partition the interval $[0,7 \\text { into } N \\text { equal subintervals of width } \\Delta t>0$ :\n", "$$\n", "0=\\tau_{0}<\\tau_{1}<\\cdots<\\tau_{N}=T \\text { and } \\Delta t=T / N\n", "$$\n", "- set $Y_{0}=x_{0}$\n", "- recursively define $Y_{n}$ for $1 \\leq n \\leq N$ by\n", "$$\n", "\\begin{array}{l}\n", "{Y_{n+1}=Y_{n}+a\\left(Y_{n}\\right) \\Delta t+b\\left(Y_{n}\\right) \\Delta W_{n}} \\\\\n", "{\\Delta W_{n}=W_{\\tau_{n+1}}-W_{\\tau_{n}}}\n", "\\end{array}\n", "$$" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [], "source": [ "# Calculate Price with these parameters again\n", "S0 = 100.\n", "r = 0.05\n", "sigma = 0.25\n", "T = 1.0\n", "I = 5000\n", "K = 105" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10.012785056220235" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Finite Difference Scheme\n", "(Next Time!)" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.5" } }, "nbformat": 4, "nbformat_minor": 1 }