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}