1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Safe, ergonomic builders for Black–Scholes / Bachelier pricing and implied volatility.
//!
//! This crate exposes builders for computing:
//! - the implied **Black** volatility,
//! - the implied **Normal** (Bachelier) volatility,
//! - undiscounted European option prices under the **Black–Scholes** model,
//! - undiscounted European option prices under the **Bachelier** (Normal) model.
//!
//! Additionally, the crate provides a `SpecialFn` trait for implementing special functions such as `erf`
//! and `normal_cdf` used by the underlying algorithms.
//!
//! # Getting started
//!
//! Add the crate to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! implied-vol = "2.0.0"
//! ```
//!
//! To enable aggressive fused-multiply-add optimizations (when available), enable the
//! `fma` feature in `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! implied-vol = { version = "2.0.0", features = ["fma"] }
//! ```
//!
//! # Models and notation
//!
//! For clarity, we restrict the documentation examples to *undiscounted* European call options.
//! The same APIs apply to put options and to the Bachelier model (normal model).
//!
//! Let `BS(F, K, T, σ)` denote the undiscounted European call price under the Black–Scholes model,
//! where:
//! - `F`: forward price (underlying), `0 < F < ∞`,
//! - `K`: strike price, `0 < K < ∞`,
//! - `T`: time to expiry, `0 ≤ T < ∞`,
//! - `σ`: volatility, `0 ≤ σ < ∞`.
//!
//! Given `F`, `K`, `T` and an observed option price `P`, the **implied Black volatility**
//! is the value `σ` such that `BS(F, K, T, σ) = P`. In other words, it is the inverse
//! of `BS` with respect to `σ`.
//!
//! # Usage and error handling
//!
//! The crate provides builder types that validate inputs at construction time and then
//! exposes a `calculate()` method for performing the computation. Validation behavior is:
//!
//! - `build()` performs input validation and returns `Option<...>`; it yields `None` when
//! the provided parameters are outside the mathematical domain of the target function.
//! - `build_unchecked()` constructs the object without validation (for callers who prefer
//! to do their own checks or avoid the runtime cost).
//!
//! All heavy numerical work is done in `calculate()`; for implied-volatility builders
//! `calculate()` returns `Option<f64>` (or `None` when the given option price is not in the
//! function's image), and for price builders `calculate()` returns `f64`.
//!
//! ## Special functions
//!
//! Some algorithms require special mathematical functions.
//! Those are abstracted behind the `SpecialFn` trait. The crate provides
//! a default implementation named `DefaultSpecialFn` (based on the original author's code).
//! If you need to swap in a different implementation (for testing or higher-precision math),
//! implement the `SpecialFn` trait and call `calculate::<YourSpecialFn>()`.
//!
//! ## `PriceBlackScholes` (example)
//!
//! `PriceBlackScholes::builder()` validates inputs (via `build()`), returning `None` when
//! the inputs are not in the domain of `BS`. Use `build_unchecked()` to skip validation.
//!
//! ```rust
//! use implied_vol::{DefaultSpecialFn, PriceBlackScholes};
//!
//! // Valid inputs -> builder.build() returns Some(...)
//! let builder = PriceBlackScholes::builder()
//! .forward(100.0)
//! .strike(100.0)
//! .volatility(0.2)
//! .expiry(1.0)
//! .is_call(true);
//!
//! let price_builder = builder.build();
//! assert!(price_builder.is_some());
//! let price = price_builder.unwrap().calculate::<DefaultSpecialFn>();
//! assert!(price.is_finite());
//!
//! // Skip validation:
//! let price_builder = PriceBlackScholes::builder()
//! .forward(100.0)
//! .strike(100.0)
//! .volatility(0.2)
//! .expiry(1.0)
//! .is_call(true)
//! .build_unchecked();
//! let price = price_builder.calculate::<DefaultSpecialFn>();
//! assert!(price.is_finite());
//!
//! // Invalid inputs -> build() returns None
//! let invalid = PriceBlackScholes::builder()
//! .forward(f64::INFINITY) // invalid forward
//! .strike(100.0)
//! .volatility(0.2)
//! .expiry(1.0)
//! .is_call(true)
//! .build();
//! assert!(invalid.is_none());
//! ```
//!
//! ## `ImpliedBlackVolatility` (example)
//!
//! `ImpliedBlackVolatility::builder()` validates that `BS(F, K, T, ·)` is well-defined for
//! the supplied `F`, `K`, `T` and that the provided `option_price` is a finite, non-negative number.
//! - `build()` returns `None` if the inputs fall outside the model domain.
//! - After a successful `build()`, calling `calculate()` returns `Some(σ)` when the given
//! `option_price` lies in the image of `BS(F, K, T, ·)`. If the price is outside that image,
//! `calculate()` returns `None`.
//!
//! ```rust
//! use implied_vol::{DefaultSpecialFn, ImpliedBlackVolatility};
//!
//! // Valid inputs -> build() returns Some(...), calculate() may return Some(σ).
//! let builder = ImpliedBlackVolatility::builder()
//! .option_price(10.0)
//! .forward(100.0)
//! .strike(100.0)
//! .expiry(1.0)
//! .is_call(true);
//!
//! let iv_builder = builder.build();
//! assert!(iv_builder.is_some());
//! let sigma_opt = iv_builder.unwrap().calculate::<DefaultSpecialFn>();
//! assert!(sigma_opt.is_some()); // implied vol found
//!
//! // Skip validation:
//! let sigma = ImpliedBlackVolatility::builder()
//! .option_price(10.0)
//! .forward(100.0)
//! .strike(100.0)
//! .expiry(1.0)
//! .is_call(true)
//! .build_unchecked()
//! .calculate::<DefaultSpecialFn>();
//! assert!(sigma.is_some());
//!
//! // If model parameters are invalid -> build() returns None
//! let invalid_builder = ImpliedBlackVolatility::builder()
//! .option_price(10.0)
//! .forward(f64::INFINITY) // invalid forward
//! .strike(100.0)
//! .expiry(1.0)
//! .is_call(true)
//! .build();
//! assert!(invalid_builder.is_none());
//!
//! // If the option price is outside the attainable range, calculate() returns None.
//! let out_of_range = ImpliedBlackVolatility::builder()
//! .option_price(110.0) // too large for F=100,K=100
//! .forward(100.0)
//! .strike(100.0)
//! .expiry(1.0)
//! .is_call(true)
//! .build()
//! .unwrap()
//! .calculate::<DefaultSpecialFn>();
//! assert!(out_of_range.is_none());
//! ```
pub use crateDefaultSpecialFn;
pub use crateSpecialFn;
pub use *;