Skip to main content

optionstratlib_core/error/
trade.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 31/12/25
5******************************************************************************/
6
7//! Target crate (ADR-0001 D6, roadmap M1-14): **core**. Owns `TradeError`.
8
9use thiserror::Error;
10
11/// Represents errors that can occur during trade-related operations.
12///
13/// This enum encapsulates various error conditions that may arise during trade execution,
14/// validation, or parameter processing. It provides detailed context about what went wrong
15/// during trading operations.
16///
17/// `TradeError` is particularly useful for diagnosing problems in trade execution and validation.
18#[derive(Error, Debug)]
19pub enum TradeError {
20    /// Error indicating that a trade is invalid.
21    ///
22    /// This variant is used when a trade doesn't meet the requirements
23    /// for valid trading operations.
24    #[error("Invalid trade: {reason}")]
25    InvalidTrade {
26        /// A description explaining why the trade is invalid.
27        reason: String,
28    },
29
30    /// Error indicating that a trade status is invalid.
31    ///
32    /// This variant is used when a trade status doesn't meet the requirements
33    /// for valid trading operations.
34    #[error("Invalid trade status: {reason}")]
35    InvalidTradeStatus {
36        /// A description explaining why the trade status is invalid.
37        reason: String,
38    },
39
40    /// Error indicating that a monetary figure of a trade (its cost, income
41    /// or net) left the representable range.
42    ///
43    /// The premium, the contract size, the fee and the quantity are public
44    /// `Positive` fields, so their product can overflow even though each
45    /// value is valid on its own.
46    #[error("Trade arithmetic overflow in {operation}: {reason}")]
47    ArithmeticOverflow {
48        /// The computation that overflowed, for example `"Trade::cost"`.
49        operation: &'static str,
50        /// The underlying arithmetic error.
51        reason: String,
52    },
53
54    /// The current time was asked for and this build has no clock.
55    #[error(transparent)]
56    Clock(#[from] crate::error::ClockError),
57}
58
59/// Helper methods for creating trading-related errors
60///
61/// This implementation provides a set of convenience helper methods for creating
62/// different types of trading errors. These methods create properly structured
63/// error instances with clear, descriptive information about what went wrong.
64///
65/// # Methods
66///
67/// These helper methods simplify error creation throughout the codebase and ensure
68/// that errors have consistent formatting and information.
69impl TradeError {
70    /// Creates a generic invalid trade error
71    ///
72    /// # Parameters
73    ///
74    /// * `reason` - A description of why the trade is invalid
75    ///
76    /// # Returns
77    ///
78    /// A `TradeError::InvalidTrade` variant
79    #[must_use]
80    #[cold]
81    #[inline(never)]
82    pub fn invalid_trade(reason: &str) -> Self {
83        TradeError::InvalidTrade {
84            reason: reason.to_string(),
85        }
86    }
87
88    /// Creates a generic invalid trade status error
89    ///
90    /// # Parameters
91    ///
92    /// * `reason` - A description of why the trade status is invalid
93    ///
94    /// # Returns
95    ///
96    /// A `TradeError::InvalidTradeStatus` variant
97    #[must_use]
98    #[cold]
99    #[inline(never)]
100    pub fn invalid_trade_status(reason: &str) -> Self {
101        TradeError::InvalidTradeStatus {
102            reason: reason.to_string(),
103        }
104    }
105
106    /// Creates an arithmetic overflow error for `operation`.
107    ///
108    /// # Parameters
109    ///
110    /// * `operation` - The computation that overflowed
111    /// * `reason` - The underlying arithmetic error
112    ///
113    /// # Returns
114    ///
115    /// A `TradeError::ArithmeticOverflow` variant
116    #[must_use]
117    #[cold]
118    #[inline(never)]
119    pub fn arithmetic_overflow(operation: &'static str, reason: impl std::fmt::Display) -> Self {
120        TradeError::ArithmeticOverflow {
121            operation,
122            reason: reason.to_string(),
123        }
124    }
125}
126
127impl From<&str> for TradeError {
128    fn from(s: &str) -> Self {
129        TradeError::InvalidTrade {
130            reason: s.to_string(),
131        }
132    }
133}
134
135impl From<String> for TradeError {
136    fn from(s: String) -> Self {
137        TradeError::InvalidTrade { reason: s }
138    }
139}
140
141#[cfg(test)]
142mod tests_trade_errors {
143    use super::*;
144
145    #[test]
146    fn test_invalid_trade_error() {
147        let error = TradeError::InvalidTrade {
148            reason: "Trade is not valid".to_string(),
149        };
150
151        assert_eq!(error.to_string(), "Invalid trade: Trade is not valid");
152    }
153
154    #[test]
155    fn test_trade_error_arithmetic_overflow_names_operation() {
156        let error = TradeError::arithmetic_overflow("Trade::cost", "overflow");
157        assert!(matches!(
158            error,
159            TradeError::ArithmeticOverflow {
160                operation: "Trade::cost",
161                ..
162            }
163        ));
164        assert_eq!(
165            error.to_string(),
166            "Trade arithmetic overflow in Trade::cost: overflow"
167        );
168    }
169
170    #[test]
171    fn test_invalid_trade_status_error() {
172        let error = TradeError::InvalidTradeStatus {
173            reason: "Trade status is not valid".to_string(),
174        };
175
176        assert_eq!(
177            error.to_string(),
178            "Invalid trade status: Trade status is not valid"
179        );
180    }
181
182    #[test]
183    fn test_invalid_trade_error_helper_method() {
184        let error = TradeError::invalid_trade("Trade is not valid");
185        assert_eq!(error.to_string(), "Invalid trade: Trade is not valid");
186    }
187
188    #[test]
189    fn test_invalid_trade_status_error_helper_method() {
190        let error = TradeError::invalid_trade_status("Trade status is not valid");
191        assert_eq!(
192            error.to_string(),
193            "Invalid trade status: Trade status is not valid"
194        );
195    }
196}
197
198#[cfg(test)]
199mod tests_trade_error_from {
200    use super::*;
201
202    #[test]
203    fn from_str_creates_invalid_trade_error() {
204        let err: TradeError = "some error reason".into();
205
206        match err {
207            TradeError::InvalidTrade { reason } => {
208                assert_eq!(reason, "some error reason");
209            }
210            _ => panic!("Expected TradeError::InvalidTrade"),
211        }
212    }
213
214    #[test]
215    fn from_string_creates_invalid_trade_error() {
216        let input = String::from("string-based error reason");
217        let err: TradeError = input.clone().into();
218
219        match err {
220            TradeError::InvalidTrade { reason } => {
221                assert_eq!(reason, input);
222            }
223            _ => panic!("Expected TradeError::InvalidTrade"),
224        }
225    }
226}