optionstratlib 0.17.3

OptionStratLib is a comprehensive Rust library for options trading and strategy development across multiple asset classes.
Documentation
use crate::Options;
use crate::error::{PricingError, PricingResult};
use crate::pricing::black_76::black_76;
use crate::pricing::black_scholes_model::black_scholes;
use crate::pricing::garman_kohlhagen::garman_kohlhagen;
use crate::simulation::simulator::Simulator;
use positive::Positive;

/// Pricing engine selector for option pricing.
///
/// This enum allows selection between different pricing methods:
/// - `ClosedFormBS`: Uses the Black-Scholes closed-form formula
/// - `ClosedFormBlack76`: Uses the Black-76 closed-form formula
/// - `MonteCarlo`: Uses Monte Carlo simulation with a configured simulator
/// - `ClosedFormGK`: Uses the Garman-Kohlhagen closed-form formula for FX options
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum PricingEngine {
    /// Black-Scholes closed-form pricing for European options.
    ///
    /// This is the fastest pricing method with O(1) complexity.
    /// Best suited for European options with constant volatility assumptions.
    ClosedFormBS,

    /// Black-76 closed-form pricing for options on futures and forwards.
    ///
    /// Fast O(1) pricing for European options on futures, forwards, swaptions, and caps/floors.
    /// The underlying price is the forward price F, not the spot price.
    ClosedFormBlack76,

    /// Monte Carlo simulation-based pricing.
    ///
    /// Uses a configured `Simulator` to generate random price paths and
    /// estimate option prices. Supports various stochastic models through
    /// different `WalkType` configurations.
    MonteCarlo {
        /// The simulator configured with the desired stochastic model
        simulator: Simulator<Positive, Positive>,
    },

    /// Garman–Kohlhagen closed-form pricing for European FX options.
    ///
    /// Fast O(1) pricing for European options on FX spot rates. Uses two
    /// interest rates (domestic via `risk_free_rate`, foreign via
    /// `dividend_yield`). Structurally identical to Black–Scholes–Merton
    /// with `q = r_f`.
    ClosedFormGK,
}

/// Prices an option using the specified pricing engine.
///
/// This is the unified entry point for option pricing that dispatches to
/// the appropriate pricing method based on the engine configuration.
///
/// # Arguments
///
/// * `option` - The option to price
/// * `engine` - The pricing engine to use
///
/// # Returns
///
/// Returns the option price as a `Positive` value, or a `PricingError` if pricing fails.
///
/// # Examples
///
/// ```rust
/// use optionstratlib::pricing::{PricingEngine, price_option};
/// use positive::{Positive, pos_or_panic};
/// use optionstratlib::{ExpirationDate, Options};
/// use optionstratlib::model::types::{OptionStyle, OptionType, Side};
/// use rust_decimal_macros::dec;
///
/// let option = Options {
///     option_type: OptionType::European,
///     side: Side::Long,
///     underlying_symbol: "AAPL".to_string(),
///     strike_price: Positive::HUNDRED,
///     expiration_date: ExpirationDate::Days(pos_or_panic!(30.0)),
///     implied_volatility: pos_or_panic!(0.2),
///     quantity: Positive::ONE,
///     underlying_price: pos_or_panic!(105.0),
///     risk_free_rate: dec!(0.05),
///     option_style: OptionStyle::Call,
///     dividend_yield: pos_or_panic!(0.01),
///     exotic_params: None,
/// };
/// let engine = PricingEngine::ClosedFormBS;
/// let price = price_option(&option, &engine)?;
/// Ok::<(), optionstratlib::error::PricingError>(())
/// ```
///
/// # Errors
///
/// Propagates the original `PricingError` returned by the selected engine
/// without wrapping, so callers can pattern-match on the structured
/// variants. From the closed-form engines (Black–Scholes, Black-76,
/// Garman–Kohlhagen) you may receive [`PricingError::ExpirationDate`],
/// [`PricingError::Greeks`] (for example zero-volatility or non-finite
/// intermediate values bubbled up from `d1`/`d2`), and (Black-76 and
/// Garman–Kohlhagen) [`PricingError::UnsupportedOptionType`] for
/// non-European inputs. From the binomial lattice you may receive
/// [`PricingError::BinomialNodeMissing`] or [`PricingError::SqrtFailure`].
/// The Monte Carlo engine surfaces failures as
/// [`PricingError::SimulationError`], and exotic engines surface their
/// own variants (barrier, binary, compound, chooser, cliquet, lookback,
/// telegraph).
pub fn price_option(option: &Options, engine: &PricingEngine) -> PricingResult<Positive> {
    match engine {
        PricingEngine::ClosedFormBS => {
            let price_decimal = black_scholes(option)?;
            Ok(Positive::new_decimal(price_decimal.abs())?)
        }
        PricingEngine::ClosedFormBlack76 => {
            let price_decimal = black_76(option)?;
            Ok(Positive::new_decimal(price_decimal.abs())?)
        }
        PricingEngine::MonteCarlo { simulator } => simulator
            .get_mc_option_price(option)
            .map_err(|e| PricingError::simulation_error(&e.to_string())),
        PricingEngine::ClosedFormGK => {
            let price_decimal = garman_kohlhagen(option)?;
            Ok(Positive::new_decimal(price_decimal.abs())?)
        }
    }
}

/// Trait for types that can be priced using a pricing engine.
///
/// This trait provides a unified interface for pricing financial instruments.
pub trait Priceable {
    /// Prices the instrument using the specified pricing engine.
    ///
    /// # Arguments
    ///
    /// * `engine` - The pricing engine to use
    ///
    /// # Returns
    ///
    /// Returns the price as a `Positive` value, or a `PricingError` if pricing fails.
    ///
    /// # Errors
    ///
    /// Propagates any `PricingError` returned by the selected
    /// engine; see [`price_option`] for the full variant breakdown.
    fn price(&self, engine: &PricingEngine) -> PricingResult<Positive>;
}

/// Implementation of `Priceable` for `Options`.
///
/// This allows options to be priced using the unified pricing API.
impl Priceable for Options {
    fn price(&self, engine: &PricingEngine) -> PricingResult<Positive> {
        price_option(self, engine)
    }
}