1pub mod prelude;
42
43use serde::{Deserialize, Serialize};
44use std::fmt;
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
61#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
62#[repr(u8)]
63pub enum UnderlyingAssetType {
64 Crypto = 0,
66 #[default]
68 Stock = 1,
69 Forex = 2,
71 Commodity = 3,
73 Bond = 4,
75 Other = 5,
77}
78
79impl UnderlyingAssetType {
80 #[must_use]
82 #[inline]
83 pub const fn is_stock(&self) -> bool {
84 matches!(self, Self::Stock)
85 }
86
87 #[must_use]
89 #[inline]
90 pub const fn is_crypto(&self) -> bool {
91 matches!(self, Self::Crypto)
92 }
93
94 #[must_use]
96 #[inline]
97 pub const fn is_forex(&self) -> bool {
98 matches!(self, Self::Forex)
99 }
100
101 #[must_use]
103 #[inline]
104 pub const fn is_commodity(&self) -> bool {
105 matches!(self, Self::Commodity)
106 }
107
108 #[must_use]
110 #[inline]
111 pub const fn is_bond(&self) -> bool {
112 matches!(self, Self::Bond)
113 }
114}
115
116impl fmt::Display for UnderlyingAssetType {
117 #[inline]
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self {
120 Self::Crypto => write!(f, "Crypto"),
121 Self::Stock => write!(f, "Stock"),
122 Self::Forex => write!(f, "Forex"),
123 Self::Commodity => write!(f, "Commodity"),
124 Self::Bond => write!(f, "Bond"),
125 Self::Other => write!(f, "Other"),
126 }
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
146#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
147#[repr(u8)]
148pub enum Action {
149 #[default]
151 Buy = 0,
152 Sell = 1,
154 Other = 2,
156}
157
158impl Action {
159 #[must_use]
161 #[inline]
162 pub const fn is_buy(&self) -> bool {
163 matches!(self, Self::Buy)
164 }
165
166 #[must_use]
168 #[inline]
169 pub const fn is_sell(&self) -> bool {
170 matches!(self, Self::Sell)
171 }
172}
173
174impl fmt::Display for Action {
175 #[inline]
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 match self {
178 Self::Buy => write!(f, "Buy"),
179 Self::Sell => write!(f, "Sell"),
180 Self::Other => write!(f, "Other"),
181 }
182 }
183}
184
185#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
203#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
204#[repr(u8)]
205pub enum Side {
206 #[default]
210 Long = 0,
211 Short = 1,
215}
216
217impl Side {
218 #[must_use]
220 #[inline]
221 pub const fn is_long(&self) -> bool {
222 matches!(self, Self::Long)
223 }
224
225 #[must_use]
227 #[inline]
228 pub const fn is_short(&self) -> bool {
229 matches!(self, Self::Short)
230 }
231
232 #[must_use]
237 #[inline]
238 pub const fn opposite(&self) -> Self {
239 match self {
240 Self::Long => Self::Short,
241 Self::Short => Self::Long,
242 }
243 }
244}
245
246impl fmt::Display for Side {
247 #[inline]
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 match self {
250 Self::Long => write!(f, "Long"),
251 Self::Short => write!(f, "Short"),
252 }
253 }
254}
255
256impl fmt::Debug for Side {
257 #[inline]
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 match self {
260 Self::Long => write!(f, "Side::Long"),
261 Self::Short => write!(f, "Side::Short"),
262 }
263 }
264}
265
266#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
287#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
288#[repr(u8)]
289pub enum OptionStyle {
290 #[default]
294 Call = 0,
295 Put = 1,
299}
300
301impl OptionStyle {
302 #[must_use]
304 #[inline]
305 pub const fn is_call(&self) -> bool {
306 matches!(self, Self::Call)
307 }
308
309 #[must_use]
311 #[inline]
312 pub const fn is_put(&self) -> bool {
313 matches!(self, Self::Put)
314 }
315
316 #[must_use]
321 #[inline]
322 pub const fn opposite(&self) -> Self {
323 match self {
324 Self::Call => Self::Put,
325 Self::Put => Self::Call,
326 }
327 }
328}
329
330impl fmt::Display for OptionStyle {
331 #[inline]
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 match self {
334 Self::Call => write!(f, "Call"),
335 Self::Put => write!(f, "Put"),
336 }
337 }
338}
339
340impl fmt::Debug for OptionStyle {
341 #[inline]
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 match self {
344 Self::Call => write!(f, "OptionStyle::Call"),
345 Self::Put => write!(f, "OptionStyle::Put"),
346 }
347 }
348}
349
350#[cfg(test)]
351#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
352mod tests_underlying_asset_type {
353 use super::*;
354
355 #[test]
356 fn test_default() {
357 assert_eq!(UnderlyingAssetType::default(), UnderlyingAssetType::Stock);
358 }
359
360 #[test]
361 fn test_display() {
362 assert_eq!(format!("{}", UnderlyingAssetType::Crypto), "Crypto");
363 assert_eq!(format!("{}", UnderlyingAssetType::Stock), "Stock");
364 assert_eq!(format!("{}", UnderlyingAssetType::Forex), "Forex");
365 assert_eq!(format!("{}", UnderlyingAssetType::Commodity), "Commodity");
366 assert_eq!(format!("{}", UnderlyingAssetType::Bond), "Bond");
367 assert_eq!(format!("{}", UnderlyingAssetType::Other), "Other");
368 }
369
370 #[test]
371 fn test_is_helpers() {
372 assert!(UnderlyingAssetType::Stock.is_stock());
373 assert!(UnderlyingAssetType::Crypto.is_crypto());
374 assert!(UnderlyingAssetType::Forex.is_forex());
375 assert!(UnderlyingAssetType::Commodity.is_commodity());
376 assert!(UnderlyingAssetType::Bond.is_bond());
377 assert!(!UnderlyingAssetType::Other.is_stock());
378 assert!(!UnderlyingAssetType::Stock.is_crypto());
379 }
380
381 #[test]
382 fn test_copy() {
383 let asset = UnderlyingAssetType::Crypto;
384 let copied = asset;
385 assert_eq!(asset, copied);
386 }
387
388 #[test]
389 fn test_hash() {
390 use std::collections::HashSet;
391 let mut set = HashSet::new();
392 set.insert(UnderlyingAssetType::Stock);
393 set.insert(UnderlyingAssetType::Crypto);
394 set.insert(UnderlyingAssetType::Stock); assert_eq!(set.len(), 2);
396 }
397
398 #[test]
399 fn test_serialization_roundtrip() {
400 let asset = UnderlyingAssetType::Forex;
401 let json = serde_json::to_string(&asset).unwrap();
402 let deserialized: UnderlyingAssetType = serde_json::from_str(&json).unwrap();
403 assert_eq!(asset, deserialized);
404 }
405
406 #[test]
407 fn test_all_variants_serialize() {
408 let variants = [
409 UnderlyingAssetType::Crypto,
410 UnderlyingAssetType::Stock,
411 UnderlyingAssetType::Forex,
412 UnderlyingAssetType::Commodity,
413 UnderlyingAssetType::Bond,
414 UnderlyingAssetType::Other,
415 ];
416 for variant in variants {
417 let json = serde_json::to_string(&variant).unwrap();
418 let deserialized: UnderlyingAssetType = serde_json::from_str(&json).unwrap();
419 assert_eq!(variant, deserialized);
420 }
421 }
422
423 #[test]
424 fn test_repr_u8_size() {
425 assert_eq!(
426 std::mem::size_of::<UnderlyingAssetType>(),
427 1,
428 "UnderlyingAssetType should be 1 byte with #[repr(u8)]"
429 );
430 }
431}
432
433#[cfg(test)]
434#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
435mod tests_action {
436 use super::*;
437
438 #[test]
439 fn test_default() {
440 assert_eq!(Action::default(), Action::Buy);
441 }
442
443 #[test]
444 fn test_display() {
445 assert_eq!(format!("{}", Action::Buy), "Buy");
446 assert_eq!(format!("{}", Action::Sell), "Sell");
447 assert_eq!(format!("{}", Action::Other), "Other");
448 }
449
450 #[test]
451 fn test_is_helpers() {
452 assert!(Action::Buy.is_buy());
453 assert!(!Action::Buy.is_sell());
454 assert!(Action::Sell.is_sell());
455 assert!(!Action::Sell.is_buy());
456 assert!(!Action::Other.is_buy());
457 assert!(!Action::Other.is_sell());
458 }
459
460 #[test]
461 fn test_copy() {
462 let action = Action::Buy;
463 let copied = action;
464 assert_eq!(action, copied);
465 }
466
467 #[test]
468 fn test_serialization_roundtrip() {
469 let action = Action::Sell;
470 let json = serde_json::to_string(&action).unwrap();
471 let deserialized: Action = serde_json::from_str(&json).unwrap();
472 assert_eq!(action, deserialized);
473 }
474
475 #[test]
476 fn test_repr_u8_size() {
477 assert_eq!(
478 std::mem::size_of::<Action>(),
479 1,
480 "Action should be 1 byte with #[repr(u8)]"
481 );
482 }
483}
484
485#[cfg(test)]
486#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
487mod tests_side {
488 use super::*;
489
490 #[test]
491 fn test_default() {
492 assert_eq!(Side::default(), Side::Long);
493 }
494
495 #[test]
496 fn test_display() {
497 assert_eq!(format!("{}", Side::Long), "Long");
498 assert_eq!(format!("{}", Side::Short), "Short");
499 }
500
501 #[test]
502 fn test_debug() {
503 assert_eq!(format!("{:?}", Side::Long), "Side::Long");
504 assert_eq!(format!("{:?}", Side::Short), "Side::Short");
505 }
506
507 #[test]
508 fn test_is_helpers() {
509 assert!(Side::Long.is_long());
510 assert!(!Side::Long.is_short());
511 assert!(Side::Short.is_short());
512 assert!(!Side::Short.is_long());
513 }
514
515 #[test]
516 fn test_opposite() {
517 assert_eq!(Side::Long.opposite(), Side::Short);
518 assert_eq!(Side::Short.opposite(), Side::Long);
519 }
520
521 #[test]
522 fn test_copy() {
523 let side = Side::Long;
524 let copied = side;
525 assert_eq!(side, copied);
526 }
527
528 #[test]
529 fn test_hash() {
530 use std::collections::HashSet;
531 let mut set = HashSet::new();
532 set.insert(Side::Long);
533 set.insert(Side::Short);
534 set.insert(Side::Long); assert_eq!(set.len(), 2);
536 }
537
538 #[test]
539 fn test_serialization_roundtrip() {
540 let side = Side::Short;
541 let json = serde_json::to_string(&side).unwrap();
542 let deserialized: Side = serde_json::from_str(&json).unwrap();
543 assert_eq!(side, deserialized);
544 }
545
546 #[test]
547 fn test_repr_u8_size() {
548 assert_eq!(
549 std::mem::size_of::<Side>(),
550 1,
551 "Side should be 1 byte with #[repr(u8)]"
552 );
553 }
554}
555
556#[cfg(test)]
557#[allow(clippy::unwrap_used, clippy::panic, clippy::expect_used)]
558mod tests_option_style {
559 use super::*;
560
561 #[test]
562 fn test_default() {
563 assert_eq!(OptionStyle::default(), OptionStyle::Call);
564 }
565
566 #[test]
567 fn test_display() {
568 assert_eq!(format!("{}", OptionStyle::Call), "Call");
569 assert_eq!(format!("{}", OptionStyle::Put), "Put");
570 }
571
572 #[test]
573 fn test_debug() {
574 assert_eq!(format!("{:?}", OptionStyle::Call), "OptionStyle::Call");
575 assert_eq!(format!("{:?}", OptionStyle::Put), "OptionStyle::Put");
576 }
577
578 #[test]
579 fn test_is_helpers() {
580 assert!(OptionStyle::Call.is_call());
581 assert!(!OptionStyle::Call.is_put());
582 assert!(OptionStyle::Put.is_put());
583 assert!(!OptionStyle::Put.is_call());
584 }
585
586 #[test]
587 fn test_opposite() {
588 assert_eq!(OptionStyle::Call.opposite(), OptionStyle::Put);
589 assert_eq!(OptionStyle::Put.opposite(), OptionStyle::Call);
590 }
591
592 #[test]
593 fn test_ordering() {
594 assert!(OptionStyle::Call < OptionStyle::Put);
595 }
596
597 #[test]
598 fn test_copy() {
599 let style = OptionStyle::Call;
600 let copied = style;
601 assert_eq!(style, copied);
602 }
603
604 #[test]
605 fn test_hash() {
606 use std::collections::HashSet;
607 let mut set = HashSet::new();
608 set.insert(OptionStyle::Call);
609 set.insert(OptionStyle::Put);
610 set.insert(OptionStyle::Call); assert_eq!(set.len(), 2);
612 }
613
614 #[test]
615 fn test_serialization_roundtrip() {
616 let style = OptionStyle::Put;
617 let json = serde_json::to_string(&style).unwrap();
618 let deserialized: OptionStyle = serde_json::from_str(&json).unwrap();
619 assert_eq!(style, deserialized);
620 }
621
622 #[test]
623 fn test_repr_u8_size() {
624 assert_eq!(
625 std::mem::size_of::<OptionStyle>(),
626 1,
627 "OptionStyle should be 1 byte with #[repr(u8)]"
628 );
629 }
630}