If you are new to options I strongly advise you to profit from Robert Shiller's lecture on same. It combines practical market insights with a strong authoritative grasp of key models in option theory. He explains many of the areas covered below and in the following pages with a lot intuition and relatable anecdotage. We start here with Black Scholes Merton which is probably the most popular option pricing framework, due largely to its simplicity and ease in terms of implementation. The closed-form solution is efficient in terms of speed and always compares favorably relative to any numerical technique. The Black–Scholes–Merton model is a mathematical go-to model for estimating the value of European calls and puts. In the early 1970’s, Myron Scholes, and Fisher Black made an important breakthrough in the pricing of complex financial instruments. Robert Merton simultaneously was working on the same problem and applied the term Black-Scholes model to describe new generation of pricing. The Black Scholes (1973) contribution developed insights originally proposed by Bachelier 70 years before. In 1997, Myron Scholes and Robert Merton received the Nobel Prize for Economics. Tragically, Fisher Black died in 1995. The Black–Scholes formula presents a theoretical estimate (or model estimate) of the price of European-style options independently of the risk of the underlying security. Future payoffs from options can be discounted using the risk-neutral rate. Earlier academic work on options (e.g., Malkiel and Quandt 1968, 1969) had contemplated using either empirical, econometric analyses or elaborate theoretical models that possessed parameters whose values could not be calibrated directly. In contrast, Black, Scholes, and Merton’s parameters were at their core simple and did not involve references to utility or to the shifting risk appetite of investors. Below, we present a standard type formula, where: c = Call option value, p = Put option value, S=Current stock (or other underlying) price, K or X=Strike price, r=Risk-free interest rate, q = dividend yield, T=Time to maturity and N denotes taking the normal cumulative probability. b = (r - q) = cost of carry.
If the price of a stock adhered to a lognormal random walk in continuous time, and other simplifying assumptions assured, then it was possible to hedge any option transaction flawlessly. A risk neutral world was possible to construct whilst a continuously adjusted portfolio of the underlying security and government bonds or cash combined could “replicate” the option position. The Nobel committee awarded their prize precisely because the trio had unraveled the intuition behind establishing risk neutrality. The formula led to a boom in options trading and provided mathematical legitimacy to the establishment of the Chicago Board Options Exchange and other emergent options markets around the world. The history of how both CBOT and CME lobbies convinced regulators to permit option trading is outlined by Mackenzie and Millo (2003). Risk Neutrality is widely used, although often with some adjustments, by options market participants. Derman and Taleb (2005) heavily criticized the risk neutrality framework as constructed by Black Scholes. See below for a simple implementation in Excel VBA for Black Scholes. For a more exhaustive explanation of Black Scholes please follow link to spreadsheet. For clearer exposition and relevant formulae see Martin Haugh's note. You also check out Martin Haugh's powerpoint pdf for Black Scholes, Greeks and Delta Hedging.
' VBA code from www.kerryback.net
Function Black_Scholes_Call(S, K, r, sigma, q, T)
'
' Inputs are S = initial stock price
' K = strike price
' r = risk-free rate
' sigma = volatility
' q = dividend yield
' T = time to maturity
'
Dim d1, d2, N1, N2
If sigma = 0 Then
Black_Scholes_Call = Application.Max(0, Exp(-q * T) * S - Exp(-r * T) * K)
Else
d1 = (Log(S / K) + (r - q + 0.5 * sigma * sigma) * T) / (sigma * Sqr(T))
d2 = d1 - sigma * Sqr(T)
N1 = Application.NormSDist(d1)
N2 = Application.NormSDist(d2)
Black_Scholes_Call = Exp(-q * T) * S * N1 - Exp(-r * T) * K * N2
End If
End Function
Function Black_Scholes_Put(S, K, r, sigma, q, T)
'
' Inputs are S = initial stock price
' K = strike price
' r = risk-free rate
' sigma = volatility
' q = dividend yield
' T = time to maturity
'
Dim d1, d2, N1, N2
If sigma = 0 Then
Black_Scholes_Put = Application.Max(0, Exp(-r * T) * K - Exp(-q * T) * S)
Else
d1 = (Log(S / K) + (r - q + 0.5 * sigma * sigma) * T) / (sigma * Sqr(T))
d2 = d1 - sigma * Sqr(T)
N1 = Application.NormSDist(-d1)
N2 = Application.NormSDist(-d2)
Black_Scholes_Put = Exp(-r * T) * K * N2 - Exp(-q * T) * S * N1
End If
End Function
Put-call parity defines the relationship between calls, puts and the underlying asset. This principle requires that the puts and calls share the same strike, same expiration and have the same underlying. Put-call parity defines a relationship between the price of a European call option and European put option. Interestingly, De la Vega is known to have had a complete understanding of put-call parity as early as 1688 although in a modern literature this relationship is generally attributed to Stoll (1969).
User-defined forms can be customised to take input from a user in the format of a form. Different sets of controls can be introduced to add text boxes, checkbox labels, etc to assist a user to input a value(s) and store same in the worksheet. See below video explaining how to customise a userform for the Black Scholes (1973) model. Code provide on youtube portal.
See below video for implementation. Also, separately to view explanation of Put call parity please follow link. Put Call Parity is invoked to obtain value of put.
//From Fabrice Rouah
//Adapted by Shang and Byrne for Dividends
#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
// N(0,1) density
double
f (double x)
{
double pi = 4.0 * atan (1.0);
return exp (-x * x * 0.5) / sqrt (2 * pi);
}
// Boole's Rule
double
Boole (double StartPoint, double EndPoint, int n)
{
vector < double >X (n + 1, 0.0);
vector < double >Y (n + 1, 0.0);
double delta_x = (EndPoint - StartPoint) / double (n);
for (int i = 0; i <= n; i++)
{
X[i] = StartPoint + i * delta_x;
Y[i] = f (X[i]);
}
double sum = 0;
for (int t = 0; t <= (n - 1) / 4; t++)
{
int ind = 4 * t;
sum +=
(1 / 45.0) * (14 * Y[ind] + 64 * Y[ind + 1] + 24 * Y[ind + 2] +
64 * Y[ind + 3] + 14 * Y[ind + 4]) * delta_x;
}
return sum;
}
// N(0,1) cdf by Boole's Rule
double
N (double x)
{
return Boole (-10.0, x, 240);
}
// Black-Scholes Call Price
double
BSPrice (double S, double K, double T, double r,double q, double v, char OpType)
{
double d = (log (S / K) + T * (r - q + 0.5 * v * v)) / (v * sqrt (T));
double call = S *exp(-q*T)* N (d) - exp (-r * T) * K * N (d - v * sqrt (T));
if (OpType == 'C')
return call;
else
// Put Parity
return call - S*exp (-q * T) + K * exp (-r * T);
}
int
main ()
{
double S = 100.0; // Stock Price
double K = 100.0; // Strike Price
double T = 1; // Years to maturity
double r = 0.05; // Risk free interest rate
double q = 0.0;
double v = 0.20; // Yearly volatility
char OpType = 'C'; // 'C'all or 'P'ut
cout << "Black Scholes Price " << BSPrice (S, K, T, r, q, v, OpType) << endl;
};
To set up Black Scholes C++ code in Xcode for Mac please follow link.
To set up Black Scholes in Microsoft Visual Studio please follow link.
See below video for implementation. Also note that in the Python code put-call parity is invoked to estimate the value of the put. More on Python and Black Scholes.
## https://github.com/YuChenAmberLu?tab=repositories
## Adapted for Dividends
## import certain packages
from math import log, sqrt, pi, exp
from scipy.stats import norm
#import numpy as np
#import pandas as pd
from pandas import DataFrame
# Underlying price (per share): S;
# Strike price of the option (per share): K;
# Time to maturity (years): T;
# Continuously compounding risk-free interest rate: r;
# Continuously compounding dividend: q;
# Volatility: sigma;
## define two functions, d1 and d2 in Black-Scholes model
def d1(S,K,T,r,q,sigma):
# change sigma*sqrt(T) to (sigma*sqrt(T))
return(log(S/K)+(r - q +sigma**2/2.)*T)/(sigma*sqrt(T))
def d2(S,K,T,r,q,sigma):
return d1(S,K,T,r,q,sigma)-sigma*sqrt(T)
## define the call options price function
def bs_call(S,K,T,r,q,sigma):
return S*exp(-q*T)*norm.cdf(d1(S,K,T,r,q,sigma))-K*exp(-r*T)*norm.cdf(d2(S,K,T,r,q,sigma))
## define the put options price function
def bs_put(S,K,T,r,q,sigma):
return K*exp(-r*T)-S*exp(-q*T)+bs_call(S,K,T,r,q,sigma)
## input the current stock price and check if it is a number.
S = 100
K = 100
T = 1
r = 0.05
q = 0.05
sigma = 0.2
## calculate the call / put option price
price = {'Call' : [bs_call(S,K,T,r,q,sigma)],
'Put' : [bs_put(S,K,T,r,q,sigma)]}
price = DataFrame(price)
The most intuitive way to graph Black Scholes is probably to use excel. Please take a look at spreadsheet and youtube explanation of spreadsheet. To set up preliminaries the excel data table example is useful. The code below was motivated by this example which I will revisit when addressing volatility surface . I combined with Amber Lu's example above to simplify content.
import math
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from math import log, sqrt, pi, exp
from scipy.stats import norm
#import numpy as np
#import pandas as pd
from pandas import DataFrame
# Underlying price (per share): S;
# Strike price of the option (per share): K;
# Time to maturity (years): T;
# Continuously compounding risk-free interest rate: r;
# Continuously compounding dividend: q;
# Volatility: sigma;
## define two functions, d1 and d2 in Black-Scholes model
def d1(S,K,T,r,q,sigma):
# change sigma*sqrt(T) to (sigma*sqrt(T))
return(log(S/K)+(r - q +sigma**2/2.)*T)/(sigma*sqrt(T))
def d2(S,K,T,r,q,sigma):
return d1(S,K,T,r,q,sigma)-sigma*sqrt(T)
## define the call options price function
def bs_call(S,K,T,r,q,sigma):
return S*exp(-q*T)*norm.cdf(d1(S,K,T,r,q,sigma))-K*exp(-r*T)*norm.cdf(d2(S,K,T,r,q,sigma))
## define the put options price function
def bs_put(S,K,T,r,q,sigma):
return K*exp(-r*T)-S*exp(-q*T)+bs_call(S,K,T,r,q,sigma)
# Intrinsic Value and Time value
#S = 100
K = 100
T = 1
r = 0.05
q = 0.0
sigma = 0.2
# Generate spot prices
S = np.linspace(10, 200, 20)
h = np.maximum(S - K, 0) # payoff of the option
C = [bs_call(Szero,K,T,r,q,sigma) for Szero in S] #BS call option values
plt.figure()
plt.plot(S, h, 'b-.', lw=3, label='Intrinsic Value')
plt.plot(S, C, 'r', lw=3, label='Time Value')
plt.grid(True)
plt.legend(loc=0)
plt.xlabel('Spot Price of Underlying')
plt.ylabel('Option Value in Current Time')
print(C)
Cython is a superset programming language of the Python, which makes available C-like performance. Cython can be invoked in the Python environment with optional additional C-inspired syntax. See Google Colab implementation.
Cython is a compiled language. Cython facilitates wrapping independent C or C++ code into python-importable modules. For Black Scholes implementation please see this github and paper. Please also see.
See below video for implementation.
/* http://www.espenhaug.com/black_scholes.html
function BlackScholesdiv(PutCallFlag, S, X, T, r, q, v) {
var d1, d2;
d1 = (Math.log(S / X) + (r - q + v * v / 2.0) * T) / (v * Math.sqrt(T));
d2 = d1 - v * Math.sqrt(T);
if (PutCallFlag== "c")
return S * Math.exp(-q * T)* CND(d1)-X * Math.exp(-r * T) * CND(d2);
else
return X * Math.exp(-r * T) * CND(-d2) - S * Math.exp(-q * T) * CND(-d1);
}
/* The cummulative Normal distribution function: */
function CND(x){
var a1, a2, a3, a4 ,a5, k ;
a1 = 0.31938153, a2 =-0.356563782, a3 = 1.781477937, a4= -1.821255978 , a5= 1.330274429;
if(x<0.0)
return 1-CND(-x);
else
k = 1.0 / (1.0 + 0.2316419 * x);
return 1.0 - Math.exp(-x * x / 2.0)/ Math.sqrt(2*Math.PI) * k
* (a1 + k * (-0.356563782 + k * (1.781477937 + k * (-1.821255978 + k * 1.330274429)))) ;
}
Robert McDonald, Professor of Finance at the Kellogg School of Management, Northwestern University and author of "Derivatives Markets" (Pearson, 2013) has put together the derivmkts package for R. In the video, I show how to do a quick install on RStudio and estimate the Black-Scholes model with same parameters as before.
BlackScholes <- function(S, K, r, q, T, sig, type){
if(type=="C"){
d1 <- (log(S/K) + (r - q + sig^2/2)*T) / (sig*sqrt(T))
d2 <- d1 - sig*sqrt(T)
cbs <- S*exp(-q*T)*pnorm(d1) - K*exp(-r*T)*pnorm(d2)
return(cbs)}
if(type=="P"){
pbs <- (BlackScholes(S, K, r, q, T, sig,"C") + K*exp(-r*T) - S*exp(-q*T))
return(pbs)}
}
call <- BlackScholes(100,100,0.05,0.0, 1,0.2,"C")
put <- BlackScholes(100,100,0.05,0.0, 1,0.2,"P")
call
put
C# is intended to be a simple, modern, general-purpose, object-oriented programming language. The language is intended for use in developing software components suitable for deployment in distributed environments. C# applications are intended to be economical with regard to memory and processing power requirements, the language was not intended to compete directly on performance and size with C or assembly language. The C sharp code below can used to estimate Black Scholes.
namespace BlackScholesCall1
{
class BlackScholesCall
{
static void Main(string[] args)
{
// Option settings
double S = 100.0;
double K = 100.0;
double rf = 0.05;
double q = 0.0;
double T = 1.0;
double v = 0.2;
// Market price
double MktPrice = BlackScholesCall1(S, K, T, rf, q, v);
// Output results
Console.WriteLine("-----------------------------------------------");
Console.WriteLine("Black Scholes Price = {0:F5}", MktPrice);
Console.WriteLine("-----------------------------------------------");
Console.Read(); // I added this
}
// Standard Normal CDF
static double NormCDF(double x)
{
double b0 = 0.2316419;
double b1 = 0.319381530;
double b2 = -0.356563782;
double b3 = 1.781477937;
double b4 = -1.821255978;
double b5 = 1.330274429;
double pi = Math.PI;
double phi = Math.Exp(-x * x / 2.0) / Math.Sqrt(2.0 * pi);
double t, c;
double CDF = 0.5;
if (x > 0.0)
{
t = 1.0 / (1.0 + b0 * x);
CDF = 1.0 - phi * (b1 * t + b2 * Math.Pow(t, 2) + b3 * Math.Pow(t, 3) + b4 * Math.Pow(t, 4) + b5 * Math.Pow(t, 5));
}
else if (x < 0.0)
{
x = -x;
t = 1.0 / (1.0 + b0 * x);
c = 1.0 - phi * (b1 * t + b2 * Math.Pow(t, 2) + b3 * Math.Pow(t, 3) + b4 * Math.Pow(t, 4) + b5 * Math.Pow(t, 5));
CDF = 1.0 - c;
}
return CDF;
}
// Black Scholes Price of Call or put
static double BlackScholesCall1(double S, double K, double T, double rf, double q, double v)
{
double d1 = (Math.Log(S / K) + (rf - q + v * v / 2.0) * T) / v / Math.Sqrt(T);
double d2 = d1 - v * Math.Sqrt(T);
double BSCall = S * Math.Exp(-q * T) * NormCDF(d1) - K * Math.Exp(-rf * T) * NormCDF(d2);
return BSCall;
}
}
}
Financial Algorithms live and are used mainly in software. To gain some insight on how to develop your own software package see code below and follow video clips. (Also, for a very simple C # application please see this very basic video clip).
double S, K, r, q, T, v, Call;
S = double.Parse(tb_S.Text);
K = double.Parse(tb_K.Text);
r = double.Parse(tb_r.Text);
q = double.Parse(tb_q.Text);
T = double.Parse(tb_T.Text);
v = double.Parse(tb_v.Text);
double d1 = (Math.Log(S / K) + (r - q + v * v / 2.0) * T) / v / Math.Sqrt(T);
double d2 = d1 - v * Math.Sqrt(T);
double b0 = 0.2316419;
double b1 = 0.319381530;
double b2 = -0.356563782;
double b3 = 1.781477937;
double b4 = -1.821255978;
double b5 = 1.330274429;
double pi = Math.PI;
double phid1 = Math.Exp(-d1 * d1 / 2.0) / Math.Sqrt(2.0 * pi);
double td1, cd1;
double Nd1 = 0.5;
if (d1 > 0.0)
{
td1 = 1.0 / (1.0 + b0 * d1);
Nd1 = 1.0 - phid1 * (b1 * td1 + b2 * Math.Pow(td1, 2) + b3 * Math.Pow(td1, 3) + b4 * Math.Pow(td1, 4) + b5 * Math.Pow(td1, 5));
}
else if (d1 < 0.0)
{
d1 = -d1;
td1 = 1.0 / (1.0 + b0 * d1);
cd1 = 1.0 - phid1 * (b1 * td1 + b2 * Math.Pow(td1, 2) + b3 * Math.Pow(td1, 3) + b4 * Math.Pow(td1, 4) + b5 * Math.Pow(td1, 5));
Nd1 = 1.0 - cd1;
}
double phid2 = Math.Exp(-d2 * d2 / 2.0) / Math.Sqrt(2.0 * pi);
double td2, cd2;
double Nd2 = 0.5;
if (d2 > 0.0)
{
td2 = 1.0 / (1.0 + b0 * d2);
Nd2 = 1.0 - phid2 * (b1 * td2 + b2 * Math.Pow(td2, 2) + b3 * Math.Pow(td2, 3) + b4 * Math.Pow(td2, 4) + b5 * Math.Pow(td2, 5));
}
else if (d2 < 0.0)
{
d2 = -d2;
td2 = 1.0 / (1.0 + b0 * d2);
cd2 = 1.0 - phid2 * (b1 * td2 + b2 * Math.Pow(td2, 2) + b3 * Math.Pow(td2, 3) + b4 * Math.Pow(td2, 4) + b5 * Math.Pow(td2, 5));
Nd2 = 1.0 - cd2;
}
Call = S * Math.Exp(-q * T) * Nd1 - K * Math.Exp(-r * T) * Nd2;
tb_Call.Text = Call.ToString();
In this video clip, we set out how to take native Black Scholes C++ code and create a MFC dialog box where Black Scholes can be estimated. The user experience then is purely relegated to estimating the Black Scholes model without having to manipulate scripts. MFC allows the C++ code to run as elementary software. In other words, C++ functions can be easily nested in user defined software and this can distributed as an executable file. This is a very primitive form of software development.
#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
// N(0,1) density
double
f(double x)
{
double pi = 4.0 * atan(1.0);
return exp(-x * x * 0.5) / sqrt(2 * pi);
}
// Boole's Rule
double
Boole(double StartPoint, double EndPoint, int n)
{
vector < double >X(n + 1, 0.0);
vector < double >Y(n + 1, 0.0);
double delta_x = (EndPoint - StartPoint) / double(n);
for (int i = 0; i <= n; i++)
{
X[i] = StartPoint + i * delta_x;
Y[i] = f(X[i]);
}
double sum = 0;
for (int t = 0; t <= (n - 1) / 4; t++)
{
int ind = 4 * t;
sum +=
(1 / 45.0) * (14 * Y[ind] + 64 * Y[ind + 1] + 24 * Y[ind + 2] +
64 * Y[ind + 3] + 14 * Y[ind + 4]) * delta_x;
}
return sum;
}
// N(0,1) cdf by Boole's Rule
double
N(double x)
{
return Boole(-10.0, x, 240);
}
// Black-Scholes Call Price
double
BSPrice(double S, double K, double T, double r, double q, double v, char OpType)
{
double d = (log(S / K) + T * (r - q + 0.5 * v * v)) / (v * sqrt(T));
double call = S *exp(-q*T)* N(d) - exp(-r * T) * K * N(d - v * sqrt(T));
if (OpType == 'C')
return call;
else
// Put Parity
return call - S*exp(-q * T) + K * exp(-r * T);
}
int
main()
{
double S = 100.0;// Stock Price
double K = 100.0;// Strike Price
double T = 1;// Years to maturity
double r = 0.05;// Risk free interest rate
double q = 0.0;
double v = 0.20;// Yearly volatility
char OpType = 'C';// 'C'all or 'P'ut
cout << "Black Scholes Price " << BSPrice(S, K, T, r, q, v, OpType) << endl;
system("PAUSE");
}
CString textInput;
GetDlgItemText(txt_S, textInput);
double S = _ttof(textInput);
GetDlgItemText(txt_K, textInput);
double K = _ttof(textInput);
GetDlgItemText(txt_T, textInput);
double T = _ttof(textInput);
GetDlgItemText(txt_r, textInput);
double r = _ttof(textInput);
GetDlgItemText(txt_q, textInput);
double q = _ttof(textInput);
GetDlgItemText(txt_v, textInput);
double v = _ttof(textInput);
double d = (log(S / K) + T * (r - q + 0.5 * v * v)) / (v * sqrt(T));
double call = S *exp(-q*T)* N(d) - exp(-r * T) * K * N(d - v * sqrt(T));
double put = call - S*exp(-q * T) + K * exp(-r * T);
string strCall = to_string(call);
CString dEC(strCall.c_str());
SetDlgItemText(txt_Call, dEC);
In the video below, we complete the MFC dialog box to estimate the value of a Black Scholes call. MFC C++ can be used to develop rudimentary software for financial calculations.