optionstratlib-core 0.22.1

Core domain model of OptionStratLib: options, positions, legs, trades, checked Decimal helpers and their errors. Re-exported by the optionstratlib facade on every feature set.
Documentation
#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
// Per rules/global_rules.md §Error Handling, unchecked `[]` / slicing is
// banned in production code.
#![deny(clippy::indexing_slicing)]
// Unit tests routinely index into `Vec`s they just pushed into, so the lint
// is silenced in `#[cfg(test)]` only.
#![cfg_attr(test, allow(clippy::indexing_slicing))]
// Per rules/global_rules.md §Arithmetic, an operator that overflows, divides
// by zero or breaks the `Positive` invariant aborts the caller, so every
// operation on `Decimal`, `Positive` and the integers goes through its
// checked form (#788). `clippy.toml` exempts the unary minus on `Decimal`,
// which cannot overflow. Casts that truncate, wrap or drop the sign are
// denied for the same reason. Unit tests are exempt, as above.
#![deny(
    clippy::arithmetic_side_effects,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_sign_loss
)]
#![cfg_attr(
    test,
    allow(
        clippy::arithmetic_side_effects,
        clippy::cast_possible_truncation,
        clippy::cast_possible_wrap,
        clippy::cast_sign_loss
    )
)]
// An error or a default built eagerly in `ok_or` / `unwrap_or` is paid on
// the success path too (#857), so it is built in a closure.
#![deny(clippy::or_fun_call)]

//! # optionstratlib-core
//!
//! The core domain model of OptionStratLib: the types every other
//! OptionStratLib crate builds on. It holds no pricing model, market data,
//! strategy, backtesting or presentation code, and depends on no other
//! OptionStratLib crate.
//!
//! - [`model`]: [`model::Options`], [`model::Position`], [`model::leg::Leg`]
//!   and its spot, future and perpetual legs, [`model::Trade`], payoff
//!   contracts at expiry and checked `Decimal` helpers.
//! - [`error`]: the errors those types raise.
//! - [`utils`]: time frames and date helpers, the deterministic RNG,
//!   numeric helpers and [`utils::Len`].
//! - [`constants`]: library-wide numeric constants, market conventions and time units.
//!
//! Monetary values are [`rust_decimal::Decimal`] or [`positive::Positive`],
//! and arithmetic on them is checked.
//!
//! ## Foundational types
//!
//! Four standalone crates define the newtypes and enums every OptionStratLib
//! crate shares. Core depends on them and re-exports their types unchanged,
//! never wrapping or copying one, so a value reached through any path below
//! is the same type as the original and needs no conversion. Each crate
//! resolves to a single version across the workspace (`make check-graph`).
//!
//! | Type | Defining crate | Re-exported at |
//! | --- | --- | --- |
//! | `Positive`, `PositiveError`, `is_positive` | `positive` | `optionstratlib_core::model`, `optionstratlib::model`; `Positive` also in `optionstratlib::prelude` |
//! | `pos_or_panic!`, `spos!`, `assert_pos_relative_eq!` | `positive` | `optionstratlib_core`, `optionstratlib::prelude` |
//! | `ExpirationDate`, `ExpirationDateError` | `expiration_date` | `optionstratlib_core::model`, `optionstratlib::model`; `ExpirationDate` also at the `optionstratlib` root and in its prelude |
//! | `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 |
//! | `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 |
//! | `DAYS_IN_A_YEAR` | `positive` | `optionstratlib_core::constants`, `optionstratlib::constants` |
//! | `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 |
//!
//! The facade's `optionstratlib::model` *is* `optionstratlib_core::model`, so
//! every core path above is also reachable with the `optionstratlib::model`
//! prefix. Enabling `schema` adds `ToSchema` impls to these types; it does not
//! change which type a path names.
//!
//! ## Imports: no prelude
//!
//! This crate has no `prelude` module, by decision (#518). The module roots
//! already are the curated entry points: `model` re-exports the domain types
//! and the foundational ones (`Options`, `Position`, `Trade`, `TradeStatus`,
//! `ExpirationDate`, `Side`, `OptionStyle`, `OptionType`, `Positive`),
//! `model::leg` the leg types, and `utils` `TimeFrame` and `Len`.
//!
//! Measured when the crate was extracted, counting explicit
//! `optionstratlib::{model,utils,constants}::…` imports: 168 of the 178
//! example files import through the facade's `prelude::*`, and the examples
//! name 8 distinct core items explicitly (in 25 files). Tests and benches (139
//! files) name 31 distinct core items in 40 files, 18 of them through deep
//! paths such as `model::decimal::DecimalStats` or `utils::time::…`. Nothing
//! outside the workspace imports `optionstratlib_core` directly. The use is
//! spread thinly across many items, so no small glob would cover it, and the
//! few types every consumer needs are already one path away at the module
//! roots. A crate glob would only duplicate those roots and make every future
//! addition a prelude commitment. The facade prelude is M7-03's to redesign.
//!
//! ```rust
//! use optionstratlib_core::model::leg::{Leg, LegAble, SpotPosition};
//! use optionstratlib_core::model::{
//!     ExpirationDate, OptionStyle, OptionType, Options, Position, Positive, Side, Trade,
//! };
//! use optionstratlib_core::utils::{Len, TimeFrame};
//! use optionstratlib_core::pos_or_panic;
//! ```
//!
//! ## Place in the workspace
//!
//! - **Depends on** no other OptionStratLib crate; the foundational `positive`,
//!   `expiration_date`, `financial_types` and `option_type`, which it re-exports.
//! - **Must not depend on** every other OptionStratLib crate (it is the bottom
//!   layer); `make check-graph` enforces the layering (ADR-0001 D9).
//! - **In the facade:** always present: `optionstratlib::{model, utils,
//!   constants}`, the core errors in `optionstratlib::error`, the root types
//!   `Options`, `ExpirationDate`, `OptionStyle`, `OptionType`, `RainbowType`,
//!   `Side` and the root macros. The facade paths are the same types as the paths
//!   here; the [ownership
//!   map](https://github.com/joaquinbejar/OptionStratLib/blob/main/docs/ownership.md)
//!   lists every one with its feature.
//!
//! Moving from 0.21? The [0.22 architecture and adoption
//! guide](https://github.com/joaquinbejar/OptionStratLib/blob/main/docs/migration-0.22.md)
//! maps each redesign to its replacement workflow; 0.22 keeps no
//! compatibility with 0.21.
//!
//! ## Minimal example
//!
//! ```rust
//! use optionstratlib_core::model::{ExpirationDate, OptionStyle, OptionType, Options, Positive, Side};
//! use optionstratlib_core::pos_or_panic;
//! use rust_decimal_macros::dec;
//!
//! // A call struck at 100 with the underlying at 105; the payoff at
//! // expiry needs no pricing model.
//! let option = Options::new(
//!     OptionType::European,
//!     Side::Long,
//!     "XYZ".to_string(),
//!     pos_or_panic!(100.0),
//!     ExpirationDate::Days(pos_or_panic!(30.0)),
//!     pos_or_panic!(0.2),
//!     Positive::ONE,
//!     pos_or_panic!(105.0),
//!     dec!(0.05),
//!     OptionStyle::Call,
//!     Positive::ZERO,
//!     None,
//! );
//! assert_eq!(option.payoff()?, dec!(5));
//! # Ok::<(), optionstratlib_core::error::OptionsError>(())
//! ```
//!
//! ## Runnable example
//!
//! A runnable program that depends on this crate directly, with the smallest
//! 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`
//! asserts its resolved graph. Core is in every example's graph; this is the smallest program that uses it.
//!
//! ## Features
//!
//! - `schema` (off by default): derives `utoipa::ToSchema` on the core types
//!   and enables the same derive on `positive`, `expiration_date`,
//!   `financial_types` and `option_type`.

/// Domain types: options, positions, legs, trades, payoffs and checked
/// `Decimal` helpers.
pub mod model;

/// Errors raised by the core domain types.
pub mod error;

/// Time frames, date helpers, the deterministic RNG and numeric helpers.
pub mod utils;

/// Library-wide numeric constants, market conventions and time units.
pub mod constants;

pub use positive::{assert_pos_relative_eq, pos_or_panic, spos};

/// Version of the `optionstratlib-core` crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

/// Compiles the Rust examples of this crate's `README.md` as doctests, so the
/// README cannot drift from the API (#554). Exists only under `cfg(doctest)`.
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
pub struct ReadmeDoctests;