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}