##Introduction

The Black-Scholes model was first introduced by Fischer Black and Myron Scholes in 1973 in the paper "The Pricing of Options and Corporate Liabilities". Since being published, the model has become a widely used tool by investors and is still regarded as one of the best ways to determine fair prices of options.

The purpose of the model is to determine the price of a vanilla European call and put options (option that can only be exercised at the end of its maturity) based on price variation over time and assuming the asset has a lognormal distribution.

## Assumptions

To determine the price of vanilla European options, several assumptions are made:

* European options can only be exercised at expiration
* No dividends are paid during the option's life
* Market movements cannot be predicted
* The risk-free rate and volatility are constant
* Follows a lognormal distribution

## Non-Dividend Paying Black-Scholes Formula

In Black-Scholes formulas, the following parameters are defined.

* $S$, the spot price of the asset at time $t$
* $T$, the maturity of the option. Time to maturity is defined as $T - t$
* $K$, strike price of the option
* $r$, the risk-free interest rate, assumed to be constant between $t$ and $T$
* $\sigma$, volatility of underlying asset, the standard deviation of the asset returns

#### $N(d)$ is the cumulative distribution of the standard normal variable Z

$$N(d) = \frac{1}{\sqrt{2\pi}} \int_{-\infty}^d e^{-\frac{1}{2}x^2} dx$$

$C(S,t)$ is the value at time $t$ of a call option and $P(S,t)$ is the value at time $t$ of a put option. 

The Black-Scholes call formula is given as:

$$C(S,t) = SN(d_1) - Ke^{-r(T - t)} N(d_2)$$

The put formula is given:

$$P(S,t) = Ke^{-r(T - t)}N(-d_2) - SN(-d_1)$$

Where:

$$d_1 = \frac{\ln \left(\frac{S}{K} \right) + \left(r + \frac{\sigma^2}{2} \right)(T - t)}{\sigma \sqrt{T - t}}$$

$$d_2 = d_1 - \sigma \sqrt{T - t} = \frac{\ln \left(\frac{S}{K} \right) + \left(r - \frac{\sigma^2}{2}\right)(T - t)}{\sigma \sqrt{T - t}}$$

## Python Implementation of Black-Scholes formula for non-dividend paying options


```python
import numpy as np
import scipy.stats as si
import sympy as sy
import sympy.statistics as systats
```


```python
def euro_vanilla_call(S, K, T, r, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset
    
    d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    call = (S * si.norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(d2, 0.0, 1.0))
    
    return call
```


```python
euro_vanilla_call(50, 100, 1, 0.05, 0.25)
```




    0.027352509369436617




```python
def euro_vanilla_put(S, K, T, r, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset
    
    d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    put = (K * np.exp(-r * T) * si.norm.cdf(-d2, 0.0, 1.0) - S * si.norm.cdf(-d1, 0.0, 1.0))
    
    return put
```


```python
euro_vanilla_put(50, 100, 1, 0.05, 0.25)
```




    45.150294959440842



The next function can be called with 'call' or 'put' for the option parameter to calculate the desired option


```python
def euro_vanilla(S, K, T, r, sigma, option = 'call'):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset
    
    d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    if option == 'call':
        result = (S * si.norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(d2, 0.0, 1.0))
    if option == 'put':
        result = (K * np.exp(-r * T) * si.norm.cdf(-d2, 0.0, 1.0) - S * si.norm.cdf(-d1, 0.0, 1.0))
        
    return result
```


```python
euro_vanilla(50, 100, 1, 0.05, 0.25, option = 'put')
```




    45.150294959440842



Sympy implementation for Exact Results


```python
def euro_call_sym(S, K, T, r, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset
    
    N = systats.Normal(0.0, 1.0)
    
    d1 = (sy.ln(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    d2 = (sy.ln(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    
    call = (S * N.cdf(d1) - K * sy.exp(-r * T) * N.cdf(d2))
    
    return call
```


```python
euro_call_sym(50, 100, 1, 0.05, 0.25)
```




    -25.0*erf(1.22379436111989*sqrt(2)) - 22.5614712250357 + 47.5614712250357*erf(1.34879436111989*sqrt(2))




```python
def euro_put_sym(S, K, T, r, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset
    
    N = systats.Normal(0.0, 1.0)
    
    d1 = (sy.ln(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    d2 = (sy.ln(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    
    put = (K * sy.exp(-r * T) * N.cdf(-d2) - S * N.cdf(-d1))
    
    return put
```

Sympy implementation of the above function that enables one to specify a call or put result.


```python
def sym_euro_vanilla(S, K, T, r, sigma, option = 'call'):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset
    
    N = systats.Normal(0.0, 1.0)
    
    d1 = (sy.ln(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    d2 = (sy.ln(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    
    if option == 'call':
        result = (S * N.cdf(d1) - K * sy.exp(-r * T) * N.cdf(d2))
    if option == 'put':
        result = (K * sy.exp(-r * T) * N.cdf(-d2) - S * N.cdf(-d1))
        
    return result
```


```python
sym_euro_vanilla(50, 100, 1, 0.05, 0.25, option = 'put')
```




    -25.0*erf(1.22379436111989*sqrt(2)) + 22.5614712250357 + 47.5614712250357*erf(1.34879436111989*sqrt(2))



## Dividend Paying Black-Scholes Formula

For assets that pay dividends, the Black-Scholes formula is rather similar to the non-dividend paying asset formula; however, a new parameter $q$, is added. 

* $S$, the spot price of the asset at time $t$
* $T$, the maturity of the option. Time to maturity is defined as $T - t$
* $K$, strike price of the option
* $r$, the risk-free interest rate, assumed to be constant between $t$ and $T$
* $\sigma$, volatility of underlying asset, the standard deviation of the asset returns
* $q$, the dividend rate of the asset. This is assumed to pay dividends at a continuous rate

In this case, the $q$ parameter is now included in $C(S,t)$ and $P(S,t)$.

$$C(S,t) = Se^{-q(T - t)} N(d_1) - Ke^{-r(T - t)} N(d_2)$$

$$P(S,t) = Ke^{-r(T - t)} N(-d_2) - Se^{-q(T - t)} N(-d_1)$$

Then, $d_1$ and $d_2$ are slightly modified to include the continuous dividends

$$d_1 = \frac{ln \left(\frac{S}{K} \right) + \left(r - q + \frac{\sigma^2}{2} \right)(T - t)}{\sigma \sqrt{T - t}}$$

$$d_2 = d_1 - \sigma \sqrt{T - t} = \frac{ln (\frac{S}{K}) + (r - q - \frac{\sigma^2}{2})(T - t)}{\sigma \sqrt{T - t}}$$

## Python Implementation


```python
def black_scholes_call_div(S, K, T, r, q, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset 
    #sigma: volatility of underlying asset
    
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    call = (S * np.exp(-q * T) * si.norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(d2, 0.0, 1.0))
    
    return call
```


```python
def black_scholes_put_div(S, K, T, r, q, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset 
    #sigma: volatility of underlying asset
    
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    put = (K * np.exp(-r * T) * si.norm.cdf(-d2, 0.0, 1.0) - S * np.exp(-q * T) * si.norm.cdf(-d1, 0.0, 1.0))
    
    return put
```

Implementation that can be used to determine the put or call option price depending on specification


```python
def euro_vanilla_dividend(S, K, T, r, q, sigma, option = 'call'):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset 
    #sigma: volatility of underlying asset
    
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    
    if option == 'call':
        result = (S * np.exp(-q * T) * si.norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(d2, 0.0, 1.0))
    if option == 'put':
        result = (K * np.exp(-r * T) * si.norm.cdf(-d2, 0.0, 1.0) - S * np.exp(-q * T) * si.norm.cdf(-d1, 0.0, 1.0))
        
    return result
```

Sympy Implementation of Black-Scholes with Dividend-paying asset


```python
def black_scholes_call_div_sym(S, K, T, r, q, sigma):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset 
    #sigma: volatility of underlying asset
    
    N = systats.Normal(0.0, 1.0)
    
    d1 = (sy.ln(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    d2 = (sy.ln(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    
    call = S * sy.exp(-q * T) * N.cdf(d1) - K * sy.exp(-r * T) * N.cdf(d2)
    
    return call
```


```python
def black_scholes_call_put_sym(S, K, T, r, q, sigma):

    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset 
    #sigma: volatility of underlying asset
    
    N = systats.Normal(0.0, 1.0)
    
    d1 = (sy.ln(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    d2 = (sy.ln(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    
    put = K * sy.exp(-r * T) * N.cdf(-d2) - S * sy.exp(-q * T) * N.cdf(-d1)
    
    return put
```

Sympy implementation of pricing a European put or call option depending on specification


```python
def sym_euro_vanilla_dividend(S, K, T, r, q, sigma, option = 'call'):
    
    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #q: rate of continuous dividend paying asset 
    #sigma: volatility of underlying asset
    
    N = systats.Normal(0.0, 1.0)
    
    d1 = (sy.ln(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    d2 = (sy.ln(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * sy.sqrt(T))
    
    if option == 'call':
        result = S * sy.exp(-q * T) * N.cdf(d1) - K * sy.exp(-r * T) * N.cdf(d2)
    if option == 'put':
        result = K * sy.exp(-r * T) * N.cdf(-d2) - S * sy.exp(-q * T) * N.cdf(-d1)
        
    return result
```


```python
from IPython.core.display import HTML
def css():
    style = open("css/custom.css", "r").read()
    return HTML(style)
css()
```




/*
Placeholder for custom user CSS

mainly to be overridden in profile/static/custom/custom.css

This will always be an empty file in IPython
*/
<style>
@import url(http://fonts.googleapis.com/css?family=Lato|Source+Code+Pro|Montserrat:400,700);

body {
	font-family: 'Lato', sans-serif !important;
	font-size: 1.25em !important;
	line-height: 30px !important;
	font-weight: 400 !important;
	color: #8d8d8d !important;
}

#notebook-container {
	-webkit-box-shadow: none;
	box-shadow: none;
}

.rendered_html h1 { font-size: 4rem !important;  }
.rendered_html h2 { font-size: 3rem !important; }
.rendered_html h3 { font-size: 2.5rem !important; }
.rendered_html h4 { font-size: 2rem !important; }
.rendered_html h5 { font-size: 1.5rem !important; }
.rendered_html h6 { font-size: 1.5rem !important; }

.rendered_html h1, 
.rendered_html h2, 
.rendered_html h3, 
.rendered_html h4,
.rendered_html h5, 
.rendered_html h6 {
	font-family: 'Montserrat', sans-serif !important;
	font-weight: 300 !important;
	line-height: 1.5em !important;
	color: rgb(221, 153, 51) !important;
}

h1 { font-size: 4.5rem !important; }
h2 { font-size: 4rem !important; }
h3 { font-size: 3.5rem !important; }
h4 { font-size: 3rem !important; }
h5 { font-size: 2.5rem !important; }
h6 { font-size: 2rem !important; }

h1, h2, h3, h4, h5, h6 {
	font-family: 'Montserrat', sans-serif !important;
	color: #e6ae48 !important;
	line-height: 150px !important;
}

p {
	font-family: 'Lato', sans-serif !important;
	font-size: 1.25em !important;
	line-height: 30px !important;
	font-weight: 400 !important;
	color: #8d8d8d !important;
}

code {
	font-family: 'Source Code Pro', sans-serif !important;
	font-size: 1em !important;
}

pre {
	font-family: 'Source Code Pro', sans-serif !important;
	font-size: 1em !important;
}

div.input_area {
	border: none !important;
	background: whitesmoke !important;
}
</style>



06a5be84bb05c3f0a8844276b4c6a17f919585ea
ipynb
Jupyter Notebook
Finance/Black-Scholes Formula.ipynb
DiogoRibeiro7/Medium-Blog
26271e468f950a0454543dbb6f7d07b485cee18f
Apache-2.0
2019-06-01T12:35:38.000Z
2020-04-26T05:50:47.000Z
Finance/Black-Scholes Formula.ipynb
DiogoRibeiro7/Medium-Blog
26271e468f950a0454543dbb6f7d07b485cee18f
Apache-2.0
2020-01-28T22:12:15.000Z
2021-08-25T14:46:12.000Z
Finance/Black-Scholes Formula.ipynb
DiogoRibeiro7/Medium-Blog
26271e468f950a0454543dbb6f7d07b485cee18f
Apache-2.0
2019-06-06T12:58:33.000Z
2019-06-06T15:23:42.000Z
Qwen/Qwen-72B
1. YES
2. YES