Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/chains/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,12 @@ impl OptionChain {
/// this option chain. The method calculates appropriate values for chain size, strike interval,
/// and estimated spread based on the current data.
///
/// # Errors
///
/// Returns [`ChainError::ChainBuildError`] when the chain is empty,
/// when no valid strike interval can be inferred from existing
/// strikes, or when the volatility-surface sampler fails to produce a
/// skew for the generated parameters.
pub fn to_build_params(&self) -> Result<OptionChainBuildParams, ChainError> {
// Calculate chain size based on the distance from ATM strike
let atm_strike = self.atm_strike()?;
Expand Down Expand Up @@ -882,6 +888,12 @@ impl OptionChain {
/// Err(e) => error!("Error finding ATM strike: {}", e),
/// }
/// ```
///
/// # Errors
///
/// Returns [`ChainError::EmptyChainAtm`] when the chain contains no
/// options, or [`ChainError::AtmNotFound`] when no strike is close
/// enough to the underlying to be considered at-the-money.
pub fn atm_strike(&self) -> Result<&Positive, ChainError> {
let option_data = self.atm_option_data()?;
Ok(&option_data.strike_price)
Expand Down Expand Up @@ -1069,6 +1081,12 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets.
///
/// # Errors
///
/// Returns [`ChainError::FileError`] wrapping a `FileErrorKind::IOError`
/// when the file cannot be created or written, or
/// `FileErrorKind::ParseError` when `csv` serialization fails.
pub fn save_to_csv(&self, file_path: &str) -> Result<(), ChainError> {
let full_path = format!("{}/{}.csv", file_path, self.get_title());
let mut wtr = WriterBuilder::new().from_path(full_path)?;
Expand Down Expand Up @@ -1109,6 +1127,13 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets with the `async` feature.
///
/// # Errors
///
/// Returns the same variants as [`OptionChain::save_to_csv`]
/// ([`ChainError::FileError`] wrapping `FileErrorKind::IOError` or
/// `FileErrorKind::ParseError`). A `spawn_blocking` join failure is
/// surfaced as `FileErrorKind::IOError`.
#[cfg(feature = "async")]
pub async fn save_to_csv_async(&self, file_path: &str) -> Result<(), ChainError> {
let path = file_path.to_string();
Expand Down Expand Up @@ -1136,6 +1161,12 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets.
///
/// # Errors
///
/// Returns [`ChainError::FileError`] wrapping a `FileErrorKind::IOError`
/// when the file cannot be created or written, or
/// `FileErrorKind::ParseError` when `serde_json` serialization fails.
pub fn save_to_json(&self, file_path: &str) -> Result<(), ChainError> {
let full_path = format!("{}/{}.json", file_path, self.get_title());
let file = File::create(full_path)?;
Expand All @@ -1148,6 +1179,13 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets with the `async` feature.
///
/// # Errors
///
/// Returns the same variants as [`OptionChain::save_to_json`]
/// ([`ChainError::FileError`] wrapping `FileErrorKind::IOError` or
/// `FileErrorKind::ParseError`). A `spawn_blocking` join failure is
/// surfaced as `FileErrorKind::IOError`.
#[cfg(feature = "async")]
pub async fn save_to_json_async(&self, file_path: &str) -> Result<(), ChainError> {
let path = file_path.to_string();
Expand All @@ -1174,6 +1212,14 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets.
///
/// # Errors
///
/// Returns [`ChainError::FileError`] wrapping `FileErrorKind::IOError`
/// when the CSV file cannot be opened or read, or
/// `FileErrorKind::ParseError` when the CSV records cannot be parsed.
/// Invalid option data (bad strike, volatility or price) surfaces as
/// [`ChainError::OptionDataError`].
pub fn load_from_csv(file_path: &str) -> Result<Self, ChainError> {
let mut rdr = csv::Reader::from_path(file_path)?;
let mut options = BTreeSet::new();
Expand Down Expand Up @@ -1227,6 +1273,12 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets with the `async` feature.
///
/// # Errors
///
/// Returns the same variants as [`OptionChain::load_from_csv`]. A
/// `spawn_blocking` join failure is surfaced as
/// [`ChainError::FileError`] wrapping `FileErrorKind::IOError`.
#[cfg(feature = "async")]
pub async fn load_from_csv_async(file_path: &str) -> Result<Self, ChainError> {
let path = file_path.to_string();
Expand All @@ -1252,6 +1304,12 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets.
///
/// # Errors
///
/// Returns [`ChainError::FileError`] wrapping `FileErrorKind::IOError`
/// when the file cannot be opened, or `FileErrorKind::ParseError`
/// when `serde_json` deserialization fails.
pub fn load_from_json(file_path: &str) -> Result<Self, ChainError> {
let file = File::open(file_path)?;
let mut option_chain: OptionChain = serde_json::from_reader(file)?;
Expand All @@ -1276,6 +1334,12 @@ impl OptionChain {
/// # Note
///
/// This method is only available on non-WebAssembly targets with the `async` feature.
///
/// # Errors
///
/// Returns the same variants as [`OptionChain::load_from_json`]. A
/// `spawn_blocking` join failure is surfaced as
/// [`ChainError::FileError`] wrapping `FileErrorKind::IOError`.
#[cfg(feature = "async")]
pub async fn load_from_json_async(file_path: &str) -> Result<Self, ChainError> {
let path = file_path.to_string();
Expand Down Expand Up @@ -1363,6 +1427,14 @@ impl OptionChain {
/// # Returns
///
/// * `Result<Vec<Position>, ChainError>` - Vector of created positions or error message
///
/// # Errors
///
/// Returns [`ChainError::StrategyError`] wrapping a
/// `StrategyErrorKind::InvalidLegs` when the requested position counts
/// exceed available strikes on either side of the chain, or propagates
/// any [`ChainError::OptionDataError`] produced while materialising the
/// selected strikes into [`Position`] instances.
pub fn get_random_positions(
&self,
params: RandomPositionsParams,
Expand Down Expand Up @@ -2842,6 +2914,13 @@ impl OptionChain {
///
/// # Returns
/// * `Result<(), ChainError>` - Ok if successful, or an error if the operation fails.
///
/// # Errors
///
/// Returns [`ChainError::ExpirationDate`] when the chain's expiration
/// string cannot be parsed, or propagates any
/// [`ChainError::OptionDataError`] surfaced while enriching individual
/// [`OptionData`] entries with extra pricing parameters.
pub fn set_optiondata_extra_params(&mut self) -> Result<(), ChainError> {
let params = OptionDataPriceParams::new(
Some(Box::new(self.underlying_price)),
Expand Down
14 changes: 14 additions & 0 deletions src/chains/rnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,13 @@ pub trait RNDAnalysis {
///
/// # Returns
/// Result containing either RNDResult or an error
///
/// # Errors
///
/// Returns [`ChainError::EmptyDensities`] when no valid density values
/// can be extracted from the chain, or [`ChainError::OptionDataError`]
/// when individual strikes produce numerical failures during the
/// finite-difference second-derivative approximation.
fn calculate_rnd(&self, params: &RNDParameters) -> Result<RNDResult, ChainError>;

/// Calculates the implied volatility skew
Expand All @@ -418,6 +425,13 @@ pub trait RNDAnalysis {
///
/// # Returns
/// Result containing vector of (strike_price, volatility) pairs or an error
///
/// # Errors
///
/// Returns [`ChainError::EmptySkewData`] when no strike in the chain
/// produced a valid implied-volatility sample, or
/// [`ChainError::OptionDataError`] if individual option records carry
/// invalid volatility values.
fn calculate_skew(&self) -> Result<Vec<(Positive, Decimal)>, ChainError>;
}

Expand Down
15 changes: 15 additions & 0 deletions src/curves/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ pub trait BasicCurves {
///
/// * `Result<Curve, CurveError>` - A curve object containing the plotted data points,
/// or an error if the curve could not be generated
///
/// # Errors
///
/// Returns [`CurveError::ConstructionError`] when no strikes
/// produced valid samples for the requested axis, and propagates
/// any `CurveError::GreeksError` or
/// [`CurveError::InterpolationError`] surfaced by the per-strike
/// evaluator.
fn curve(
&self,
axis: &BasicAxisTypes,
Expand All @@ -64,6 +72,13 @@ pub trait BasicCurves {
/// * `Result<(Decimal, Decimal), CurveError>` - A tuple containing (strike price, metric value),
/// or an error if the values could not be extracted
///
/// # Errors
///
/// Returns [`CurveError::ConstructionError`] when the requested
/// axis metric is not available for the option data (e.g.
/// missing implied volatility) and propagates any
/// `CurveError::GreeksError` surfaced while computing Greeks-
/// based axes.
fn get_curve_strike_versus(
&self,
axis: &BasicAxisTypes,
Expand Down
22 changes: 22 additions & 0 deletions src/curves/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ pub trait StatisticalCurve: MetricsExtractor {
/// - `Result<Curve, CurveError>`: A curve matching the specified statistical properties,
/// or an error if generation fails.
///
/// # Errors
///
/// Returns [`CurveError::ConstructionError`] when the requested
/// combination of basic, shape and range-parameter metrics is
/// infeasible (e.g. negative variance, inconsistent quantiles), and
/// propagates [`CurveError::MetricsError`] when the per-sample
/// metric evaluation fails.
fn generate_statistical_curve(
&self,
basic_metrics: &BasicMetrics,
Expand Down Expand Up @@ -253,6 +260,13 @@ pub trait StatisticalCurve: MetricsExtractor {
///
/// # Returns
/// - `Result<Curve, CurveError>`: The generated curve or an error
///
/// # Errors
///
/// Same failure surface as
/// [`StatisticalCurve::generate_statistical_curve`], plus
/// [`CurveError::ConstructionError`] when the refinement loop
/// exhausts its iteration budget without matching the tolerance.
#[allow(clippy::too_many_arguments)]
fn generate_refined_statistical_curve(
&self,
Expand Down Expand Up @@ -307,6 +321,14 @@ pub trait StatisticalCurve: MetricsExtractor {
///
/// # Returns
/// - `Result<bool, CurveError>`: True if metrics match within tolerance, false otherwise
///
/// # Errors
///
/// Propagates any [`CurveError::MetricsError`] or
/// [`CurveError::ConstructionError`] raised while recomputing the
/// curve's actual metrics (typically when the curve contains
/// non-finite samples or lacks the density required for the
/// requested metric).
fn verify_curve_metrics(
&self,
curve: &Curve,
Expand Down
8 changes: 8 additions & 0 deletions src/curves/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ impl Point2D {
///
/// # Usage
/// This function allows constructing a `Point2D` directly from a tuple representation.
///
/// # Errors
///
/// Currently infallible for the blanket `Into<Decimal>` bounds;
/// the `Result` signature is retained so future implementations
/// that can reject non-finite or out-of-range conversions (e.g.
/// via `TryFrom<f64>`) can surface
/// [`CurveError::ConstructionError`] without a breaking change.
pub fn from_tuple<T: Into<Decimal>, U: Into<Decimal>>(x: T, y: U) -> Result<Self, CurveError> {
Ok(Self::new(x, y))
}
Expand Down
4 changes: 2 additions & 2 deletions src/error/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ pub enum OptionsError {
/// A specialized result type for operations related to Options calculations and processing.
///
/// This type alias simplifies error handling for functions that can fail with various
/// options-specific errors. It uses the [`OptionsError`] enum to provide structured
/// options-specific errors. It uses the `OptionsError` enum to provide structured
/// error information about validation failures, pricing issues, Greeks calculations,
/// time-related problems, and other option-specific errors.
///
Expand Down Expand Up @@ -233,7 +233,7 @@ pub enum OptionsError {
/// * Expiration and time value calculations
/// * Option payoff analysis
///
/// [`OptionsError`]: enum.OptionsError.html
/// See `OptionsError` for the full variant list.
pub type OptionsResult<T> = Result<T, OptionsError>;

/// Helper methods for creating common options errors.
Expand Down
7 changes: 7 additions & 0 deletions src/geometrics/analysis/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ impl Metrics {
/// The result provides the basic statistical measures (`BasicMetrics`) and
/// shape metrics (`ShapeMetrics`) that were part of the `CurveMetrics` instance.
///
/// # Errors
///
/// Currently infallible - the conversion is a direct field
/// re-pack. The `Result` signature is retained so future
/// implementations that validate the metrics invariants (e.g.
/// non-negative variance) can surface
/// [`CurveError::MetricsError`] without breaking callers.
pub fn analysis_result(&self) -> Result<AnalysisResult, CurveError> {
Ok(AnalysisResult {
statistics: self.basic,
Expand Down
38 changes: 38 additions & 0 deletions src/geometrics/analysis/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,79 @@ pub trait MetricsExtractor: Len {
/// # Returns
/// - `Ok(BasicMetrics)`: Struct containing mean, median, mode, and standard deviation.
/// - `Err(CurvesError)`: If metrics computation fails.
///
/// # Errors
///
/// Returns [`MetricsError::BasicError`] when the sample is
/// empty, when the variance cannot be computed in `Decimal`
/// precision, or when the mode aggregation is ambiguous for the
/// provided samples.
fn compute_basic_metrics(&self) -> Result<BasicMetrics, MetricsError>;

/// Computes shape-related metrics for the curve.
///
/// # Returns
/// - `Ok(ShapeMetrics)`: Struct containing skewness, kurtosis, peaks, valleys, and inflection points.
/// - `Err(CurvesError)`: If metrics computation fails.
///
/// # Errors
///
/// Returns [`MetricsError::ShapeError`] when the sample has
/// fewer than three points (skewness/kurtosis undefined) or when
/// the central-moment computation overflows the `Decimal` range.
fn compute_shape_metrics(&self) -> Result<ShapeMetrics, MetricsError>;

/// Computes range-related metrics for the curve.
///
/// # Returns
/// - `Ok(RangeMetrics)`: Struct containing min/max points, range, quartiles, and interquartile range.
/// - `Err(CurvesError)`: If metrics computation fails.
///
/// # Errors
///
/// Returns [`MetricsError::RangeError`] when the sample is
/// empty (no min/max available) or when the quartile
/// interpolation fails for a sparse sample.
fn compute_range_metrics(&self) -> Result<RangeMetrics, MetricsError>;

/// Computes trend-related metrics for the curve.
///
/// # Returns
/// - `Ok(TrendMetrics)`: Struct containing slope, intercept, R-squared, and moving average.
/// - `Err(CurvesError)`: If metrics computation fails.
///
/// # Errors
///
/// Returns [`MetricsError::TrendError`] when the linear
/// regression cannot be fit (fewer than two samples or zero
/// variance on the independent axis).
fn compute_trend_metrics(&self) -> Result<TrendMetrics, MetricsError>;

/// Computes risk-related metrics for the curve.
///
/// # Returns
/// - `Ok(RiskMetrics)`: Struct containing volatility, VaR, expected shortfall, beta, and Sharpe ratio.
/// - `Err(CurvesError)`: If metrics computation fails.
///
/// # Errors
///
/// Returns [`MetricsError::RiskError`] when the sample cannot
/// support VaR/ES estimation (fewer than the required quantile
/// sample count) or when the Sharpe ratio denominator is zero.
fn compute_risk_metrics(&self) -> Result<RiskMetrics, MetricsError>;

/// Computes and aggregates all curve metrics into a comprehensive `CurveMetrics` struct.
///
/// # Returns
/// - `Ok(CurveMetrics)`: A struct containing all computed metrics.
/// - `Err(CurvesError)`: If any metrics computation fails.
///
/// # Errors
///
/// Propagates any [`MetricsError`] returned by
/// `compute_basic_metrics`, `compute_shape_metrics`,
/// `compute_range_metrics`, `compute_trend_metrics` or
/// `compute_risk_metrics` (the first failing call short-circuits).
fn compute_curve_metrics(&self) -> Result<Metrics, MetricsError> {
let basic = self.compute_basic_metrics()?;
let shape = self.compute_shape_metrics()?;
Expand Down
Loading
Loading