implied_vol/
lib.rs

1//! This module provides a Rust implementation of Peter Jäckel's original C++ code for calculating
2//! implied volatilities in financial derivatives.
3//! To learn more about the algorithms, please refer to Peter Jäckel's papers [Let's Be Rational](http://www.jaeckel.org/LetsBeRational.pdf) and [Implied Normal Volatility](http://www.jaeckel.org/ImpliedNormalVolatility.pdf).
4//!
5//! # Features
6//!
7//! - Calculation of implied Black volatility
8//! - Calculation of the price of a European option using the Black-Scholes model
9//! - Calculation of implied normal volatility
10//! - Calculation of the price of an option using Bachelier's model
11//!
12//! All models support both call and put options.
13//!
14//! # Examples
15//!
16//! Check out the documentation for each function for practical examples.
17//!
18//! # Usage
19//!
20//! Import the crate in your Rust project by adding the following to your `Cargo.toml`
21//!
22//! ```toml
23//! [dependencies]
24//! implied-vol = "1.2.3"
25//! ```
26//!
27//! Then, in your code, bring the functions you need into scope with:
28//!
29//! ```rust
30//! let black_vol = implied_vol::implied_black_volatility(20.0, 100.0, 90.0, 30.0, true);
31//! assert_eq!(black_vol, 0.07011701801482094);
32//!
33//! let price = implied_vol::calculate_european_option_price_by_black_scholes(100.0, 90.0, 0.07011701801482094, 30.0, true);
34//! assert!(((price - 20.0) / price).abs() <= 2.0 * f64::EPSILON);
35//!
36//! let normal_vol = implied_vol::implied_normal_volatility(20.0, 100.0, 90.0, 30.0, true);
37//! assert_eq!(normal_vol, 6.614292466299764);
38//!
39//! let price = implied_vol::calculate_european_option_price_by_bachelier(100.0, 90.0, 6.614292466299764, 30.0, true);
40//! assert!(((price - 20.0) / price).abs()<= 2.0 * f64::EPSILON);
41//! ```
42//!
43//! Moreover, you can use some internal functions if you specify feature flags:
44//!
45//! ```toml
46//! [dependencies]
47//! implied-vol = { versions = "1.0.0", features = ["normal-distribution", "error-function"] }
48//! ```
49//!
50//! For detailed explanations of each feature, please refer to the README.md file.
51pub use crate::special_function::{DefaultSpecialFn, SpecialFn};
52
53mod bachelier;
54mod constants;
55mod fused_multiply_add;
56mod lets_be_rational;
57mod rational_cubic;
58pub mod special_function;
59
60/// Calculates the implied black volatility using a transformed rational guess with limited iterations.
61///
62/// # Arguments
63///
64/// * `option_price` - The current price of the option.
65/// * `forward` - The current forward price of the underlying asset.
66/// * `strike` - The strike price of the option.
67/// * `expiry` - The time to expiration in years.
68/// * `is_call` - A boolean flag indicating whether the option is a call (true) or put (false).
69///
70/// # Returns
71///
72/// The implied Black volatility.
73///
74/// # Examples
75///
76/// ```
77/// let black_vol = implied_vol::implied_black_volatility(20.0, 100.0, 90.0, 30.0, true);
78/// assert_eq!(black_vol, 0.07011701801482094);
79/// ```
80#[inline]
81pub fn implied_black_volatility(
82    option_price: f64,
83    forward: f64,
84    strike: f64,
85    expiry: f64,
86    is_call: bool,
87) -> f64 {
88    lets_be_rational::implied_black_volatility::<DefaultSpecialFn>(
89        option_price,
90        forward,
91        strike,
92        expiry,
93        is_call,
94    )
95}
96
97/// Computes the implied Black-Scholes volatility of an option given its price.
98///
99/// # Type Parameters
100/// - `SpFn`: A type implementing the `SpecialFn` trait. This type is used internally
101///   by the `lets_be_rational` library for specialized mathematical computations.
102///
103/// # Arguments
104/// - `option_price`: The observed market price of the option.
105/// - `forward`: The forward price of the underlying asset.
106/// - `strike`: The strike price of the option.
107/// - `expiry`: The time to expiry (in years) of the option.
108/// - `is_call`: A boolean flag indicating the type of the option:
109///   - `true` if the option is a call.
110///   - `false` if the option is a put.
111///
112/// # Returns
113/// - The implied Black volatility of the option.
114///
115/// # Examples
116/// ```
117/// use implied_vol::implied_black_volatility_custom;
118/// use implied_vol::special_function::DefaultSpecialFn;
119///
120/// let option_price = 10.0;
121/// let forward = 100.0;
122/// let strike = 105.0;
123/// let expiry = 1.0; // 1 year
124/// let is_call = true;
125///
126/// let implied_vol = implied_black_volatility_custom::<DefaultSpecialFn>(
127///     option_price,
128///     forward,
129///     strike,
130///     expiry,
131///     is_call,
132/// );
133///
134/// println!("Implied Volatility: {}", implied_vol);
135/// ```
136#[inline]
137pub fn implied_black_volatility_custom<SpFn: SpecialFn>(
138    option_price: f64,
139    forward: f64,
140    strike: f64,
141    expiry: f64,
142    is_call: bool,
143) -> f64 {
144    lets_be_rational::implied_black_volatility::<SpFn>(
145        option_price,
146        forward,
147        strike,
148        expiry,
149        is_call,
150    )
151}
152
153/// Calculates the price of a European option using the Black-Scholes formula.
154///
155/// # Arguments
156///
157/// * `forward` - The current value of the underlying asset.
158/// * `strike` - The strike price of the option.
159/// * `volatility` - The volatility of the underlying asset.
160/// * `expiry` - The time to expiration of the option.
161/// * `is_call` - A boolean flag indicating whether the option is a call (true) or put (false).
162///
163/// # Returns
164///
165/// The price of the European option based on the Black-Scholes model.
166///
167/// # Examples
168///
169/// ```
170/// let price = implied_vol::calculate_european_option_price_by_black_scholes(100.0, 90.0, 0.07011701801482094, 30.0, true);
171/// assert!((price - 20.0).abs()<= 2.0 * f64::EPSILON * 20.0);
172/// ```
173#[inline]
174pub fn calculate_european_option_price_by_black_scholes(
175    forward: f64,
176    strike: f64,
177    volatility: f64,
178    expiry: f64,
179    is_call: bool,
180) -> f64 {
181    lets_be_rational::black::<DefaultSpecialFn>(forward, strike, volatility, expiry, is_call)
182}
183
184/// Calculates the price of a European option using the Black-Scholes model with a custom special function.
185///
186/// # Type Parameters
187/// - `SpFn`: A custom type that implements the `SpecialFn` trait to handle special mathematical functions used in the computation.
188///
189/// # Parameters
190/// - `forward` (`f64`): The forward price of the underlying asset.
191/// - `strike` (`f64`): The strike price of the option.
192/// - `volatility` (`f64`): The implied volatility of the underlying asset, expressed as a decimal.
193/// - `expiry` (`f64`): The time to expiry of the option, measured in years.
194/// - `is_call` (`bool`): A flag to specify the option type:
195///   - `true` for a call option.
196///   - `false` for a put option.
197///
198/// # Returns
199/// - `f64`: The Black-Scholes price of the European option, calculated using the custom special function.
200///
201/// # Example
202/// ```
203/// use implied_vol::calculate_european_option_price_by_black_scholes_custom;
204/// use implied_vol::special_function::DefaultSpecialFn;
205/// let forward_price = 100.0; // Forward price of the underlying asset
206/// let strike_price = 105.0; // Strike price of the option
207/// let volatility = 0.2; // Implied volatility (20%)
208/// let time_to_expiry = 1.0; // Time to expiry in years
209/// let is_call = true; // Call option
210///
211/// let price = calculate_european_option_price_by_black_scholes_custom::<DefaultSpecialFn>(
212///     forward_price,
213///     strike_price,
214///     volatility,
215///     time_to_expiry,
216///     is_call,
217/// );
218///
219/// println!("European option price: {}", price);
220/// ```
221#[inline]
222pub fn calculate_european_option_price_by_black_scholes_custom<SpFn: SpecialFn>(
223    forward: f64,
224    strike: f64,
225    volatility: f64,
226    expiry: f64,
227    is_call: bool,
228) -> f64 {
229    lets_be_rational::black::<SpFn>(forward, strike, volatility, expiry, is_call)
230}
231
232/// Calculates the implied normal volatility.
233///
234/// # Arguments
235///
236/// * `price` - The market price of the option.
237/// * `forward` - The forward price of the underlying asset.
238/// * `strike` - The strike price of the option.
239/// * `expiry` - The time to expiration in years.
240/// * `is_call` - A boolean flag indicating whether the option is a call (true) or put (false).
241///
242/// # Returns
243///
244/// The implied normal volatility as a `f64` value.
245///
246/// # Examples
247///
248/// ```
249/// let normal_vol = implied_vol::implied_normal_volatility(20.0, 100.0, 90.0, 30.0, true);
250/// assert_eq!(normal_vol, 6.614292466299764);
251/// ```
252pub fn implied_normal_volatility(
253    option_price: f64,
254    forward: f64,
255    strike: f64,
256    expiry: f64,
257    is_call: bool,
258) -> f64 {
259    bachelier::implied_normal_volatility::<DefaultSpecialFn>(
260        option_price,
261        forward,
262        strike,
263        expiry,
264        is_call,
265    )
266}
267
268/// Calculates the price of an option using Bachelier's model.
269///
270/// # Arguments
271///
272/// * `forward` - The forward price of the underlying asset.
273/// * `strike` - The strike price of the option.
274/// * `volatility` - The volatility of the underlying asset.
275/// * `expiry` - The time to expiration in years.
276/// * `is_call` - A boolean flag indicating whether the option is a call (true) or a put (false).
277///
278/// # Returns
279///
280/// The price of the European option.
281///
282/// # Examples
283///
284/// ```
285/// let price = implied_vol::calculate_european_option_price_by_bachelier(100.0, 90.0, 6.614292466299764, 30.0, true);
286/// assert!((price - 20.0).abs()<= 2.0 * f64::EPSILON * 20.0);
287/// ```
288#[inline]
289pub fn calculate_european_option_price_by_bachelier(
290    forward: f64,
291    strike: f64,
292    volatility: f64,
293    expiry: f64,
294    is_call: bool,
295) -> f64 {
296    bachelier::bachelier(forward, strike, volatility, expiry, is_call)
297}
298
299#[cfg(feature = "error-function")]
300/// Calculates the scaled complementary error function of `x`.
301///
302/// The scaled complementary error function is defined as: `erfcx(x) = exp(x^2) * erfc(x)`,
303/// where `erfc(x)` is the complementary error function.
304///
305/// # Arguments
306///
307/// * `x` - The input value to calculate the scaled complementary error function for.
308///
309/// # Returns
310///
311/// The result of calculating the scaled complementary error function of `x`.
312///
313/// # Example
314///
315/// ```
316/// let result = implied_vol::erfcx(0.5);
317/// assert!((result - 0.6156903441929259) / result <= f64::EPSILON);
318/// ```
319#[inline]
320#[deprecated(since = "1.3.0", note = "Use `DefaultSpecialFn::erfcx` instead")]
321pub fn erfcx(x: f64) -> f64 {
322    DefaultSpecialFn::erfcx(x)
323}
324
325#[cfg(feature = "error-function")]
326/// Calculates the complementary error function.
327///
328/// # Arguments
329///
330/// * `x` - The input number for which the complementary error function needs to be calculated.
331///
332/// # Returns
333///
334/// The result of the complementary error function calculation.
335///
336/// # Example
337///
338/// ```
339/// let result = implied_vol::erfc(0.5);
340/// assert!((result - 0.4795001221869535) / result <= f64::EPSILON);
341/// ```
342#[inline]
343#[deprecated(since = "1.3.0", note = "Use `DefaultSpecialFn::erfc` instead")]
344pub fn erfc(x: f64) -> f64 {
345    DefaultSpecialFn::erfc(x)
346}
347
348/// Calculates the probability density function of a standard normal distribution.
349///
350/// # Arguments
351///
352/// * `x` - The value at which to calculate the probability density function.
353///
354/// # Returns
355///
356/// The probability density function value at the given `x` value.
357///
358/// # Examples
359///
360/// ```
361/// let pdf = implied_vol::norm_pdf(0.0);
362/// assert!((pdf - 0.3989422804014327) / pdf <= f64::EPSILON);
363/// ```
364#[cfg(feature = "normal-distribution")]
365#[deprecated(since = "1.3.0", note = "Use `DefaultSpecialFn::norm_pdf` instead")]
366#[inline]
367pub fn norm_pdf(x: f64) -> f64 {
368    DefaultSpecialFn::norm_pdf(x)
369}
370/// Calculates the cumulative distribution function (CDF) of the standard normal distribution.
371///
372/// # Arguments
373///
374/// * `x` - The value at which to calculate the CDF.
375///
376/// # Returns
377///
378/// The CDF value for `x` in the standard normal distribution, ranging from 0 to 1.
379///
380/// # Examples
381///
382/// ```
383/// let cdf = implied_vol::norm_cdf(1.5);
384/// assert!((cdf - 0.9331927987311419) / cdf <= f64::EPSILON);
385/// ```
386#[cfg(feature = "normal-distribution")]
387#[deprecated(since = "1.3.0", note = "Use `DefaultSpecialFn::norm_cdf` instead")]
388#[inline]
389pub fn norm_cdf(x: f64) -> f64 {
390    DefaultSpecialFn::norm_cdf(x)
391}
392
393#[cfg(feature = "normal-distribution")]
394/// Calculates the inverse cumulative distribution function (CDF).
395///
396/// The inverse CDF is also known as the quantile function or percent-point function.
397/// It returns the value x such that P(X < x) = probability, where X follows a standard normal distribution.
398///
399/// # Arguments
400///
401/// * `x` - The probability value between 0 and 1.
402///
403/// # Examples
404///
405/// ```
406/// let probability = 0.8;
407/// let inverse_cdf = implied_vol::inverse_norm_cdf(probability);
408/// assert!((inverse_cdf - 0.8416212335729144) / inverse_cdf <= f64::EPSILON);
409/// ```
410///
411/// # Panics
412///
413/// This function will panic if the given probability value is outside the range [0, 1].
414#[inline]
415#[deprecated(
416    since = "1.3.0",
417    note = "Use `DefaultSpecialFn::inverse_norm_cdf` instead"
418)]
419pub fn inverse_norm_cdf(x: f64) -> f64 {
420    DefaultSpecialFn::inverse_norm_cdf(x)
421}