pub struct OptionPricingModel {
pub strike_price: f64,
pub underlying_price: f64,
pub volatility: f64,
pub time_to_expiration: f64,
}
impl OptionPricingModel {
pub fn new(strike_price: f64, underlying_price: f64, volatility: f64, time_to_expiration: f64) -> Self {
OptionPricingModel {
strike_price,
underlying_price,
volatility,
time_to_expiration,
}
}
pub fn black_scholes_call(&self) -> f64 {
let d1 = (self.underlying_price.ln() + (self.volatility.powi(2) / 2.0) * self.time_to_expiration)
/ (self.volatility * self.time_to_expiration.sqrt());
let d2 = d1 - self.volatility * self.time_to_expiration.sqrt();
let call_price = self.underlying_price * self.norm_cdf(d1) - self.strike_price * self.norm_cdf(d2);
call_price
}
pub fn black_scholes_put(&self) -> f64 {
let d1 = (self.underlying_price.ln() + (self.volatility.powi(2) / 2.0) * self.time_to_expiration)
/ (self.volatility * self.time_to_expiration.sqrt());
let d2 = d1 - self.volatility * self.time_to_expiration.sqrt();
let put_price = self.strike_price * self.norm_cdf(-d2) - self.underlying_price * self.norm_cdf(-d1);
put_price
}
fn norm_cdf(&self, x: f64) -> f64 {
0.5 * (1.0 + self.erf(x / (2.0_f64).sqrt()))
}
fn erf(&self, x: f64) -> f64 {
let a1 = 0.254829592;
let a2 = -0.284496736;
let a3 = 1.421413741;
let a4 = -1.453152027;
let a5 = 1.061405429;
let p = 0.3275911;
let sign = if x < 0.0 { -1.0 } else { 1.0 };
let x = x.abs();
let t = 1.0 / (1.0 + p * x);
let y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * (-x * x).exp();
sign * y
}
}
pub fn calculate_price(strike: f64, underlying: f64, time: f64, _rate: f64, volatility: f64) -> f64 {
let model = OptionPricingModel::new(strike, underlying, volatility, time);
model.black_scholes_call()
}