use crate::{SpecialFn, lets_be_rational};
use bon::Builder;
#[derive(Builder)]
#[builder(const, derive(Clone, Debug),
finish_fn(name = build_unchecked,
doc{
/// Build without performing any validation.
///
/// This constructor constructs the `PriceBlackScholes` directly from
/// the builder's fields and does **not** check for NaNs, infinities, or
/// sign constraints. Use only when you are certain the inputs are valid
/// or when you want to avoid the cost of runtime validation.
})
)]
pub struct PriceBlackScholes {
forward: f64,
strike: f64,
volatility: f64,
expiry: f64,
is_call: bool,
}
impl<S: price_black_scholes_builder::IsComplete> PriceBlackScholesBuilder<S> {
pub const fn build(self) -> Option<PriceBlackScholes> {
let price_black_scholes = self.build_unchecked();
if !price_black_scholes.forward.is_finite() || !(price_black_scholes.forward > 0.0) {
return None;
}
if !price_black_scholes.strike.is_finite() || !(price_black_scholes.strike > 0.0) {
return None;
}
if !(price_black_scholes.volatility >= 0.0) {
return None;
}
if !(price_black_scholes.expiry >= 0.0) {
return None;
}
Some(price_black_scholes)
}
}
impl PriceBlackScholes {
#[must_use]
#[inline(always)]
pub fn calculate<SpFn: SpecialFn>(&self) -> f64 {
if self.is_call {
lets_be_rational::bs_option_price::black_input_unchecked::<SpFn, true>(
self.forward,
self.strike,
self.volatility,
self.expiry,
)
} else {
lets_be_rational::bs_option_price::black_input_unchecked::<SpFn, false>(
self.forward,
self.strike,
self.volatility,
self.expiry,
)
}
}
}