Skip to main content

optionstratlib_core/
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-core
34//!
35//! The core domain model of OptionStratLib: the types every other
36//! OptionStratLib crate builds on. It holds no pricing model, market data,
37//! strategy, backtesting or presentation code, and depends on no other
38//! OptionStratLib crate.
39//!
40//! - [`model`]: [`model::Options`], [`model::Position`], [`model::leg::Leg`]
41//!   and its spot, future and perpetual legs, [`model::Trade`], payoff
42//!   contracts at expiry and checked `Decimal` helpers.
43//! - [`error`]: the errors those types raise.
44//! - [`utils`]: time frames and date helpers, the deterministic RNG,
45//!   numeric helpers and [`utils::Len`].
46//! - [`constants`]: library-wide numeric constants, market conventions and time units.
47//!
48//! Monetary values are [`rust_decimal::Decimal`] or [`positive::Positive`],
49//! and arithmetic on them is checked.
50//!
51//! ## Foundational types
52//!
53//! Four standalone crates define the newtypes and enums every OptionStratLib
54//! crate shares. Core depends on them and re-exports their types unchanged,
55//! never wrapping or copying one, so a value reached through any path below
56//! is the same type as the original and needs no conversion. Each crate
57//! resolves to a single version across the workspace (`make check-graph`).
58//!
59//! | Type | Defining crate | Re-exported at |
60//! | --- | --- | --- |
61//! | `Positive`, `PositiveError`, `is_positive` | `positive` | `optionstratlib_core::model`, `optionstratlib::model`; `Positive` also in `optionstratlib::prelude` |
62//! | `pos_or_panic!`, `spos!`, `assert_pos_relative_eq!` | `positive` | `optionstratlib_core`, `optionstratlib::prelude` |
63//! | `ExpirationDate`, `ExpirationDateError` | `expiration_date` | `optionstratlib_core::model`, `optionstratlib::model`; `ExpirationDate` also at the `optionstratlib` root and in its prelude |
64//! | `Side`, `OptionStyle`, `Action`, `UnderlyingAssetType` | `financial_types` | `optionstratlib_core::model::types`; `Side` and `OptionStyle` also in `optionstratlib_core::model` and at the `optionstratlib` root; `Side`, `OptionStyle` and `Action` in its prelude |
65//! | `OptionType`, `OptionBasicType`, `AsianAveragingType`, `BarrierType`, `BinaryType`, `LookbackType`, `RainbowType` | `option_type` | `optionstratlib_core::model::types`; `OptionType` and `RainbowType` also in `optionstratlib_core::model` and at the `optionstratlib` root; `OptionType` in its prelude |
66//! | `DAYS_IN_A_YEAR` | `positive` | `optionstratlib_core::constants`, `optionstratlib::constants` |
67//! | `StrictlyPositive`, `PositiveResult`, the other `positive::constants`; `ParseEnumError`; the day-count conventions (`DayCount`, `Actual360`, `Actual365Fixed`, `Thirty360US`) | `positive`; `financial_types`; `expiration_date` | not re-exported: name them through the defining crate |
68//!
69//! The facade's `optionstratlib::model` *is* `optionstratlib_core::model`, so
70//! every core path above is also reachable with the `optionstratlib::model`
71//! prefix. Enabling `schema` adds `ToSchema` impls to these types; it does not
72//! change which type a path names.
73//!
74//! ## Imports: no prelude
75//!
76//! This crate has no `prelude` module, by decision (#518). The module roots
77//! already are the curated entry points: `model` re-exports the domain types
78//! and the foundational ones (`Options`, `Position`, `Trade`, `TradeStatus`,
79//! `ExpirationDate`, `Side`, `OptionStyle`, `OptionType`, `Positive`),
80//! `model::leg` the leg types, and `utils` `TimeFrame` and `Len`.
81//!
82//! Measured when the crate was extracted, counting explicit
83//! `optionstratlib::{model,utils,constants}::…` imports: 168 of the 178
84//! example files import through the facade's `prelude::*`, and the examples
85//! name 8 distinct core items explicitly (in 25 files). Tests and benches (139
86//! files) name 31 distinct core items in 40 files, 18 of them through deep
87//! paths such as `model::decimal::DecimalStats` or `utils::time::…`. Nothing
88//! outside the workspace imports `optionstratlib_core` directly. The use is
89//! spread thinly across many items, so no small glob would cover it, and the
90//! few types every consumer needs are already one path away at the module
91//! roots. A crate glob would only duplicate those roots and make every future
92//! addition a prelude commitment. The facade prelude is M7-03's to redesign.
93//!
94//! ```rust
95//! use optionstratlib_core::model::leg::{Leg, LegAble, SpotPosition};
96//! use optionstratlib_core::model::{
97//!     ExpirationDate, OptionStyle, OptionType, Options, Position, Positive, Side, Trade,
98//! };
99//! use optionstratlib_core::utils::{Len, TimeFrame};
100//! use optionstratlib_core::pos_or_panic;
101//! ```
102//!
103//! ## Place in the workspace
104//!
105//! - **Depends on** no other OptionStratLib crate; the foundational `positive`,
106//!   `expiration_date`, `financial_types` and `option_type`, which it re-exports.
107//! - **Must not depend on** every other OptionStratLib crate (it is the bottom
108//!   layer); `make check-graph` enforces the layering (ADR-0001 D9).
109//! - **In the facade:** always present: `optionstratlib::{model, utils,
110//!   constants}`, the core errors in `optionstratlib::error`, the root types
111//!   `Options`, `ExpirationDate`, `OptionStyle`, `OptionType`, `RainbowType`,
112//!   `Side` and the root macros. The facade paths are the same types as the paths
113//!   here; the [ownership
114//!   map](https://github.com/joaquinbejar/OptionStratLib/blob/main/docs/ownership.md)
115//!   lists every one with its feature.
116//!
117//! Moving from 0.21? The [0.22 architecture and adoption
118//! guide](https://github.com/joaquinbejar/OptionStratLib/blob/main/docs/migration-0.22.md)
119//! maps each redesign to its replacement workflow; 0.22 keeps no
120//! compatibility with 0.21.
121//!
122//! ## Minimal example
123//!
124//! ```rust
125//! use optionstratlib_core::model::{ExpirationDate, OptionStyle, OptionType, Options, Positive, Side};
126//! use optionstratlib_core::pos_or_panic;
127//! use rust_decimal_macros::dec;
128//!
129//! // A call struck at 100 with the underlying at 105; the payoff at
130//! // expiry needs no pricing model.
131//! let option = Options::new(
132//!     OptionType::European,
133//!     Side::Long,
134//!     "XYZ".to_string(),
135//!     pos_or_panic!(100.0),
136//!     ExpirationDate::Days(pos_or_panic!(30.0)),
137//!     pos_or_panic!(0.2),
138//!     Positive::ONE,
139//!     pos_or_panic!(105.0),
140//!     dec!(0.05),
141//!     OptionStyle::Call,
142//!     Positive::ZERO,
143//!     None,
144//! );
145//! assert_eq!(option.payoff()?, dec!(5));
146//! # Ok::<(), optionstratlib_core::error::OptionsError>(())
147//! ```
148//!
149//! ## Runnable example
150//!
151//! A runnable program that depends on this crate directly, with the smallest
152//! 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`
153//! asserts its resolved graph. Core is in every example's graph; this is the smallest program that uses it.
154//!
155//! ## Features
156//!
157//! - `schema` (off by default): derives `utoipa::ToSchema` on the core types
158//!   and enables the same derive on `positive`, `expiration_date`,
159//!   `financial_types` and `option_type`.
160
161/// Domain types: options, positions, legs, trades, payoffs and checked
162/// `Decimal` helpers.
163pub mod model;
164
165/// Errors raised by the core domain types.
166pub mod error;
167
168/// Time frames, date helpers, the deterministic RNG and numeric helpers.
169pub mod utils;
170
171/// Library-wide numeric constants, market conventions and time units.
172pub mod constants;
173
174pub use positive::{assert_pos_relative_eq, pos_or_panic, spos};
175
176/// Version of the `optionstratlib-core` crate.
177pub const VERSION: &str = env!("CARGO_PKG_VERSION");
178
179/// Compiles the Rust examples of this crate's `README.md` as doctests, so the
180/// README cannot drift from the API (#554). Exists only under `cfg(doctest)`.
181#[cfg(doctest)]
182#[doc = include_str!("../README.md")]
183pub struct ReadmeDoctests;