implied_vol/builder/
price_bachelier.rs

1use crate::{SpecialFn, lets_be_rational};
2use bon::Builder;
3
4#[derive(Builder)]
5#[builder(const, derive(Clone, Debug),
6finish_fn(name = build_unchecked,
7doc{
8/// Build without performing any validation.
9///
10/// This constructor constructs the `PriceBachelier` directly from
11/// the builder's fields and does **not** check for NaNs, infinities, or
12/// sign constraints. Use only when you are certain the inputs are valid
13/// or when you want to avoid the cost of runtime validation.
14})
15)]
16pub struct PriceBachelier {
17    forward: f64,
18    strike: f64,
19    volatility: f64,
20    expiry: f64,
21    is_call: bool,
22}
23
24impl<S: price_bachelier_builder::IsComplete> PriceBachelierBuilder<S> {
25    pub const fn build(self) -> Option<PriceBachelier> {
26        let price_bachelier = self.build_unchecked();
27        if !price_bachelier.forward.is_finite() {
28            return None;
29        }
30        if !price_bachelier.strike.is_finite() {
31            return None;
32        }
33        if !(price_bachelier.volatility >= 0.0) {
34            return None;
35        }
36        if !(price_bachelier.expiry >= 0.0) {
37            return None;
38        }
39        Some(price_bachelier)
40    }
41}
42
43impl PriceBachelier {
44    #[must_use]
45    #[inline(always)]
46    pub fn calculate<SpFn: SpecialFn>(&self) -> f64 {
47        if self.is_call {
48            lets_be_rational::bachelier_impl::bachelier_price::<true>(
49                self.forward,
50                self.strike,
51                self.volatility,
52                self.expiry,
53            )
54        } else {
55            lets_be_rational::bachelier_impl::bachelier_price::<false>(
56                self.forward,
57                self.strike,
58                self.volatility,
59                self.expiry,
60            )
61        }
62    }
63}