option_chain_orderbook/
error.rs1use rust_decimal::Decimal;
7use thiserror::Error;
8
9pub type Result<T> = std::result::Result<T, Error>;
11
12#[derive(Error, Debug)]
14pub enum Error {
15 #[error("option contract not found: {symbol}")]
17 ContractNotFound {
18 symbol: String,
20 },
21
22 #[error("expiration not found: {expiration}")]
24 ExpirationNotFound {
25 expiration: String,
27 },
28
29 #[error("strike not found: {strike}")]
31 StrikeNotFound {
32 strike: u64,
34 },
35
36 #[error("underlying not found: {underlying}")]
38 UnderlyingNotFound {
39 underlying: String,
41 },
42
43 #[error("no data available: {message}")]
45 NoDataAvailable {
46 message: String,
48 },
49
50 #[error("order book error: {message}")]
52 OrderBookError {
53 message: String,
55 },
56
57 #[error("pricing error: {message}")]
59 PricingError {
60 message: String,
62 },
63
64 #[error("greeks calculation error: {message}")]
66 GreeksError {
67 message: String,
69 },
70
71 #[error(
73 "inventory limit exceeded: {limit_type} limit of {limit} exceeded with value {current}"
74 )]
75 InventoryLimitExceeded {
76 limit_type: String,
78 limit: Decimal,
80 current: Decimal,
82 },
83
84 #[error("risk limit breached: {limit_type}")]
86 RiskLimitBreached {
87 limit_type: String,
89 },
90
91 #[error("hedging error: {message}")]
93 HedgingError {
94 message: String,
96 },
97
98 #[error("quoting error: {message}")]
100 QuotingError {
101 message: String,
103 },
104
105 #[error("market data error: {message}")]
107 MarketDataError {
108 message: String,
110 },
111
112 #[error("adapter error for {exchange}: {message}")]
114 AdapterError {
115 exchange: String,
117 message: String,
119 },
120
121 #[error("configuration error: {message}")]
123 ConfigurationError {
124 message: String,
126 },
127
128 #[error("validation error: {message}")]
130 ValidationError {
131 message: String,
133 },
134
135 #[error("serialization error: {0}")]
137 SerializationError(#[from] serde_json::Error),
138
139 #[error("decimal conversion error: {message}")]
141 DecimalError {
142 message: String,
144 },
145
146 #[error("optionstratlib decimal error: {0}")]
148 OptionStratLibDecimal(#[from] optionstratlib::error::decimal::DecimalError),
149}
150
151impl Error {
152 #[must_use]
154 pub fn contract_not_found(symbol: impl Into<String>) -> Self {
155 Self::ContractNotFound {
156 symbol: symbol.into(),
157 }
158 }
159
160 #[must_use]
162 pub fn expiration_not_found(expiration: impl Into<String>) -> Self {
163 Self::ExpirationNotFound {
164 expiration: expiration.into(),
165 }
166 }
167
168 #[must_use]
170 pub fn strike_not_found(strike: u64) -> Self {
171 Self::StrikeNotFound { strike }
172 }
173
174 #[must_use]
176 pub fn underlying_not_found(underlying: impl Into<String>) -> Self {
177 Self::UnderlyingNotFound {
178 underlying: underlying.into(),
179 }
180 }
181
182 #[must_use]
184 pub fn no_data(message: impl Into<String>) -> Self {
185 Self::NoDataAvailable {
186 message: message.into(),
187 }
188 }
189
190 #[must_use]
192 pub fn orderbook(message: impl Into<String>) -> Self {
193 Self::OrderBookError {
194 message: message.into(),
195 }
196 }
197
198 #[must_use]
200 pub fn pricing(message: impl Into<String>) -> Self {
201 Self::PricingError {
202 message: message.into(),
203 }
204 }
205
206 #[must_use]
208 pub fn greeks(message: impl Into<String>) -> Self {
209 Self::GreeksError {
210 message: message.into(),
211 }
212 }
213
214 #[must_use]
216 pub fn inventory_limit_exceeded(
217 limit_type: impl Into<String>,
218 limit: Decimal,
219 current: Decimal,
220 ) -> Self {
221 Self::InventoryLimitExceeded {
222 limit_type: limit_type.into(),
223 limit,
224 current,
225 }
226 }
227
228 #[must_use]
230 pub fn risk_limit_breached(limit_type: impl Into<String>) -> Self {
231 Self::RiskLimitBreached {
232 limit_type: limit_type.into(),
233 }
234 }
235
236 #[must_use]
238 pub fn hedging(message: impl Into<String>) -> Self {
239 Self::HedgingError {
240 message: message.into(),
241 }
242 }
243
244 #[must_use]
246 pub fn quoting(message: impl Into<String>) -> Self {
247 Self::QuotingError {
248 message: message.into(),
249 }
250 }
251
252 #[must_use]
254 pub fn market_data(message: impl Into<String>) -> Self {
255 Self::MarketDataError {
256 message: message.into(),
257 }
258 }
259
260 #[must_use]
262 pub fn adapter(exchange: impl Into<String>, message: impl Into<String>) -> Self {
263 Self::AdapterError {
264 exchange: exchange.into(),
265 message: message.into(),
266 }
267 }
268
269 #[must_use]
271 pub fn configuration(message: impl Into<String>) -> Self {
272 Self::ConfigurationError {
273 message: message.into(),
274 }
275 }
276
277 #[must_use]
279 pub fn validation(message: impl Into<String>) -> Self {
280 Self::ValidationError {
281 message: message.into(),
282 }
283 }
284
285 #[must_use]
287 pub fn decimal(message: impl Into<String>) -> Self {
288 Self::DecimalError {
289 message: message.into(),
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use rust_decimal_macros::dec;
298
299 #[test]
300 fn test_contract_not_found_error() {
301 let err = Error::contract_not_found("BTC-20240329-50000-C");
302 assert!(err.to_string().contains("BTC-20240329-50000-C"));
303 }
304
305 #[test]
306 fn test_inventory_limit_exceeded_error() {
307 let err = Error::inventory_limit_exceeded("delta", dec!(100000), dec!(150000));
308 let msg = err.to_string();
309 assert!(msg.contains("delta"));
310 assert!(msg.contains("100000"));
311 assert!(msg.contains("150000"));
312 }
313
314 #[test]
315 fn test_adapter_error() {
316 let err = Error::adapter("Deribit", "connection timeout");
317 let msg = err.to_string();
318 assert!(msg.contains("Deribit"));
319 assert!(msg.contains("connection timeout"));
320 }
321
322 #[test]
323 fn test_strike_not_found_error() {
324 let err = Error::strike_not_found(50000);
325 let msg = err.to_string();
326 assert!(msg.contains("50000"));
327 }
328
329 #[test]
330 fn test_underlying_not_found_error() {
331 let err = Error::underlying_not_found("BTC");
332 let msg = err.to_string();
333 assert!(msg.contains("BTC"));
334 }
335
336 #[test]
337 fn test_orderbook_error() {
338 let err = Error::orderbook("order rejected");
339 let msg = err.to_string();
340 assert!(msg.contains("order rejected"));
341 }
342
343 #[test]
344 fn test_pricing_error() {
345 let err = Error::pricing("invalid volatility");
346 let msg = err.to_string();
347 assert!(msg.contains("invalid volatility"));
348 }
349
350 #[test]
351 fn test_greeks_error() {
352 let err = Error::greeks("delta calculation failed");
353 let msg = err.to_string();
354 assert!(msg.contains("delta calculation failed"));
355 }
356
357 #[test]
358 fn test_risk_limit_breached_error() {
359 let err = Error::risk_limit_breached("max_delta");
360 let msg = err.to_string();
361 assert!(msg.contains("max_delta"));
362 }
363
364 #[test]
365 fn test_hedging_error() {
366 let err = Error::hedging("hedge order failed");
367 let msg = err.to_string();
368 assert!(msg.contains("hedge order failed"));
369 }
370
371 #[test]
372 fn test_quoting_error() {
373 let err = Error::quoting("spread too wide");
374 let msg = err.to_string();
375 assert!(msg.contains("spread too wide"));
376 }
377
378 #[test]
379 fn test_market_data_error() {
380 let err = Error::market_data("stale data");
381 let msg = err.to_string();
382 assert!(msg.contains("stale data"));
383 }
384
385 #[test]
386 fn test_configuration_error() {
387 let err = Error::configuration("missing API key");
388 let msg = err.to_string();
389 assert!(msg.contains("missing API key"));
390 }
391
392 #[test]
393 fn test_validation_error() {
394 let err = Error::validation("invalid quantity");
395 let msg = err.to_string();
396 assert!(msg.contains("invalid quantity"));
397 }
398
399 #[test]
400 fn test_decimal_error() {
401 let err = Error::decimal("overflow");
402 let msg = err.to_string();
403 assert!(msg.contains("overflow"));
404 }
405
406 #[test]
407 fn test_no_data_error() {
408 let err = Error::no_data("no strikes available");
409 let msg = err.to_string();
410 assert!(msg.contains("no strikes available"));
411 }
412
413 #[test]
414 fn test_expiration_not_found_error() {
415 let err = Error::expiration_not_found("2024-03-29");
416 let msg = err.to_string();
417 assert!(msg.contains("2024-03-29"));
418 }
419}