Black Scholes

·4 min read·Sashank Neupane

1. Model Assumptions

Price dynamic (real-world):

dSt=μStdt+σStdWtdS_t = \mu S_t\,dt + \sigma S_t\,dW_t

Risk-neutral measure:

μrdSt=rStdt+σStdWt\mu \rightarrow r \Rightarrow dS_t = r S_t\,dt + \sigma S_t\,dW_t

Key fairy-tale assumptions: frictionless markets, continuous trading, constant rr and σ\sigma, no jumps, no arbitrage.


2. Ito's Lemma ⇒ Black–Scholes PDE

V=V(S,t)dV=Vtdt+VSdS+122VS2(dS)2V = V(S,t) \Rightarrow dV = \frac{\partial V}{\partial t}dt + \frac{\partial V}{\partial S}dS + \tfrac{1}{2}\frac{\partial^2 V}{\partial S^2}(dS)^2

Since (dS)2=σ2S2dt(dS)^2 = \sigma^2 S^2 dt and dS=rSdt+σSdWtdS = rS\,dt + \sigma S\,dW_t:

dV=(Vt+rSVS+12σ2S22VS2)dt+σSVSdWtdV = \left(\frac{\partial V}{\partial t} + r S \frac{\partial V}{\partial S} + \tfrac{1}{2}\sigma^2 S^2 \frac{\partial^2 V}{\partial S^2}\right) dt + \sigma S \frac{\partial V}{\partial S} dW_t

Delta-hedge away dWtdW_t risk and earn rr:

Vt+rSVS+12σ2S22VS2rV=0\frac{\partial V}{\partial t} + r S \frac{\partial V}{\partial S} + \tfrac{1}{2}\sigma^2 S^2 \frac{\partial^2 V}{\partial S^2} - rV = 0

3. Closed-Form Call/Put

d1=ln(S/K)+(r+12σ2)(Tt)σTt,d2=d1σTtd_1 = \frac{\ln(S/K) + \left(r + \tfrac{1}{2}\sigma^2\right)(T-t)} {\sigma \sqrt{T-t}},\qquad d_2 = d_1 - \sigma\sqrt{T-t} C=SN(d1)Ker(Tt)N(d2)C = S\,N(d_1) - K e^{-r(T-t)} N(d_2) P=Ker(Tt)N(d2)SN(d1)P = K e^{-r(T-t)} N(-d_2) - S\,N(-d_1)

where N()N(\cdot) is the standard normal CDF.


4. Greeks (Analytical)

Δcall=N(d1),Δput=N(d1)1\Delta_{\text{call}} = N(d_1),\quad \Delta_{\text{put}} = N(d_1) - 1 Γ=N(d1)SσTt=1SσTt12πed12/2\Gamma = \frac{N'(d_1)}{S \sigma \sqrt{T-t}} = \frac{1}{S \sigma \sqrt{T-t}}\frac{1}{\sqrt{2\pi}}e^{-d_1^2/2} Θcall=SN(d1)σ2TtrKer(Tt)N(d2)\Theta_{\text{call}} = -\frac{S N'(d_1)\sigma}{2\sqrt{T-t}} - rK e^{-r(T-t)} N(d_2) Vega=Vσ=STtN(d1)\text{Vega} = \frac{\partial V}{\partial \sigma} = S \sqrt{T-t}\, N'(d_1) ρcall=(Tt)Ker(Tt)N(d2)\rho_{\text{call}} = (T-t) K e^{-r(T-t)} N(d_2)

5. NumPy Implementation

python
# black_scholes_numpy.py
import numpy as np
from scipy.stats import norm
def d1_d2(S, K, r, sigma, T):
"""Return d1, d2 for arrays or scalars."""
t_sqrt = np.sqrt(T)
d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * t_sqrt)
d2 = d1 - sigma * t_sqrt
return d1, d2
def call_price(S, K, r, sigma, T):
d1, d2 = d1_d2(S, K, r, sigma, T)
return S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
def put_price(S, K, r, sigma, T):
d1, d2 = d1_d2(S, K, r, sigma, T)
return K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
def delta_call(S, K, r, sigma, T):
d1, _ = d1_d2(S, K, r, sigma, T)
return norm.cdf(d1)
def gamma(S, K, r, sigma, T):
d1, _ = d1_d2(S, K, r, sigma, T)
return norm.pdf(d1) / (S * sigma * np.sqrt(T))
def vega(S, K, r, sigma, T):
d1, _ = d1_d2(S, K, r, sigma, T)
return S * norm.pdf(d1) * np.sqrt(T)

6. JAX Autodiff Variant

python
# black_scholes_jax.py
import jax.numpy as jnp
from jax import grad, jit
from jax.scipy.stats.norm import cdf, pdf
@jit
def d1_d2_jax(S, K, r, sigma, T):
t_sqrt = jnp.sqrt(T)
d1 = (jnp.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * t_sqrt)
d2 = d1 - sigma * t_sqrt
return d1, d2
@jit
def call_price_jax(S, K, r, sigma, T):
d1, d2 = d1_d2_jax(S, K, r, sigma, T)
return S * cdf(d1) - K * jnp.exp(-r * T) * cdf(d2)
delta_jax = jit(grad(call_price_jax, argnums=0))
vega_jax = jit(grad(call_price_jax, argnums=3))

7. Local Volatility (Dupire-esque)

σlocal(K,T)=CT(K,T)+rKCK(K,T)12K22CK2(K,T)\sigma_{\text{local}}(K,T) = \sqrt{ \frac{ \frac{\partial C}{\partial T}(K,T) + r K \frac{\partial C}{\partial K}(K,T) }{ \frac{1}{2} K^2 \frac{\partial^2 C}{\partial K^2}(K,T) } }

Approx via implied vol surface σimp(K,T)\sigma_{\text{imp}}(K,T):

σlocalσimp2+2σimpTσimpT1+2d1TσimpKKσimp+d12T(σimpK)2K2\sigma_{\text{local}} \approx \sqrt{ \frac{ \sigma_{\text{imp}}^{2} + 2 \sigma_{\text{imp}} T \frac{\partial \sigma_{\text{imp}}}{\partial T} }{ 1 + 2 d_1 \sqrt{T} \frac{\partial \sigma_{\text{imp}}}{\partial K} \frac{K}{\sigma_{\text{imp}}} + d_1^2 T \left(\frac{\partial \sigma_{\text{imp}}}{\partial K}\right)^2 K^2 } }

8. Quick Feedback Widget (Optional React)

tsx
export function Feedback() {
const [done, setDone] = useState(false)
const send = (val: 'yes' | 'no') => {
// POST to API
setDone(true)
}
if (done) return <p>Thanks!</p>
return (
<div style={{ marginTop: 32 }}>
<p>Was this clear?</p>
<button onClick={() => send('yes')}>Yes</button>
<button onClick={() => send('no')}>No</button>
</div>
)
}

9. Next Steps

  • Calibrate σimp(K,T)\sigma_{\text{imp}}(K,T) to market data.
  • Extend to stochastic vol (Heston), jumps (Merton), or mixed models.
  • Study discrete hedging errors & transaction costs.

Implementation Note: The NumPy implementation above is production-ready for most use cases. The JAX variant offers automatic differentiation for Greeks and can be significantly faster on GPUs for large-scale computations.

0 views