use crate::instrument::Option;
use crate::normal;
fn _n(d: f64) -> f64 {
return normal::cdf(d, 0., 1.);
}
pub fn bsm(option: Option) -> (f64, f64) {
let x = option.underlying;
let k = option.strike;
let t = option.ttm;
let vol = option.vol;
let r1 = option.quote_rate;
let r2 = option.base_rate;
let d1 = ((x / k).ln() + (r1 - r2 + vol * vol / 2.) * t) / (vol * t.sqrt());
let d2 = d1 - vol * t.sqrt();
let call = x * _n(d1) * (-r2 * t).exp() - k * _n(d2) * (-r1 * t).exp();
let put = k * _n(-d2) * (-r1 * t).exp() - x * _n(-d1) * (-r2 * t).exp();
return (call, put);
}
pub fn garman_kohlhagen(option: Option) -> (f64, f64) {
return bsm(option);
}
pub fn black(option: Option) -> (f64, f64) {
let f = option.underlying;
let k = option.strike;
let t = option.ttm;
let vol = option.vol;
let r = option.quote_rate;
let d1 = ((f / k).ln() + (vol * vol / 2.) * t) / (vol * t.sqrt());
let d2 = d1 - vol * t.sqrt();
let call = (-r * t).exp() * (f * _n(d1) - k * _n(d2));
let put = (-r * t).exp() * (k * _n(-d2) - f * _n(-d1));
return (call, put);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_bsm() {
let option = Option {
underlying: 100.,
strike: 96.,
ttm: 3. / 12.,
vol: 0.2,
quote_rate: 0.02,
base_rate: 0.01,
};
let (call, put) = bsm(option);
assert!((call - 6.3674).abs() < 1e-4);
assert!((put - 2.1383).abs() < 1e-4);
}
#[test]
fn check_garman_kohlhagen() {
let option = Option {
underlying: 1.43,
strike: 1.45,
ttm: 1. / 12.,
vol: 0.3,
quote_rate: 0.05,
base_rate: 0.03,
};
let (call, put) = garman_kohlhagen(option);
assert!((call - 0.041292).abs() < 1e-4);
assert!((put - 0.058833).abs() < 1e-4);
}
#[test]
fn check_black() {
let option = Option {
underlying: 101.,
strike: 100.,
ttm: 1. / 12.,
vol: 0.3,
quote_rate: 0.05,
base_rate: 0.0,
};
let (call, put) = black(option);
assert!((call - 3.977396).abs() < 1e-4);
assert!((put - 2.981554).abs() < 1e-4);
}
}