#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use serde::{Deserialize, Serialize};
use statrs::distribution::{Continuous, ContinuousCDF, Normal};
use statrs::statistics::Distribution;
use std::f64::consts::E;
use std::f64::consts::PI;
use std::f64::NAN;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Option {
pub is_call: bool,
pub S: f64, pub K: f64, pub T: f64,
pub divdend: f64, pub r: f64, }
pub fn d1(op: &Option, vol: f64) -> f64 {
let num = (op.S / op.K).log(E) + (op.r - op.divdend + vol.powf(2.0) / 2.0) * op.T;
let denomin = vol * (op.T.sqrt());
num / denomin
}
fn d2(op: &Option, vol: f64) -> f64 {
d1(op, vol) - vol * (op.T.sqrt())
}
fn discount_r(op: &Option) -> f64 {
(0.0 - op.r * op.T).exp()
}
fn discount_divd(op: &Option) -> f64 {
(0.0 - op.divdend * op.T).exp()
}
impl Option {
pub fn bsm_call(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let num1 = self.S * discount_divd(&self) * norm.cdf(d1(&self, vol));
let num2 = self.K * discount_r(&self) * norm.cdf(d2(&self, vol));
num1 - num2
}
pub fn bsm_put(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let num1 = self.S * discount_divd(&self) * norm.cdf(0.0 - d1(self, vol));
let num2 = self.K * discount_r(&self) * norm.cdf(0.0 - d2(self, vol));
num2 - num1
}
pub fn bsm_price(&self, vol: f64) -> f64 {
if self.is_call {
return self.bsm_call(vol);
}
self.bsm_put(vol)
}
pub fn bsm_vega(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let x = d1(&self, vol);
self.S * self.T.sqrt() * norm.pdf(x) * discount_divd(&self) / 100.0
}
pub fn bsm_implied_vol(&self, mp: f64) -> f64 {
let precise = 1e-3;
let mut l = Vec::new();
let mut start = 0.5;
for n in 0..9000 {
let dif = mp - self.bsm_price(start);
if dif.abs() < precise {
return start;
}
l.push((dif.abs(), start));
let mut veg = self.bsm_vega(start);
if veg == 0. {
veg = 0.01;
}
start = start + precise * 2. * dif / veg;
}
l.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
l[0].1
}
pub fn bsm_delta(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let nd = norm.cdf(d1(&self, vol));
if self.is_call {
return nd * discount_divd(&self);
}
(nd - 1.0) * discount_divd(&self)
}
pub fn bsm_gamma(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let d = d1(&self, vol);
norm.pdf(d) * discount_divd(&self) / (self.S * vol * self.T.sqrt())
}
pub fn bsm_theta(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let first = (0.0 - self.S) * norm.pdf(d1(&self, vol)) * vol * discount_divd(&self)
/ (self.T.sqrt() * 2.0);
if self.is_call {
let second = self.divdend * self.S * norm.cdf(d1(&self, vol)) * discount_divd(&self);
let third = (0.0 - self.r) * self.K * discount_r(&self) * norm.cdf(d2(&self, vol));
return (first + second + third) / 365.0;
}
let second =
-1.0 * self.divdend * self.S * norm.cdf(-1.0 * d1(&self, vol)) * discount_divd(&self);
let third = self.r * self.K * discount_r(&self) * norm.cdf(-1.0 * d2(&self, vol));
(first + second + third) / 365.0
}
pub fn bsm_rho(&self, vol: f64) -> f64 {
let norm = Normal::standard();
let n = self.K * self.T * discount_r(&self) / 100.0;
let x = if self.is_call {
norm.cdf(d2(&self, vol))
} else {
-1.0 * norm.cdf(-1.0 * d2(&self, vol))
};
n * x
}
}
#[test]
#[docify::export]
fn test_check_validate() {
let op2 = Option {
is_call: false,
S: 73110., K: 75000.0, T: 0.12,
divdend: 0., r: 0.013, };
let mp = 2600.;
let iv = op2.bsm_implied_vol(mp);
dbg!(iv);
}