Module raop.pricing_models.black_scholes
Expand source code
import numpy as np
from raop.pricing_models.pricing_model import OptionPricingModel
from raop.utils import logger
from scipy.stats import norm
class BlackScholes(OptionPricingModel):
"""
Subclass of `OptionPricingModel` defining a pricing model that uses the Black-Scholes model.
Attributes:
option (collections.namedtuple): its keys are:
"name"
"option_type"
"s"
"k"
"r"
"time_to_maturity"
"sigma"
name (str): name of the pricing model. Default is "Black-Scholes".
Methods:
**compute_price**: estimate the price of the option described in `option` using Black-Scholes model.
**compute_greeks**: estimate the greeks of the option described in `option` using Black-Scholes model.
"""
def __init__(self, option):
super().__init__(option)
if self.option.name != "european": # for now this model is only adapted to price European options
error_msg = f"This method is only adapted for European options: " \
f"option name must be 'european'."
log.error(error_msg)
raise ValueError(error_msg)
self.name = "Black-Scholes"
def compute_price(self) -> float:
"""
Compute `self.option` 's price using Black-Scholes model.
In this model, prices of European call and put options are given by:
$$Call(S, K) = N(d_1) S - N(d_2) K e^{-r (T - t)}$$
$$Put(S, K) = -N(-d_1) S + N(-d_2) K e^{-r (T - t)}$$
With:
$$N(x) := \mathbb{P}(X \leq x) \quad X \sim \mathcal{N}(0, 1)$$
$$d_1 = ln(\\frac{S}{K}) + \\frac{r + \\frac{\sigma^2}{2}}{\sigma \sqrt{T - t}}$$
$$d_2 = d_1 - \sigma \sqrt{T - t}$$
For further details , see for example
[the Black-Scholes Model Wikipedia page](https://en.wikipedia.org/wiki/Black-Scholes_model).
Returns:
float: option's price estimated with Black-Scholes model.
"""
log.info(f"Started computing option's price with {self.name} method...")
opt = self.option
s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity
price = None
# option's price is estimated by analytical formulas
if opt.option_type == "call":
n_d1 = norm.cdf(self._d1) # norm.cdf is the normal law's cumulative distribution function
n_d2 = norm.cdf(self._d2)
price = n_d1 * s - n_d2 * k * np.exp(-r * dt)
elif opt.option_type == "put":
n_minus_d1 = norm.cdf(-self._d1)
n_minus_d2 = norm.cdf(-self._d2)
price = n_minus_d2 * k * np.exp(-r * dt) - n_minus_d1 * s
log.info(f"Finished computing option's price! [Price = {price}]\n")
return price
def compute_greeks(self) -> dict:
"""
Compute `self.option` 's greeks using Black-Scholes model.
In this model, greeks of European options are given by:
$$\delta^{Call} = N(d_1) \quad \\text{and} \quad \delta^{Put} = N(d_1) - 1$$
$$\gamma = \\frac{N'(d_1)}{S \sigma \sqrt{T - t}}$$
$$\\nu = S N'(d_1) \sqrt{T - t}$$
$$\\theta^{Call} = \\theta_0 - r K e^{-r (T - t)} N(d_2) \quad \\text{and} \quad \\theta^{Put} = \\theta_0 + r K e^{-r (T - t)} N(-d_2)$$
$$\\rho^{Call} = \\rho_0 N(d_2) \quad \\text{and} \quad \\rho^{Put} = -\\rho_0 N(-d_2)$$
With:
$$\\theta_0 = \\frac{S N'(d_1) \sigma}{2 \sqrt{T - t}}$$
$$\\rho_0 = K (T - t) e^{-r (T - t)}$$
See `BlackScholes.compute_price` method for other variables definitions.
Returns:
dict: dictionary containing values of `self.option` 's greeks estimated with Black-Scholes model. Its keys are:
"delta"
"gamma"
"vega"
"theta"
"rho"
"""
log.info(f"Started computing option's Greeks with {self.name} method...")
# all greeks are computed with analytical formulas
greeks = {
"delta": self._delta,
"gamma": self._gamma,
"vega": self._vega,
"theta": self._theta,
"rho": self._rho,
}
log.info(f"Finished computing option's Greeks!\nGreeks: {greeks}\n")
return greeks
@property
def _delta(self) -> float:
log.info(f"Computing Delta...")
opt = self.option
if opt.option_type == "call":
return norm.cdf(self._d1)
elif opt.option_type == "put":
return norm.cdf(self._d1) - 1
@property
def _gamma(self) -> float:
log.info(f"Computing Gamma...")
opt = self.option
s, sigma, dt = opt.s, opt.sigma, opt.time_to_maturity
return self._norm_cdf_derivative(self._d1)/(s * sigma * np.sqrt(dt))
@property
def _vega(self) -> float:
log.info(f"Computing Vega...")
opt = self.option
s, dt = opt.s, opt.time_to_maturity
return s * self._norm_cdf_derivative(self._d1) * np.sqrt(dt)
@property
def _theta(self) -> float:
log.info(f"Computing Theta...")
opt = self.option
s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity
theta0 = - (s * self._norm_cdf_derivative(self._d1) * sigma)/(2 * np.sqrt(dt))
if opt.option_type == "call":
return theta0 - r * k * np.exp(-r * dt) * norm.cdf(self._d2)
elif opt.option_type == "put":
return theta0 + r * k * np.exp(-r * dt) * norm.cdf(-self._d2)
@property
def _rho(self) -> float:
log.info(f"Computing Rho...")
opt = self.option
s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity
rho0 = k * dt * np.exp(-r * dt)
if opt.option_type == "call":
return rho0 * norm.cdf(self._d2)
elif opt.option_type == "put":
return -rho0 * norm.cdf(-self._d2)
@staticmethod
def _norm_cdf_derivative(x):
return np.exp(-x**2 / 2) / np.sqrt(2 * np.pi)
@property
def _d1(self) -> float:
opt = self.option
s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity
return (np.log(s/k) + (r + sigma**2 / 2) * dt) / (sigma * np.sqrt(dt))
@property
def _d2(self) -> float:
opt = self.option
sigma, dt = opt.sigma, opt.time_to_maturity
return self._d1 - sigma * np.sqrt(dt)
log = logger
Classes
class BlackScholes (option)-
Subclass of
OptionPricingModeldefining a pricing model that uses the Black-Scholes model.Attributes
option:collections.namedtuple- its keys are:
"name" "option_type" "s" "k" "r" "time_to_maturity" "sigma" name:str- name of the pricing model. Default is "Black-Scholes".
Methods
compute_price: estimate the price of the option described in
optionusing Black-Scholes model.compute_greeks: estimate the greeks of the option described in
optionusing Black-Scholes model.Expand source code
class BlackScholes(OptionPricingModel): """ Subclass of `OptionPricingModel` defining a pricing model that uses the Black-Scholes model. Attributes: option (collections.namedtuple): its keys are: "name" "option_type" "s" "k" "r" "time_to_maturity" "sigma" name (str): name of the pricing model. Default is "Black-Scholes". Methods: **compute_price**: estimate the price of the option described in `option` using Black-Scholes model. **compute_greeks**: estimate the greeks of the option described in `option` using Black-Scholes model. """ def __init__(self, option): super().__init__(option) if self.option.name != "european": # for now this model is only adapted to price European options error_msg = f"This method is only adapted for European options: " \ f"option name must be 'european'." log.error(error_msg) raise ValueError(error_msg) self.name = "Black-Scholes" def compute_price(self) -> float: """ Compute `self.option` 's price using Black-Scholes model. In this model, prices of European call and put options are given by: $$Call(S, K) = N(d_1) S - N(d_2) K e^{-r (T - t)}$$ $$Put(S, K) = -N(-d_1) S + N(-d_2) K e^{-r (T - t)}$$ With: $$N(x) := \mathbb{P}(X \leq x) \quad X \sim \mathcal{N}(0, 1)$$ $$d_1 = ln(\\frac{S}{K}) + \\frac{r + \\frac{\sigma^2}{2}}{\sigma \sqrt{T - t}}$$ $$d_2 = d_1 - \sigma \sqrt{T - t}$$ For further details , see for example [the Black-Scholes Model Wikipedia page](https://en.wikipedia.org/wiki/Black-Scholes_model). Returns: float: option's price estimated with Black-Scholes model. """ log.info(f"Started computing option's price with {self.name} method...") opt = self.option s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity price = None # option's price is estimated by analytical formulas if opt.option_type == "call": n_d1 = norm.cdf(self._d1) # norm.cdf is the normal law's cumulative distribution function n_d2 = norm.cdf(self._d2) price = n_d1 * s - n_d2 * k * np.exp(-r * dt) elif opt.option_type == "put": n_minus_d1 = norm.cdf(-self._d1) n_minus_d2 = norm.cdf(-self._d2) price = n_minus_d2 * k * np.exp(-r * dt) - n_minus_d1 * s log.info(f"Finished computing option's price! [Price = {price}]\n") return price def compute_greeks(self) -> dict: """ Compute `self.option` 's greeks using Black-Scholes model. In this model, greeks of European options are given by: $$\delta^{Call} = N(d_1) \quad \\text{and} \quad \delta^{Put} = N(d_1) - 1$$ $$\gamma = \\frac{N'(d_1)}{S \sigma \sqrt{T - t}}$$ $$\\nu = S N'(d_1) \sqrt{T - t}$$ $$\\theta^{Call} = \\theta_0 - r K e^{-r (T - t)} N(d_2) \quad \\text{and} \quad \\theta^{Put} = \\theta_0 + r K e^{-r (T - t)} N(-d_2)$$ $$\\rho^{Call} = \\rho_0 N(d_2) \quad \\text{and} \quad \\rho^{Put} = -\\rho_0 N(-d_2)$$ With: $$\\theta_0 = \\frac{S N'(d_1) \sigma}{2 \sqrt{T - t}}$$ $$\\rho_0 = K (T - t) e^{-r (T - t)}$$ See `BlackScholes.compute_price` method for other variables definitions. Returns: dict: dictionary containing values of `self.option` 's greeks estimated with Black-Scholes model. Its keys are: "delta" "gamma" "vega" "theta" "rho" """ log.info(f"Started computing option's Greeks with {self.name} method...") # all greeks are computed with analytical formulas greeks = { "delta": self._delta, "gamma": self._gamma, "vega": self._vega, "theta": self._theta, "rho": self._rho, } log.info(f"Finished computing option's Greeks!\nGreeks: {greeks}\n") return greeks @property def _delta(self) -> float: log.info(f"Computing Delta...") opt = self.option if opt.option_type == "call": return norm.cdf(self._d1) elif opt.option_type == "put": return norm.cdf(self._d1) - 1 @property def _gamma(self) -> float: log.info(f"Computing Gamma...") opt = self.option s, sigma, dt = opt.s, opt.sigma, opt.time_to_maturity return self._norm_cdf_derivative(self._d1)/(s * sigma * np.sqrt(dt)) @property def _vega(self) -> float: log.info(f"Computing Vega...") opt = self.option s, dt = opt.s, opt.time_to_maturity return s * self._norm_cdf_derivative(self._d1) * np.sqrt(dt) @property def _theta(self) -> float: log.info(f"Computing Theta...") opt = self.option s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity theta0 = - (s * self._norm_cdf_derivative(self._d1) * sigma)/(2 * np.sqrt(dt)) if opt.option_type == "call": return theta0 - r * k * np.exp(-r * dt) * norm.cdf(self._d2) elif opt.option_type == "put": return theta0 + r * k * np.exp(-r * dt) * norm.cdf(-self._d2) @property def _rho(self) -> float: log.info(f"Computing Rho...") opt = self.option s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity rho0 = k * dt * np.exp(-r * dt) if opt.option_type == "call": return rho0 * norm.cdf(self._d2) elif opt.option_type == "put": return -rho0 * norm.cdf(-self._d2) @staticmethod def _norm_cdf_derivative(x): return np.exp(-x**2 / 2) / np.sqrt(2 * np.pi) @property def _d1(self) -> float: opt = self.option s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity return (np.log(s/k) + (r + sigma**2 / 2) * dt) / (sigma * np.sqrt(dt)) @property def _d2(self) -> float: opt = self.option sigma, dt = opt.sigma, opt.time_to_maturity return self._d1 - sigma * np.sqrt(dt)Ancestors
Methods
def compute_greeks(self) ‑> dict-
Compute
self.option's greeks using Black-Scholes model.In this model, greeks of European options are given by: \delta^{Call} = N(d_1) \quad \text{and} \quad \delta^{Put} = N(d_1) - 1 \gamma = \frac{N'(d_1)}{S \sigma \sqrt{T - t}} \nu = S N'(d_1) \sqrt{T - t} \theta^{Call} = \theta_0 - r K e^{-r (T - t)} N(d_2) \quad \text{and} \quad \theta^{Put} = \theta_0 + r K e^{-r (T - t)} N(-d_2) \rho^{Call} = \rho_0 N(d_2) \quad \text{and} \quad \rho^{Put} = -\rho_0 N(-d_2)
With: \theta_0 = \frac{S N'(d_1) \sigma}{2 \sqrt{T - t}} \rho_0 = K (T - t) e^{-r (T - t)}
See
BlackScholes.compute_price()method for other variables definitions.Returns
dict- dictionary containing values of
self.option's greeks estimated with Black-Scholes model. Its keys are:"delta" "gamma" "vega" "theta" "rho"
Expand source code
def compute_greeks(self) -> dict: """ Compute `self.option` 's greeks using Black-Scholes model. In this model, greeks of European options are given by: $$\delta^{Call} = N(d_1) \quad \\text{and} \quad \delta^{Put} = N(d_1) - 1$$ $$\gamma = \\frac{N'(d_1)}{S \sigma \sqrt{T - t}}$$ $$\\nu = S N'(d_1) \sqrt{T - t}$$ $$\\theta^{Call} = \\theta_0 - r K e^{-r (T - t)} N(d_2) \quad \\text{and} \quad \\theta^{Put} = \\theta_0 + r K e^{-r (T - t)} N(-d_2)$$ $$\\rho^{Call} = \\rho_0 N(d_2) \quad \\text{and} \quad \\rho^{Put} = -\\rho_0 N(-d_2)$$ With: $$\\theta_0 = \\frac{S N'(d_1) \sigma}{2 \sqrt{T - t}}$$ $$\\rho_0 = K (T - t) e^{-r (T - t)}$$ See `BlackScholes.compute_price` method for other variables definitions. Returns: dict: dictionary containing values of `self.option` 's greeks estimated with Black-Scholes model. Its keys are: "delta" "gamma" "vega" "theta" "rho" """ log.info(f"Started computing option's Greeks with {self.name} method...") # all greeks are computed with analytical formulas greeks = { "delta": self._delta, "gamma": self._gamma, "vega": self._vega, "theta": self._theta, "rho": self._rho, } log.info(f"Finished computing option's Greeks!\nGreeks: {greeks}\n") return greeks def compute_price(self) ‑> float-
Compute
self.option's price using Black-Scholes model.In this model, prices of European call and put options are given by: Call(S, K) = N(d_1) S - N(d_2) K e^{-r (T - t)} Put(S, K) = -N(-d_1) S + N(-d_2) K e^{-r (T - t)}
With: N(x) := \mathbb{P}(X \leq x) \quad X \sim \mathcal{N}(0, 1) d_1 = ln(\frac{S}{K}) + \frac{r + \frac{\sigma^2}{2}}{\sigma \sqrt{T - t}} d_2 = d_1 - \sigma \sqrt{T - t}
For further details , see for example the Black-Scholes Model Wikipedia page.
Returns
float- option's price estimated with Black-Scholes model.
Expand source code
def compute_price(self) -> float: """ Compute `self.option` 's price using Black-Scholes model. In this model, prices of European call and put options are given by: $$Call(S, K) = N(d_1) S - N(d_2) K e^{-r (T - t)}$$ $$Put(S, K) = -N(-d_1) S + N(-d_2) K e^{-r (T - t)}$$ With: $$N(x) := \mathbb{P}(X \leq x) \quad X \sim \mathcal{N}(0, 1)$$ $$d_1 = ln(\\frac{S}{K}) + \\frac{r + \\frac{\sigma^2}{2}}{\sigma \sqrt{T - t}}$$ $$d_2 = d_1 - \sigma \sqrt{T - t}$$ For further details , see for example [the Black-Scholes Model Wikipedia page](https://en.wikipedia.org/wiki/Black-Scholes_model). Returns: float: option's price estimated with Black-Scholes model. """ log.info(f"Started computing option's price with {self.name} method...") opt = self.option s, k, r, sigma, dt = opt.s, opt.k, opt.r, opt.sigma, opt.time_to_maturity price = None # option's price is estimated by analytical formulas if opt.option_type == "call": n_d1 = norm.cdf(self._d1) # norm.cdf is the normal law's cumulative distribution function n_d2 = norm.cdf(self._d2) price = n_d1 * s - n_d2 * k * np.exp(-r * dt) elif opt.option_type == "put": n_minus_d1 = norm.cdf(-self._d1) n_minus_d2 = norm.cdf(-self._d2) price = n_minus_d2 * k * np.exp(-r * dt) - n_minus_d1 * s log.info(f"Finished computing option's price! [Price = {price}]\n") return price