optionstratlib_pricing/lib.rs
1#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
2// Per rules/global_rules.md §Error Handling, unchecked `[]` / slicing is
3// banned in production code.
4#![deny(clippy::indexing_slicing)]
5// Unit tests routinely index into `Vec`s they just pushed into, so the lint
6// is silenced in `#[cfg(test)]` only.
7#![cfg_attr(test, allow(clippy::indexing_slicing))]
8// Per rules/global_rules.md §Arithmetic, an operator that overflows, divides
9// by zero or breaks the `Positive` invariant aborts the caller, so every
10// operation on `Decimal`, `Positive` and the integers goes through its
11// checked form (#788). `clippy.toml` exempts the unary minus on `Decimal`,
12// which cannot overflow. Casts that truncate, wrap or drop the sign are
13// denied for the same reason. Unit tests are exempt, as above.
14#![deny(
15 clippy::arithmetic_side_effects,
16 clippy::cast_possible_truncation,
17 clippy::cast_possible_wrap,
18 clippy::cast_sign_loss
19)]
20#![cfg_attr(
21 test,
22 allow(
23 clippy::arithmetic_side_effects,
24 clippy::cast_possible_truncation,
25 clippy::cast_possible_wrap,
26 clippy::cast_sign_loss
27 )
28)]
29// An error or a default built eagerly in `ok_or` / `unwrap_or` is paid on
30// the success path too (#857), so it is built in a closure.
31#![deny(clippy::or_fun_call)]
32
33//! # optionstratlib-pricing
34//!
35//! Option pricing models, Greeks and volatility of OptionStratLib, on the
36//! core domain types. It depends only on `optionstratlib-core`,
37//! `optionstratlib-math` and general numeric crates: no option chain,
38//! simulation engine, strategy, plotting, I/O or async runtime.
39//!
40//! - [`pricing`]: Black-Scholes, Black-76, Garman-Kohlhagen, binomial,
41//! Monte Carlo over supplied paths, telegraph, American and exotic
42//! kernels, the [`pricing::OptionPricing`] extension trait for
43//! `Options`, the unified [`pricing::price_option_with`] entry point, profit
44//! contracts and solver defaults.
45//! - [`greeks`]: first and higher-order Greeks, the [`greeks::Greeks`]
46//! trait, model-specific Greeks and the Greeks of the leg types.
47//! - [`volatility`]: implied-volatility solvers and volatility models and
48//! traits.
49//! - [`error`]: [`error::PricingError`], [`error::GreeksError`] and
50//! [`error::VolatilityError`].
51//!
52//! ## Numerical boundary
53//!
54//! Prices, premia, strikes, rates and Greeks cross the public boundary as
55//! `rust_decimal::Decimal` or `Positive`. `f64` stays inside the numerical
56//! kernels (the normal distribution, lattice and finite-difference steps).
57//! The only public `f64`s are error diagnostics, which `make
58//! check-float-boundary` enforces (#522). Most kernels bring floats back
59//! through `optionstratlib_core::model::decimal::finite_decimal`, so a
60//! non-finite intermediate becomes a typed `NonFinite` error; several exotic
61//! pricers still substitute zero for a failed step instead, which #639
62//! tracks. The kernels perform no filesystem, network or stdout I/O and
63//! install no logging subscriber.
64//!
65//! For fixed inputs, closed-form and lattice pricers are deterministic; an
66//! `ExpirationDate::DateTime` expiry is measured from the current clock, so
67//! use `ExpirationDate::Days` for reproducible results. No public function
68//! draws from the thread-local RNG implicitly (#638): Monte Carlo pricing
69//! (`pricing::monte_carlo_option_pricing`), the telegraph pricer
70//! (`pricing::telegraph`, `OptionPricing::calculate_price_telegraph`),
71//! `pricing::TelegraphProcess`, `pricing::simulate_returns` and
72//! `volatility::simulate_heston_volatility` take the generator from the
73//! caller. Pass a seeded one such as
74//! `optionstratlib_core::utils::deterministic_rng(seed)` for reproducible
75//! results, or `&mut rand::rng()` for fresh draws on every call.
76//!
77//! ## Imports
78//!
79//! There is no prelude, for the same reason as in core and math (#518): the
80//! module roots re-export each capability, and the `optionstratlib` facade
81//! prelude serves broad imports.
82//!
83//! ## Place in the workspace
84//!
85//! - **Depends on** `optionstratlib-core` and `optionstratlib-math`.
86//! - **Must not depend on** `optionstratlib-simulation`, `-market`, `-analytics`,
87//! `-strategies`, `-backtest` and `-visualization`; `make check-graph` enforces
88//! the layering (ADR-0001 D9).
89//! - **In the facade:** `optionstratlib::{pricing, greeks, volatility}` and the
90//! pricing errors in `optionstratlib::error`, under the facade feature
91//! `pricing`. The facade paths are the same types as the paths here; the
92//! [ownership
93//! map](https://github.com/joaquinbejar/OptionStratLib/blob/main/docs/ownership.md)
94//! lists every one with its feature.
95//!
96//! Moving from 0.21? The [0.22 architecture and adoption
97//! guide](https://github.com/joaquinbejar/OptionStratLib/blob/main/docs/migration-0.22.md)
98//! maps each redesign to its replacement workflow; 0.22 keeps no
99//! compatibility with 0.21.
100//!
101//! ## Minimal example
102//!
103//! ```rust
104//! use optionstratlib_core::model::{ExpirationDate, OptionStyle, OptionType, Options, Positive, Side};
105//! use optionstratlib_core::pos_or_panic;
106//! use optionstratlib_pricing::pricing::black_scholes;
107//! use rust_decimal_macros::dec;
108//!
109//! // Hull's worked example: S = 42, K = 40, r = 10%, sigma = 20%, T = 0.5.
110//! let call = Options::new(
111//! OptionType::European,
112//! Side::Long,
113//! "XYZ".to_string(),
114//! pos_or_panic!(40.0),
115//! ExpirationDate::Days(pos_or_panic!(182.5)),
116//! pos_or_panic!(0.2),
117//! Positive::ONE,
118//! pos_or_panic!(42.0),
119//! dec!(0.10),
120//! OptionStyle::Call,
121//! Positive::ZERO,
122//! None,
123//! );
124//! let price = black_scholes(&call)?;
125//! assert!((price - dec!(4.76)).abs() < dec!(0.01));
126//! # Ok::<(), optionstratlib_pricing::error::PricingError>(())
127//! ```
128//!
129//! ## Runnable example
130//!
131//! A runnable program that depends on this crate directly, with the smallest
132//! dependency set and no facade, is [`osl-example-direct-pricing`](https://github.com/joaquinbejar/OptionStratLib/tree/main/examples/direct/pricing); `make tree-example-direct-pricing`
133//! asserts its resolved graph.
134//!
135//! ## Features
136//!
137//! - `schema` (off by default): derives `utoipa::ToSchema` on the pricing
138//! types and enables `optionstratlib-core/schema` and
139//! `optionstratlib-math/schema`.
140
141/// Pricing models, the `OptionPricing` trait, profit contracts and solver
142/// defaults.
143pub mod pricing;
144
145/// Greeks: equations, the `Greeks` trait, model-specific and numerical
146/// Greeks, and the Greeks of the leg types.
147pub mod greeks;
148
149/// Implied-volatility solvers, volatility models and volatility traits.
150pub mod volatility;
151
152/// Errors raised by the pricing layer.
153pub mod error;
154
155// Formulas shared by `pricing` and `greeks` (normal distribution, d1/d2,
156// discounting). Private: it is the neutral bottom of the crate's internal
157// graph and depends on neither module (#523).
158mod kernels;
159
160/// Version of the `optionstratlib-pricing` crate.
161pub const VERSION: &str = env!("CARGO_PKG_VERSION");
162
163/// Compiles the Rust examples of this crate's `README.md` as doctests, so the
164/// README cannot drift from the API (#554). Exists only under `cfg(doctest)`.
165#[cfg(doctest)]
166#[doc = include_str!("../README.md")]
167pub struct ReadmeDoctests;