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
55/// Helper methods for creating trading-related errors
56///
57/// This implementation provides a set of convenience helper methods for creating
58/// different types of trading errors. These methods create properly structured
59/// error instances with clear, descriptive information about what went wrong.
60///
61/// # Methods
62///
63/// These helper methods simplify error creation throughout the codebase and ensure
64/// that errors have consistent formatting and information.
65impl TradeError {
66    /// Creates a generic invalid trade error
67    ///
68    /// # Parameters
69    ///
70    /// * `reason` - A description of why the trade is invalid
71    ///
72    /// # Returns
73    ///
74    /// A `TradeError::InvalidTrade` variant
75    #[must_use]
76    #[cold]
77    #[inline(never)]
78    pub fn invalid_trade(reason: &str) -> Self {
79        TradeError::InvalidTrade {
80            reason: reason.to_string(),
81        }
82    }
83
84    /// Creates a generic invalid trade status error
85    ///
86    /// # Parameters
87    ///
88    /// * `reason` - A description of why the trade status is invalid
89    ///
90    /// # Returns
91    ///
92    /// A `TradeError::InvalidTradeStatus` variant
93    #[must_use]
94    #[cold]
95    #[inline(never)]
96    pub fn invalid_trade_status(reason: &str) -> Self {
97        TradeError::InvalidTradeStatus {
98            reason: reason.to_string(),
99        }
100    }
101
102    /// Creates an arithmetic overflow error for `operation`.
103    ///
104    /// # Parameters
105    ///
106    /// * `operation` - The computation that overflowed
107    /// * `reason` - The underlying arithmetic error
108    ///
109    /// # Returns
110    ///
111    /// A `TradeError::ArithmeticOverflow` variant
112    #[must_use]
113    #[cold]
114    #[inline(never)]
115    pub fn arithmetic_overflow(operation: &'static str, reason: impl std::fmt::Display) -> Self {
116        TradeError::ArithmeticOverflow {
117            operation,
118            reason: reason.to_string(),
119        }
120    }
121}
122
123impl From<&str> for TradeError {
124    fn from(s: &str) -> Self {
125        TradeError::InvalidTrade {
126            reason: s.to_string(),
127        }
128    }
129}
130
131impl From<String> for TradeError {
132    fn from(s: String) -> Self {
133        TradeError::InvalidTrade { reason: s }
134    }
135}
136
137#[cfg(test)]
138mod tests_trade_errors {
139    use super::*;
140
141    #[test]
142    fn test_invalid_trade_error() {
143        let error = TradeError::InvalidTrade {
144            reason: "Trade is not valid".to_string(),
145        };
146
147        assert_eq!(error.to_string(), "Invalid trade: Trade is not valid");
148    }
149
150    #[test]
151    fn test_trade_error_arithmetic_overflow_names_operation() {
152        let error = TradeError::arithmetic_overflow("Trade::cost", "overflow");
153        assert!(matches!(
154            error,
155            TradeError::ArithmeticOverflow {
156                operation: "Trade::cost",
157                ..
158            }
159        ));
160        assert_eq!(
161            error.to_string(),
162            "Trade arithmetic overflow in Trade::cost: overflow"
163        );
164    }
165
166    #[test]
167    fn test_invalid_trade_status_error() {
168        let error = TradeError::InvalidTradeStatus {
169            reason: "Trade status is not valid".to_string(),
170        };
171
172        assert_eq!(
173            error.to_string(),
174            "Invalid trade status: Trade status is not valid"
175        );
176    }
177
178    #[test]
179    fn test_invalid_trade_error_helper_method() {
180        let error = TradeError::invalid_trade("Trade is not valid");
181        assert_eq!(error.to_string(), "Invalid trade: Trade is not valid");
182    }
183
184    #[test]
185    fn test_invalid_trade_status_error_helper_method() {
186        let error = TradeError::invalid_trade_status("Trade status is not valid");
187        assert_eq!(
188            error.to_string(),
189            "Invalid trade status: Trade status is not valid"
190        );
191    }
192}
193
194#[cfg(test)]
195mod tests_trade_error_from {
196    use super::*;
197
198    #[test]
199    fn from_str_creates_invalid_trade_error() {
200        let err: TradeError = "some error reason".into();
201
202        match err {
203            TradeError::InvalidTrade { reason } => {
204                assert_eq!(reason, "some error reason");
205            }
206            _ => panic!("Expected TradeError::InvalidTrade"),
207        }
208    }
209
210    #[test]
211    fn from_string_creates_invalid_trade_error() {
212        let input = String::from("string-based error reason");
213        let err: TradeError = input.clone().into();
214
215        match err {
216            TradeError::InvalidTrade { reason } => {
217                assert_eq!(reason, input);
218            }
219            _ => panic!("Expected TradeError::InvalidTrade"),
220        }
221    }
222}