quant_opts/lets_be_rational/
mod.rs

1use statrs::consts::SQRT_2PI;
2
3use crate::{OptionType, lets_be_rational::black::normalised_black};
4
5// NOTE: black has to be private. If is public then `calc_rational_iv` is decreased benches dramatically.
6mod black;
7
8// For internal validation/benchmarks we sometimes need direct access to
9// specific helpers from `black.rs`. To avoid permanently degrading
10// optimisations on the critical IV path, these are only re-exported
11// under a dedicated feature.
12#[cfg(feature = "lets-be-rational-validation")]
13pub use black::{
14    asymptotic_expansion_of_normalised_black_call, small_t_expansion_of_normalised_black_call,
15};
16
17mod cody;
18mod intrinsic;
19pub(crate) mod normal_distribution;
20mod rational_cubic;
21mod so_rational;
22
23const IMPLIED_VOLATILITY_MAXIMUM_ITERATIONS: i32 = 2;
24pub(crate) const DENORMALISATION_CUTOFF: f64 = 0.0;
25pub(crate) const ONE_OVER_SQRT_TWO_PI: f64 = 1.0 / SQRT_2PI;
26
27/// Calculates the price of a European option using the Black model.
28///
29/// This function computes the theoretical price of a European call or put option based on the Black model, which is an extension of the Black-Scholes model for futures contracts. The model assumes that the price of the underlying asset follows a geometric Brownian motion and that markets are frictionless.
30///
31/// # Arguments
32/// * `forward_price` - The forward price of the underlying asset.
33/// * `strike_price` - The strike price of the option.
34/// * `sigma` - The volatility of the underlying asset's returns.
35/// * `time_to_maturity` - The time to maturity of the option, in years.
36/// * `option_type` - The type of the option (call or put), represented by `OptionType`.
37///
38/// # Returns
39/// The theoretical price of the option as a `f64`.
40///
41/// # Examples
42/// ```
43/// use blackscholes::{OptionType, lets_be_rational::black};
44///
45/// let forward_price = 100.0;
46/// let strike_price = 95.0;
47/// let sigma = 0.2;
48/// let time_to_maturity = 1.0;
49/// let option_type = OptionType::Call; // For a call option
50///
51/// let price = black(forward_price, strike_price, sigma, time_to_maturity, option_type);
52/// println!("The price of the option is: {}", price);
53/// ```
54///
55/// # Note
56/// The function uses the natural logarithm of the forward price over the strike price,
57/// multiplies it by the square root of time to maturity, and applies the option type
58/// to determine the final price. It's suitable for European options *only*.
59pub fn black(
60    forward_price: f64,
61    strike_price: f64,
62    sigma: f64,
63    time_to_maturity: f64,
64    option_type: OptionType,
65) -> f64 {
66    let signed_diff = option_type * (forward_price - strike_price);
67    let intrinsic = signed_diff.max(0.0);
68    // Map in-the-money to out-of-the-money
69    if signed_diff > 0.0 {
70        intrinsic
71            + black(
72                forward_price,
73                strike_price,
74                sigma,
75                time_to_maturity,
76                -option_type,
77            )
78    } else {
79        intrinsic.max(
80            (forward_price.sqrt() * strike_price.sqrt())
81                * normalised_black(
82                    (forward_price / strike_price).ln(),
83                    sigma * time_to_maturity.sqrt(),
84                    option_type,
85                ),
86        )
87    }
88}
89
90/// Calculates the implied volatility of an option using a rational guess.
91///
92/// This function estimates the implied volatility of a European call or put option based on the market price, forward price, strike price, time to maturity, and option type. It uses a rational guess approach with limited iterations (2) to find the implied volatility.
93///
94/// # Arguments
95/// * `market_price` - The market price of the option.
96/// * `forward_price` - The forward price of the underlying asset.
97/// * `strike_price` - The strike price of the option.
98/// * `time_to_maturity` - The time to maturity of the option, in years.
99/// * `option_type` - The type of the option (call or put), represented by `OptionType`.
100///
101/// # Returns
102/// The implied volatility of the option as a `f64`.
103///
104/// # Examples
105/// ```
106/// use blackscholes::{OptionType, lets_be_rational::implied_volatility_from_a_transformed_rational_guess};
107///
108/// let market_price = 10.0;
109/// let forward_price = 100.0;
110/// let strike_price = 95.0;
111/// let time_to_maturity = 1.0;
112/// let option_type = OptionType::Call;
113///
114/// let implied_volatility = implied_volatility_from_a_transformed_rational_guess(
115///     market_price,
116///     forward_price,
117///     strike_price,
118///     time_to_maturity,
119///     option_type,
120/// );
121/// println!("The implied volatility of the option is: {}", implied_volatility);
122/// ```
123///
124/// # Note
125/// This function is suitable for European options *only* and uses a rational guess approach with limited iterations (2) to estimate the implied volatility.
126pub fn implied_volatility_from_a_transformed_rational_guess(
127    market_price: f64,
128    forward_price: f64,
129    strike_price: f64,
130    time_to_maturity: f64,
131    option_type: OptionType,
132) -> f64 {
133    so_rational::implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(
134        market_price,
135        forward_price,
136        strike_price,
137        time_to_maturity,
138        option_type,
139        IMPLIED_VOLATILITY_MAXIMUM_ITERATIONS,
140    )
141}