surface_lib/models/bs/
mod.rs

1// A minimal Black-Scholes implementation that provides call and put pricing helpers
2// required by the calibration pipeline.  Implied-volatility and Greeks are
3// intentionally omitted to keep the lightweight focus of surface-lib.
4
5#[allow(non_snake_case)]
6fn norm_cdf(x: f64) -> f64 {
7    // 0.5 * [1 + erf(x / sqrt(2))]
8    0.5 * (1.0 + libm::erf(x / (2.0_f64).sqrt()))
9}
10
11/// Price of a European call option under Black-Scholes assumptions.
12#[allow(non_snake_case)]
13pub fn bs_call_price(S: f64, K: f64, r: f64, q: f64, T: f64, sigma: f64) -> f64 {
14    if T <= 0.0 || sigma <= 0.0 {
15        return (S * (-q * T).exp() - K * (-r * T).exp()).max(0.0);
16    }
17    let d1 = ((S / K).ln() + (r - q + 0.5 * sigma.powi(2)) * T) / (sigma * T.sqrt());
18    let d2 = d1 - sigma * T.sqrt();
19    S * (-q * T).exp() * norm_cdf(d1) - K * (-r * T).exp() * norm_cdf(d2)
20}
21
22/// Price of a European put option under Black-Scholes assumptions.
23#[allow(non_snake_case)]
24pub fn bs_put_price(S: f64, K: f64, r: f64, q: f64, T: f64, sigma: f64) -> f64 {
25    if T <= 0.0 || sigma <= 0.0 {
26        return (K * (-r * T).exp() - S * (-q * T).exp()).max(0.0);
27    }
28    let d1 = ((S / K).ln() + (r - q + 0.5 * sigma.powi(2)) * T) / (sigma * T.sqrt());
29    let d2 = d1 - sigma * T.sqrt();
30    let nd1m = 1.0 - norm_cdf(d1);
31    let nd2m = 1.0 - norm_cdf(d2);
32    K * (-r * T).exp() * nd2m - S * (-q * T).exp() * nd1m
33}