optionstratlib_pricing/error/greeks.rs
1/******************************************************************************
2 Author: Joaquín Béjar García
3 Email: jb@taunais.com
4 Date: 25/12/24
5******************************************************************************/
6
7//! # Greeks Error Module
8//!
9//! This module provides error handling for Greek calculations and equations in option pricing.
10//! It defines error types for various mathematical calculations and validations used in
11//! financial derivatives analysis.
12//!
13//! ## Error Types
14//!
15//! ### Greeks Error (`GreeksError`)
16//! Main error enum that encompasses:
17//! * Calculation errors in Greek values
18//! * Input validation errors
19//! * Mathematical operation errors
20//! * Boundary condition errors
21//!
22//! ### Mathematical Error (`MathErrorKind`)
23//! Handles specific mathematical errors:
24//! * Division by zero
25//! * Overflow conditions
26//! * Invalid domain errors
27//! * Convergence failures
28//!
29//! ### Input Validation Error (`InputErrorKind`)
30//! Manages validation of input parameters:
31//! * Invalid volatility values
32//! * Invalid time values
33//! * Invalid price values
34//! * Invalid rate values
35//!
36//! Target crate (ADR-0001 D6, roadmap M1-14): **pricing**. Owns `GreeksError`.
37//!
38//! The `...Kind` enums are canonical in this module (`optionstratlib_pricing::error::greeks`) and are not
39//! flattened into `optionstratlib_pricing::error`: the facade gathers every crate's errors
40//! in one `optionstratlib::error` module, where flat kind names would collide
41//! (`StrategyErrorKind` is defined in both core `error::position` and market
42//! `error::chains`, `PriceErrorKind` in both analytics `error::probability`
43//! and strategies `error::strategies`), so each crate keeps its kinds in its
44//! kind module and the facade re-exports that module (#550).
45
46use crate::error::VolatilityError;
47use optionstratlib_core::error::DecimalError;
48use optionstratlib_core::model::ExpirationDateError;
49use optionstratlib_core::model::Positive;
50use thiserror::Error;
51
52/// Represents errors that can occur during options Greek calculations.
53///
54/// This enum encapsulates the various types of errors that might arise during
55/// the calculation of option Greeks (delta, gamma, theta, vega, rho, vanna, vomma,
56/// veta, etc.) and related financial computations. It provides a structured approach
57/// to error handling by categorizing errors based on their nature and source.
58///
59/// `GreeksError` serves as the primary error type for the Greek calculation system,
60/// allowing for precise error reporting and handling across different calculation stages.
61#[derive(Error, Debug)]
62pub enum GreeksError {
63 /// Errors related to mathematical calculations such as division by zero,
64 /// overflow, domain errors, or convergence failures.
65 #[error("Mathematical error: {0}")]
66 MathError(MathErrorKind),
67
68 /// Errors related to input validation, including invalid volatility, time,
69 /// price, interest rate, or strike price values.
70 #[error("Input validation error: {0}")]
71 InputError(InputErrorKind),
72
73 /// Errors specific to the calculation of individual Greeks (delta, gamma,
74 /// theta, vega, rho) or other option-related computations.
75 #[error("Greek calculation error: {0}")]
76 CalculationError(CalculationErrorKind),
77
78 /// Errors specific to delta-neutrality calculations and adjustments.
79 #[error("Delta neutrality error: {0}")]
80 DeltaNeutrality(DeltaNeutralityErrorKind),
81
82 /// Expiration-date conversion error surfaced during Greeks calculations.
83 #[error(transparent)]
84 ExpirationDate(#[from] ExpirationDateError),
85
86 /// Pricing-layer error surfaced during Greeks calculations (e.g. numerical
87 /// Greeks falling back to a pricing engine).
88 #[error(transparent)]
89 Pricing(Box<crate::error::PricingError>),
90
91 /// Positive value errors
92 #[error(transparent)]
93 PositiveError(#[from] optionstratlib_core::model::PositiveError),
94
95 /// A Greeks kernel produced a non-finite `f64` value (`NaN` /
96 /// `±∞`) at an `f64` → `Decimal` boundary.
97 ///
98 /// Emitted when analytical or numerical Greeks (`delta`, `gamma`,
99 /// `vega`, `theta`, `rho`, `vanna`, `vomma`, `veta`, `charm`,
100 /// `color`, or their finite-difference variants) produce an
101 /// intermediate `f64` that would otherwise be silently cast into
102 /// `Decimal`. `context` is a static call-site tag following the
103 /// same convention as [`optionstratlib_core::error::DecimalError::Overflow`].
104 #[error("greeks non-finite {context}: {value}")]
105 NonFinite {
106 /// Static tag identifying the kernel and step that produced
107 /// the non-finite value.
108 context: &'static str,
109 /// The offending `f64` value (`NaN`, `+∞`, or `-∞`).
110 value: f64,
111 },
112}
113
114impl From<crate::error::PricingError> for GreeksError {
115 #[inline]
116 fn from(err: crate::error::PricingError) -> Self {
117 GreeksError::Pricing(Box::new(err))
118 }
119}
120
121/// Errors raised by delta-neutrality sizing and adjustment helpers.
122///
123/// These variants replace the former `GreeksError::StdError(String)` catch-all for
124/// the `greeks::calculate_delta_neutral_sizes` and strategy delta-neutrality flows,
125/// providing a precise, typed description of why a neutral size could not be
126/// produced.
127#[derive(Error, Debug)]
128pub enum DeltaNeutralityErrorKind {
129 /// One (or both) of the input deltas is zero, which makes neutrality impossible.
130 #[error("deltas cannot be zero for delta neutrality")]
131 ZeroDelta,
132
133 /// The two deltas are equal, so their linear combination cannot produce a zero delta.
134 #[error("deltas cannot be equal for delta neutrality")]
135 EqualDeltas,
136
137 /// The two deltas share the same sign, so they cannot offset each other.
138 #[error("deltas must have opposite signs for delta neutrality")]
139 SameSignDeltas,
140
141 /// A candidate sizing would require negative contract counts.
142 #[error("delta neutrality would require negative position sizes")]
143 NegativePositionSize,
144
145 /// The delta residual after sizing exceeds the neutrality threshold.
146 #[error("could not achieve delta neutrality: residual above threshold")]
147 NotAchievable,
148
149 /// The sum of the calculated sizes does not match the requested total size.
150 #[error("calculated sizes {calculated} do not match requested total size {expected}")]
151 SizeMismatch {
152 /// Sum of the sizes produced by the neutrality solver.
153 calculated: Positive,
154 /// Total size originally requested by the caller.
155 expected: Positive,
156 },
157
158 /// The strategy has no options to evaluate, so delta neutrality is undefined.
159 #[error("no options found for delta-neutral calculation")]
160 EmptyOptions,
161
162 /// An option's delta-per-contract is zero, which prevents neutral sizing.
163 #[error("option delta per contract cannot be zero")]
164 OptionDeltaZero,
165
166 /// The strategy does not hold enough contracts to perform the required sell adjustment.
167 #[error("insufficient contracts to perform the requested delta adjustment")]
168 InsufficientContracts,
169}
170
171/// Represents various types of mathematical errors that can occur during calculations.
172///
173/// This enum provides specific error categories for mathematical operations, allowing
174/// precise error handling and informative reporting. Each variant contains detailed
175/// information about the specific error condition that occurred.
176///
177/// `MathErrorKind` serves as part of the error handling system for numerical calculations,
178/// particularly in financial and statistical contexts where numerical stability and
179/// precision are critical.
180///
181/// # Variants
182///
183/// * `DivisionByZero` - Represents the fundamental error of attempting to divide by zero,
184/// which is mathematically undefined.
185///
186/// * `Overflow` - Represents errors when calculations exceed the numerical limits of
187/// the underlying data type, typically with very large numbers or exponential operations.
188///
189/// * `InvalidDomain` - Represents errors when a mathematical function is evaluated
190/// outside its valid domain, containing both the problematic value and reason.
191///
192/// * `ConvergenceFailure` - Represents errors when an iterative algorithm fails to
193/// converge to a solution within specified parameters.
194///
195/// # Usage
196///
197/// This error type is typically used in financial models, statistical calculations,
198/// and numerical algorithms where precise error identification is essential for
199/// debugging and proper error handling.
200///
201/// ```rust
202/// use optionstratlib_pricing::error::greeks::MathErrorKind;
203///
204/// fn calculate_square_root(value: f64) -> Result<f64, MathErrorKind> {
205/// if value < 0.0 {
206/// return Err(MathErrorKind::InvalidDomain {
207/// value,
208/// reason: "Cannot calculate square root of a negative number".to_string()
209/// });
210/// }
211/// Ok(value.sqrt())
212/// }
213/// ```
214#[derive(Error, Debug)]
215pub enum MathErrorKind {
216 /// Error that occurs when attempting to divide by zero.
217 ///
218 /// This is a fundamental mathematical error that must be caught to prevent undefined behavior.
219 /// In numerical calculations, division by zero is undefined and will cause program crashes
220 /// if not properly handled.
221 #[error("Division by zero")]
222 DivisionByZero,
223
224 /// Error that occurs when a calculation exceeds the numerical limits of the data type.
225 ///
226 /// This typically happens with very large numbers or during exponential operations.
227 /// Overflow errors can lead to incorrect results and should be caught to maintain
228 /// calculation integrity.
229 #[error("Numerical overflow")]
230 Overflow,
231
232 /// Error that occurs when a function is evaluated outside its valid domain.
233 ///
234 /// # Fields
235 /// * `value` - The input value that caused the domain error
236 /// * `reason` - A descriptive explanation of why the value is invalid
237 ///
238 /// Domain errors are common in mathematical functions like logarithms, square roots,
239 /// and trigonometric functions where certain input values are not allowed.
240 #[error("Invalid domain value {value}: {reason}")]
241 InvalidDomain {
242 /// The value that was outside the valid domain
243 value: f64,
244 /// Detailed explanation of why the value is invalid for the operation
245 reason: String,
246 },
247
248 /// Error that occurs when an iterative algorithm fails to converge to a solution.
249 ///
250 /// # Fields
251 /// * `iterations` - The number of iterations performed before failure
252 /// * `tolerance` - The convergence tolerance that was not satisfied
253 ///
254 /// Convergence failures typically occur in numerical methods like Newton-Raphson,
255 /// implied volatility calculations, or other root-finding algorithms.
256 #[error("Failed to converge after {iterations} iterations (tolerance: {tolerance})")]
257 ConvergenceFailure {
258 /// Number of iterations attempted before giving up
259 iterations: usize,
260 /// The tolerance threshold that wasn't met during convergence
261 tolerance: f64,
262 },
263}
264
265/// Represents different types of input validation errors that can occur during financial calculations.
266///
267/// This enum encapsulates various error conditions related to the validation of input parameters
268/// in financial models, particularly for options pricing and risk analysis. Each variant contains
269/// detailed information about the invalid input, including both the problematic value and a reason
270/// explaining why it was rejected.
271///
272/// # Variants
273///
274/// * `InvalidVolatility` - Represents errors related to improper volatility values
275/// such as negative values or unreasonably large inputs that would cause calculation issues.
276///
277/// * `InvalidTime` - Represents errors related to time inputs (typically time to expiration)
278/// that are outside acceptable bounds or otherwise unsuitable for financial calculations.
279///
280/// * `InvalidPrice` - Represents errors related to price inputs (like underlying asset prices)
281/// that are negative or otherwise invalid for the calculation context.
282///
283/// * `InvalidRate` - Represents errors related to interest rate values that are outside
284/// acceptable bounds for the specific financial modeling context.
285///
286/// * `InvalidStrike` - Represents errors related to strike price inputs that are malformed,
287/// out of bounds, or otherwise unsuitable for options calculations.
288///
289/// # Usage
290///
291/// These error kinds are typically used within higher-level error types to provide specific
292/// information about validation failures, enabling precise error handling and informative
293/// error messages for users.
294#[derive(Error, Debug)]
295pub enum InputErrorKind {
296 /// Error indicating an invalid volatility input.
297 ///
298 /// This error occurs when a volatility value is outside acceptable bounds
299 /// (typically negative values or unreasonably large values) or otherwise invalid
300 /// for the calculation being performed.
301 #[error("Invalid volatility {value}: {reason}")]
302 InvalidVolatility {
303 /// The invalid volatility value that was provided
304 value: f64,
305 /// Detailed explanation of why the volatility value is invalid
306 reason: String,
307 },
308
309 /// Error indicating an invalid time input.
310 ///
311 /// This error occurs when a time value (typically representing time to expiration)
312 /// is outside acceptable bounds or otherwise invalid for the calculation being performed.
313 #[error("Invalid time {value}: {reason}")]
314 InvalidTime {
315 /// The invalid time value that was provided (as a Positive type)
316 value: Positive,
317 /// Detailed explanation of why the time value is invalid
318 reason: String,
319 },
320
321 /// Error indicating an invalid price input.
322 ///
323 /// This error occurs when a price value (such as an underlying asset price)
324 /// is outside acceptable bounds (typically negative values) or otherwise
325 /// invalid for the calculation being performed.
326 #[error("Invalid price {value}: {reason}")]
327 InvalidPrice {
328 /// The invalid price value that was provided
329 value: f64,
330 /// Detailed explanation of why the price value is invalid
331 reason: String,
332 },
333
334 /// Error indicating an invalid interest rate input.
335 ///
336 /// This error occurs when an interest rate value is outside acceptable bounds
337 /// or otherwise invalid for the calculation being performed.
338 #[error("Invalid rate {value}: {reason}")]
339 InvalidRate {
340 /// The invalid interest rate value that was provided
341 value: f64,
342 /// Detailed explanation of why the rate value is invalid
343 reason: String,
344 },
345
346 /// Error indicating an invalid strike price input.
347 ///
348 /// This error occurs when a strike price value is outside acceptable bounds,
349 /// in an incorrect format, or otherwise invalid for the calculation being performed.
350 #[error("Invalid strike {value}: {reason}")]
351 InvalidStrike {
352 /// The invalid strike value that was provided (as a String)
353 value: String,
354 /// Detailed explanation of why the strike value is invalid
355 reason: String,
356 },
357}
358
359/// Represents specific error types that can occur during financial derivative calculations.
360///
361/// This enum categorizes errors that happen during the calculation of option Greeks and other
362/// financial metrics. Each variant provides detailed context about what went wrong during
363/// the specific calculation, allowing for precise error handling and debugging.
364///
365/// The enum is designed to be used within a broader error handling system for options pricing
366/// and financial calculations, providing specific error types for different aspects of
367/// the derivatives pricing process.
368#[derive(Error, Debug)]
369pub enum CalculationErrorKind {
370 /// Error in delta calculation
371 ///
372 /// Delta measures the rate of change of the option price with respect to changes
373 /// in the underlying asset's price.
374 #[error("Delta calculation error: {reason}")]
375 DeltaError {
376 /// Detailed description of what caused the delta calculation to fail
377 reason: String,
378 },
379 /// Error in gamma calculation
380 ///
381 /// Gamma measures the rate of change of delta with respect to changes in the
382 /// underlying asset's price.
383 #[error("Gamma calculation error: {reason}")]
384 GammaError {
385 /// Detailed description of what caused the gamma calculation to fail
386 reason: String,
387 },
388 /// Error in theta calculation
389 ///
390 /// Theta measures the rate of decay of the option's value over time, often
391 /// referred to as time decay.
392 #[error("Theta calculation error: {reason}")]
393 ThetaError {
394 /// Detailed description of what caused the theta calculation to fail
395 reason: String,
396 },
397 /// Error in vega calculation
398 ///
399 /// Vega measures the sensitivity of the option price to changes in the
400 /// underlying asset's volatility.
401 #[error("Vega calculation error: {reason}")]
402 VegaError {
403 /// Detailed description of what caused the vega calculation to fail
404 reason: String,
405 },
406 /// Error in rho calculation
407 ///
408 /// Rho measures the sensitivity of the option price to changes in the
409 /// risk-free interest rate.
410 #[error("Rho calculation error: {reason}")]
411 RhoError {
412 /// Detailed description of what caused the rho calculation to fail
413 reason: String,
414 },
415 /// Error in vanna calculation
416 ///
417 /// Vanna measures the sensitivity of the option delta to changes in the
418 /// underlying asset's volatility.
419 #[error("Vanna calculation error: {reason}")]
420 VannaError {
421 /// Detailed description of what caused the vanna calculation to fail
422 reason: String,
423 },
424 /// Error in vomma calculation
425 ///
426 /// Vomma measures the sensitivity of the option vega to changes in the
427 /// underlying asset's volatility.
428 #[error("Vomma calculation error: {reason}")]
429 VommaError {
430 /// Detailed description of what caused the vomma calculation to fail
431 reason: String,
432 },
433 /// Error in veta calculation
434 ///
435 /// Veta measures the sensitivity of the option vega with respect to the
436 /// passage of time.
437 #[error("Veta calculation error: {reason}")]
438 VetaError {
439 /// Detailed description of what caused the veta calculation to fail
440 reason: String,
441 },
442 /// Error in charm calculation
443 ///
444 /// Charm measures the sensitivity of the option delta with respect to the
445 /// passage of time.
446 #[error("Charm calculation error: {reason}")]
447 CharmError {
448 /// Detailed description of what caused the charm calculation to fail
449 reason: String,
450 },
451 /// Error in color calculation
452 ///
453 /// Color measures the sensitivity of the option gamma with respect to the
454 /// passage of time.
455 #[error("Color calculation error: {reason}")]
456 ColorError {
457 /// Detailed description of what caused the color calculation to fail
458 reason: String,
459 },
460 /// Error originating from decimal operations
461 ///
462 /// Wraps a decimal library error that occurred during option calculations,
463 /// typically related to precision, arithmetic operations, or invalid values.
464 #[error(transparent)]
465 DecimalError(#[from] DecimalError),
466}
467
468/// Type alias for Results returned from Greek calculation functions.
469///
470/// This alias wraps the standard Rust `Result` type to provide a specialized
471/// result type for Greek calculations, using `GreeksError` as the error type.
472///
473/// # Type Parameters
474///
475/// * `T` - The success value type that will be returned when operations succeed.
476///
477/// # Related Types
478///
479/// This type alias is part of the error handling system for Greek calculations
480/// and works with the `GreeksError` enum which provides detailed error information.
481///
482/// # Usage Context
483///
484/// Typically used in functions that calculate option Greeks (delta, gamma, theta,
485/// vega, rho, vanna, vomma, veta) and other financial metrics where specialized
486/// error handling for mathematical and input validation errors is needed.
487pub type GreeksResult<T> = Result<T, GreeksError>;
488
489/// Implementation of factory methods for creating specific `GreeksError` instances.
490///
491/// This implementation provides convenient constructor methods for creating different types
492/// of errors that can occur during options Greek calculations. These methods make error creation
493/// more concise and readable in the codebase, while ensuring consistent error formatting.
494impl GreeksError {
495 /// Creates an error for invalid volatility values.
496 ///
497 /// Use this method when a volatility input is outside acceptable bounds or otherwise
498 /// unsuitable for calculations. Common cases include negative values or unreasonably
499 /// large volatilities.
500 ///
501 /// # Parameters
502 /// * `value` - The invalid volatility value that triggered the error
503 /// * `reason` - An explanation of why the volatility value is invalid
504 ///
505 /// # Returns
506 /// A `GreeksError::InputError` with `InvalidVolatility` kind
507 #[must_use]
508 #[cold]
509 #[inline(never)]
510 pub fn invalid_volatility(value: f64, reason: &str) -> Self {
511 GreeksError::InputError(InputErrorKind::InvalidVolatility {
512 value,
513 reason: reason.to_string(),
514 })
515 }
516
517 /// Creates an error for invalid time values.
518 ///
519 /// Use this method when a time input (typically representing time to expiration)
520 /// is outside acceptable bounds or otherwise invalid for calculations.
521 ///
522 /// # Parameters
523 /// * `value` - The invalid time value (as a `Positive` type) that triggered the error
524 /// * `reason` - An explanation of why the time value is invalid
525 ///
526 /// # Returns
527 /// A `GreeksError::InputError` with `InvalidTime` kind
528 #[must_use]
529 #[cold]
530 #[inline(never)]
531 pub fn invalid_time(value: Positive, reason: &str) -> Self {
532 GreeksError::InputError(InputErrorKind::InvalidTime {
533 value,
534 reason: reason.to_string(),
535 })
536 }
537
538 /// Creates an error for delta calculation failures.
539 ///
540 /// Use this method when a calculation of the delta Greek value fails for any reason.
541 /// Delta measures the rate of change of option price with respect to changes in the
542 /// underlying asset price.
543 ///
544 /// # Parameters
545 /// * `reason` - A detailed explanation of what caused the delta calculation to fail
546 ///
547 /// # Returns
548 /// A `GreeksError::CalculationError` with `DeltaError` kind
549 #[must_use]
550 #[cold]
551 #[inline(never)]
552 pub fn delta_error(reason: &str) -> Self {
553 GreeksError::CalculationError(CalculationErrorKind::DeltaError {
554 reason: reason.to_string(),
555 })
556 }
557
558 /// Creates a [`GreeksError::NonFinite`] from a static call-site
559 /// tag and the offending `f64` value.
560 ///
561 /// Intended to be used at `f64` → `Decimal` boundaries inside
562 /// Greeks kernels, as a thin constructor paired with an
563 /// `if !value.is_finite() { .. }` guard.
564 #[cold]
565 #[inline(never)]
566 #[must_use]
567 pub fn non_finite(context: &'static str, value: f64) -> Self {
568 GreeksError::NonFinite { context, value }
569 }
570}
571
572/// Implements conversion from `DecimalError` to `GreeksError`.
573///
574/// This implementation allows decimal calculation errors to be automatically converted
575/// into the appropriate `GreeksError` variant, simplifying error handling when working
576/// with decimal operations in financial calculations.
577///
578impl From<DecimalError> for GreeksError {
579 fn from(error: DecimalError) -> Self {
580 GreeksError::CalculationError(CalculationErrorKind::DecimalError(error))
581 }
582}
583
584/// Implements conversion from `VolatilityError` to `GreeksError`.
585///
586/// This implementation allows volatility-related errors to be automatically converted
587/// into appropriate `InputErrorKind::InvalidVolatility` errors, providing consistent
588/// error handling for invalid volatility values.
589///
590impl From<VolatilityError> for GreeksError {
591 fn from(error: VolatilityError) -> Self {
592 GreeksError::InputError(InputErrorKind::InvalidVolatility {
593 value: 0.0,
594 reason: error.to_string(),
595 })
596 }
597}
598
599impl From<DeltaNeutralityErrorKind> for GreeksError {
600 #[inline]
601 fn from(kind: DeltaNeutralityErrorKind) -> Self {
602 GreeksError::DeltaNeutrality(kind)
603 }
604}
605
606#[cfg(test)]
607mod tests_error_greeks {
608 use super::*;
609
610 #[test]
611 fn test_invalid_volatility_error_creation() {
612 let error = GreeksError::invalid_volatility(-0.5, "Volatility cannot be negative");
613 match error {
614 GreeksError::InputError(InputErrorKind::InvalidVolatility { value, reason }) => {
615 assert_eq!(value, -0.5);
616 assert_eq!(reason, "Volatility cannot be negative");
617 }
618 _ => panic!("Wrong error type"),
619 }
620 }
621
622 #[test]
623 fn test_delta_error_creation() {
624 let error = GreeksError::delta_error("Failed to calculate delta");
625 match error {
626 GreeksError::CalculationError(CalculationErrorKind::DeltaError { reason }) => {
627 assert_eq!(reason, "Failed to calculate delta");
628 }
629 _ => panic!("Wrong error type"),
630 }
631 }
632
633 #[test]
634 fn test_math_error_display() {
635 let error = GreeksError::MathError(MathErrorKind::DivisionByZero);
636 assert_eq!(error.to_string(), "Mathematical error: Division by zero");
637
638 let error = GreeksError::MathError(MathErrorKind::InvalidDomain {
639 value: 1.5,
640 reason: "Value out of range".to_string(),
641 });
642 assert_eq!(
643 error.to_string(),
644 "Mathematical error: Invalid domain value 1.5: Value out of range"
645 );
646 }
647
648 #[test]
649 fn test_input_error_display() {
650 let error = GreeksError::InputError(InputErrorKind::InvalidPrice {
651 value: -100.0,
652 reason: "Price cannot be negative".to_string(),
653 });
654 assert_eq!(
655 error.to_string(),
656 "Input validation error: Invalid price -100: Price cannot be negative"
657 );
658
659 let error = GreeksError::InputError(InputErrorKind::InvalidRate {
660 value: 2.5,
661 reason: "Rate must be between 0 and 1".to_string(),
662 });
663 assert_eq!(
664 error.to_string(),
665 "Input validation error: Invalid rate 2.5: Rate must be between 0 and 1"
666 );
667 }
668
669 #[test]
670 fn test_calculation_error_display() {
671 let error = GreeksError::CalculationError(CalculationErrorKind::GammaError {
672 reason: "Invalid input parameters".to_string(),
673 });
674 assert_eq!(
675 error.to_string(),
676 "Greek calculation error: Gamma calculation error: Invalid input parameters"
677 );
678
679 let error = GreeksError::CalculationError(CalculationErrorKind::VegaError {
680 reason: "Calculation overflow".to_string(),
681 });
682 assert_eq!(
683 error.to_string(),
684 "Greek calculation error: Vega calculation error: Calculation overflow"
685 );
686 }
687
688 #[test]
689 fn test_convergence_failure_display() {
690 let error = GreeksError::MathError(MathErrorKind::ConvergenceFailure {
691 iterations: 1000,
692 tolerance: 0.0001,
693 });
694 assert_eq!(
695 error.to_string(),
696 "Mathematical error: Failed to converge after 1000 iterations (tolerance: 0.0001)"
697 );
698 }
699
700 #[test]
701 fn test_result_type() {
702 fn test_function() -> GreeksResult<f64> {
703 Err(GreeksError::delta_error("Test error"))
704 }
705
706 let result = test_function();
707 assert!(result.is_err());
708 match result {
709 Err(GreeksError::CalculationError(CalculationErrorKind::DeltaError { reason })) => {
710 assert_eq!(reason, "Test error");
711 }
712 _ => panic!("Wrong error type"),
713 }
714 }
715
716 #[test]
717 fn test_debug_implementation() {
718 let error = GreeksError::delta_error("Test error");
719 let debug_string = format!("{error:?}");
720 assert!(debug_string.contains("DeltaError"));
721 assert!(debug_string.contains("Test error"));
722 }
723}
724
725#[cfg(test)]
726mod tests_error_greeks_extended {
727 use super::*;
728 use optionstratlib_core::error::DecimalError::InvalidPrecision;
729 use optionstratlib_core::pos_or_panic;
730
731 #[test]
732 fn test_greeks_error_delta_neutrality() {
733 let error = GreeksError::DeltaNeutrality(DeltaNeutralityErrorKind::ZeroDelta);
734 assert_eq!(
735 format!("{error}"),
736 "Delta neutrality error: deltas cannot be zero for delta neutrality"
737 );
738 }
739
740 #[test]
741 fn test_math_error_overflow() {
742 let error = MathErrorKind::Overflow;
743 assert_eq!(format!("{error}"), "Numerical overflow");
744 }
745
746 #[test]
747 fn test_input_error_invalid_volatility() {
748 let error = InputErrorKind::InvalidVolatility {
749 value: 0.5,
750 reason: "Out of bounds".to_string(),
751 };
752 assert_eq!(format!("{error}"), "Invalid volatility 0.5: Out of bounds");
753 }
754
755 #[test]
756 fn test_calculation_error_delta() {
757 let error = CalculationErrorKind::DeltaError {
758 reason: "Unable to compute delta".to_string(),
759 };
760 assert_eq!(
761 format!("{error}"),
762 "Delta calculation error: Unable to compute delta"
763 );
764 }
765
766 #[test]
767 fn test_calculation_error_theta() {
768 let error = CalculationErrorKind::ThetaError {
769 reason: "Negative time decay".to_string(),
770 };
771 assert_eq!(
772 format!("{error}"),
773 "Theta calculation error: Negative time decay"
774 );
775 }
776
777 #[test]
778 fn test_calculation_error_rho() {
779 let error = CalculationErrorKind::RhoError {
780 reason: "Interest rate too high".to_string(),
781 };
782 assert_eq!(
783 format!("{error}"),
784 "Rho calculation error: Interest rate too high"
785 );
786 }
787
788 #[test]
789 fn test_calculation_error_vanna() {
790 let error = CalculationErrorKind::VannaError {
791 reason: "Unable to compute vanna".to_string(),
792 };
793 assert_eq!(
794 format!("{error}"),
795 "Vanna calculation error: Unable to compute vanna"
796 );
797 }
798
799 #[test]
800 fn test_calculation_error_vomma() {
801 let error = CalculationErrorKind::VommaError {
802 reason: "Unable to compute vomma".to_string(),
803 };
804 assert_eq!(
805 format!("{error}"),
806 "Vomma calculation error: Unable to compute vomma"
807 );
808 }
809
810 #[test]
811 fn test_calculation_error_veta() {
812 let error = CalculationErrorKind::VetaError {
813 reason: "Unable to compute veta".to_string(),
814 };
815 assert_eq!(
816 format!("{error}"),
817 "Veta calculation error: Unable to compute veta"
818 );
819 }
820
821 #[test]
822 fn test_calculation_error_charm() {
823 let error = CalculationErrorKind::CharmError {
824 reason: "Unable to compute charm".to_string(),
825 };
826 assert_eq!(
827 format!("{error}"),
828 "Charm calculation error: Unable to compute charm"
829 );
830 }
831
832 #[test]
833 fn test_calculation_error_color() {
834 let error = CalculationErrorKind::ColorError {
835 reason: "Unable to compute color".to_string(),
836 };
837 assert_eq!(
838 format!("{error}"),
839 "Color calculation error: Unable to compute color"
840 );
841 }
842
843 #[test]
844 fn test_calculation_error_decimal() {
845 use optionstratlib_core::error::DecimalError as DecErr;
846 let decimal_error = DecErr::InvalidPrecision {
847 precision: 0,
848 reason: "Precision error".to_string(),
849 };
850 let error =
851 GreeksError::CalculationError(CalculationErrorKind::DecimalError(decimal_error));
852 let error_string = format!("{error}");
853 assert!(error_string.contains("Invalid decimal precision"));
854 }
855
856 #[test]
857 fn test_invalid_time_constructor() {
858 let error = GreeksError::invalid_time(pos_or_panic!(5.0), "Time must be positive");
859 assert_eq!(
860 format!("{error}"),
861 "Input validation error: Invalid time 5: Time must be positive"
862 );
863 }
864
865 #[test]
866 fn test_decimal_error_conversion() {
867 let decimal_error = InvalidPrecision {
868 precision: 0,
869 reason: "Precision lost".to_string(),
870 };
871
872 let error: GreeksError = decimal_error.into();
873
874 match error {
875 GreeksError::CalculationError(CalculationErrorKind::DecimalError(_)) => {
876 // Conversion successful
877 }
878 _ => panic!("Wrong error variant"),
879 }
880 }
881
882 #[test]
883 fn test_implied_volatility_error_conversion() {
884 let iv_error = VolatilityError::ZeroVega;
885 let error: GreeksError = iv_error.into();
886 assert_eq!(
887 format!("{error}"),
888 "Input validation error: Invalid volatility 0: Vega is zero, cannot calculate implied volatility"
889 );
890 }
891
892 #[test]
893 fn test_delta_neutrality_zero_delta() {
894 let error = GreeksError::DeltaNeutrality(DeltaNeutralityErrorKind::ZeroDelta);
895 assert_eq!(
896 format!("{error}"),
897 "Delta neutrality error: deltas cannot be zero for delta neutrality"
898 );
899 }
900}