Black-Scholes Option Pricing Model

European option pricing, arbitrage bounds, and implied volatility

1. Black-Scholes Formulas

Option Price

Call:

$$C = S_0 \, e^{-qT} \, N(d_1) - K \, e^{-rT} \, N(d_2)$$

Put:

$$P = K \, e^{-rT} \, N(-d_2) - S_0 \, e^{-qT} \, N(-d_1)$$

Intermediate Variables

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

$$d_2 = d_1 - \sigma\sqrt{T}$$

Parameters

SymbolDescription
$S_0$Current stock price
$K$Strike price
$T$Time to expiration (years)
$r$Risk-free interest rate (continuous)
$q$Continuous dividend yield
$\sigma$Volatility of the underlying
$N(\cdot)$Standard normal CDF

Arbitrage Boundaries

Call bounds:

$$\max\!\left(S_0 e^{-qT} - K e^{-rT},\; 0\right) \leq C \leq S_0 e^{-qT}$$

Put bounds:

$$\max\!\left(K e^{-rT} - S_0 e^{-qT},\; 0\right) \leq P \leq K e^{-rT}$$

Implied Volatility

Find $\sigma_{\text{IV}}$ such that:

$$C_{\text{BS}}(S_0, K, T, r, q, \sigma_{\text{IV}}) = C_{\text{market}}$$

Solved numerically via Brent's method on the interval $\sigma \in [10^{-6}, 10]$.


2. Implementation

import numpy as np
from scipy.stats import norm
from scipy.optimize import brentq

2.1 Core Functions — $d_1$, $d_2$, and pricing

S = 100.0          # Spot price
K = 100.0          # Strike price
T = 1.0            # Time to expiry in years
r = 0.05           # Risk-free rate
q = 0.0            # Dividend yield
sigma = 0.20       # Volatility
option_type = "call"
def d1(S, K, T, r, q, sigma):
    return (np.log(S / K) + (r - q + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))


def d2(S, K, T, r, q, sigma):
    return d1(S, K, T, r, q, sigma) - sigma * np.sqrt(T)
def bs_price(S, K, T, r, q, sigma, option_type):

    d1_val = d1(S, K, T, r, q, sigma)
    d2_val = d2(S, K, T, r, q, sigma)

    if option_type == "call":
        price = S * np.exp(-q * T) * norm.cdf(d1_val) - K * np.exp(-r * T) * norm.cdf(d2_val)
    elif option_type == "put":
        price = K * np.exp(-r * T) * norm.cdf(-d2_val) - S * np.exp(-q * T) * norm.cdf(-d1_val)
    else:
        raise ValueError(f"option_type must be 'call' or 'put', got '{option_type}'")

    return price

2.3 Price the Option

sigma = 0.5
d1_val = d1(S, K, T, r, q, sigma)
d2_val = d2(S, K, T, r, q, sigma)
Nd1_val = norm.cdf(d1_val)
Nd2_val = norm.cdf(d2_val)
price  = bs_price(S, K, T, r, q, sigma, option_type)

print(f"d1    = {d1_val:.6f}")
print(f"d2    = {d2_val:.6f}")
print(f"Nd1    = {Nd1_val:.6f}")
print(f"Nd2    = {Nd2_val:.6f}")
print(f"\n price = ${price:.4f}")

3. Arbitrage Boundary Values

Every valid option price must fall within these no-arbitrage bounds.

def price_bounds(S, K, T, r, q, option_type):
    pv_S = S * np.exp(-q * T)   # PV of the stock (adjusted for dividends)
    pv_K = K * np.exp(-r * T)   # PV of the strike

    if option_type == "call":
        lower = max(pv_S - pv_K, 0.0)
        upper = pv_S
    elif option_type == "put":
        lower = max(pv_K - pv_S, 0.0)
        upper = pv_K
    else:
        raise ValueError(f"option_type must be 'call' or 'put', got '{option_type}'")

    return lower, upper

lower, upper = price_bounds(S, K, T, r, q, option_type)


print(f"Lower bound : ${lower:.4f}")
print(f"BS price    : ${price:.4f}")
print(f"Upper bound : ${upper:.4f}")
print(f"\nBounds satisfied: {lower <= price <= upper}")

4. Implied Volatility

Given a market price, recover $\sigma_{\text{IV}}$ using scipy.optimize.brentq.

def implied_volatility(S, K, T, r, q, market_price, option_type,
                       sigma_lo=1e-6, sigma_hi=10.0, tol=1e-12):

    lower, upper = price_bounds(S, K, T, r, q, option_type)
    if market_price < lower:
        raise ValueError(f"Market price ${market_price:.4f} < lower bound ${lower:.4f}")
    if market_price > upper:
        raise ValueError(f"Market price ${market_price:.4f} > upper bound ${upper:.4f}")

    def objective(sigma):
        return bs_price(S, K, T, r, q, sigma, option_type) - market_price

    sigma_iv = brentq(objective, sigma_lo, sigma_hi, xtol=tol)
    return sigma_iv
market_price = 8
implied_volatility(S, K, T, r, q, market_price, option_type,
                       sigma_lo=1e-6, sigma_hi=10.0, tol=1e-12)
David Arias, CFA
Written and Modelled by

David Arias, CFA

Licensed portfolio manager with 4+ years of experience, specializing in emerging markets private debt, derivatives, and quantitative finance.

Let's Connect