optionstratlib_core/error/clock.rs
1//! Target crate (ADR-0001 D6): **core**. Owns `ClockError`.
2//!
3//! A build without the `clock` feature has no wall clock, and everything that
4//! asks for the current time gets this error rather than a made-up instant: a
5//! wrong time would silently price an option wrongly.
6
7use expiration_date::error::ExpirationDateError;
8use thiserror::Error;
9
10/// The current time was asked for and this build cannot provide it.
11///
12/// [`crate::utils::time::now`], [`crate::utils::time::evaluation_time`] and
13/// [`crate::utils::time::resolution_base`] return it as such, and
14/// `TradeError`, `PositionError`, `ChainError`, `StrategyError` and
15/// `TransactionError` have a `Clock` variant for it.
16///
17/// A time to expiry is different. `years_to_expiration` and its siblings return
18/// the `expiration_date` crate's own error, which cannot name this one, so
19/// [`ExpirationDateError::InvalidDateTime`] holds its message, and it arrives as
20/// `OptionsError::ExpirationDate`, `PricingError::Options`, `GreeksError::ExpirationDate`
21/// and so on.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
23#[non_exhaustive]
24pub enum ClockError {
25 /// The crate was built without the `clock` feature, and the operation has no
26 /// explicit time to use instead. A reference datetime stands in for the clock only
27 /// where a time to expiry or an expiry date is measured, not where a new record is
28 /// stamped.
29 #[error(
30 "the wall clock is unavailable: this build has no `clock` feature, so pass the time \
31 explicitly, through a date argument or the `*_at` form of the call where there is \
32 one; a time to expiry can also measure from the thread's reference datetime"
33 )]
34 Unavailable,
35}
36
37impl From<ClockError> for ExpirationDateError {
38 #[cold]
39 #[inline(never)]
40 fn from(err: ClockError) -> Self {
41 Self::InvalidDateTime(err.to_string())
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn test_clock_error_converts_to_invalid_datetime_keeping_its_message() {
51 let message = ClockError::Unavailable.to_string();
52 let converted = ExpirationDateError::from(ClockError::Unavailable);
53 assert!(
54 matches!(converted, ExpirationDateError::InvalidDateTime(ref text) if *text == message)
55 );
56 assert!(message.contains("wall clock is unavailable"));
57 }
58}