1use nautilus_model::enums::{
19 MarketStatus as NautilusMarketStatus, OrderSide, OrderStatus, OrderType, TimeInForce,
20};
21use rust_decimal::Decimal;
22use serde::{Deserialize, Serialize};
23use strum::{AsRefStr, Display, EnumIter, EnumString};
24
25#[derive(
27 Clone,
28 Copy,
29 Debug,
30 PartialEq,
31 Eq,
32 Hash,
33 AsRefStr,
34 Display,
35 EnumIter,
36 EnumString,
37 Serialize,
38 Deserialize,
39)]
40#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
41#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
42pub enum BetfairSide {
43 Back,
45 Lay,
47}
48
49#[derive(
51 Clone,
52 Copy,
53 Debug,
54 PartialEq,
55 Eq,
56 Hash,
57 AsRefStr,
58 Display,
59 EnumIter,
60 EnumString,
61 Serialize,
62 Deserialize,
63)]
64#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
65#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
66pub enum BetfairOrderType {
67 Limit,
69 LimitOnClose,
71 MarketOnClose,
73 MarketAtTheClose,
75}
76
77#[derive(
79 Clone,
80 Copy,
81 Debug,
82 PartialEq,
83 Eq,
84 Hash,
85 AsRefStr,
86 Display,
87 EnumIter,
88 EnumString,
89 Serialize,
90 Deserialize,
91)]
92#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
93#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
94pub enum BetfairOrderStatus {
95 Pending,
97 ExecutionComplete,
99 Executable,
101 Expired,
103}
104
105#[derive(
107 Clone,
108 Copy,
109 Debug,
110 PartialEq,
111 Eq,
112 Hash,
113 AsRefStr,
114 Display,
115 EnumIter,
116 EnumString,
117 Serialize,
118 Deserialize,
119)]
120#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
121#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
122pub enum MarketProjection {
123 Competition,
124 Event,
125 EventType,
126 MarketStartTime,
127 MarketDescription,
128 RunnerDescription,
129 RunnerMetadata,
130}
131
132#[derive(
134 Clone,
135 Copy,
136 Debug,
137 PartialEq,
138 Eq,
139 Hash,
140 AsRefStr,
141 Display,
142 EnumIter,
143 EnumString,
144 Serialize,
145 Deserialize,
146)]
147#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
148#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
149pub enum MarketStatus {
150 Inactive,
151 Open,
152 Suspended,
153 Closed,
154}
155
156#[derive(
158 Clone,
159 Copy,
160 Debug,
161 PartialEq,
162 Eq,
163 Hash,
164 AsRefStr,
165 Display,
166 EnumIter,
167 EnumString,
168 Serialize,
169 Deserialize,
170)]
171#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
172#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
173pub enum MarketSort {
174 MinimumTraded,
175 MaximumTraded,
176 MinimumAvailable,
177 MaximumAvailable,
178 FirstToStart,
179 LastToStart,
180}
181
182#[derive(
184 Clone,
185 Copy,
186 Debug,
187 PartialEq,
188 Eq,
189 Hash,
190 AsRefStr,
191 Display,
192 EnumIter,
193 EnumString,
194 Serialize,
195 Deserialize,
196)]
197#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
198#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
199pub enum MarketBettingType {
200 Odds,
201 Line,
202 Range,
203 AsianHandicapDoubleLine,
204 AsianHandicapSingleLine,
205 FixedOdds,
206}
207
208#[derive(
210 Clone,
211 Copy,
212 Debug,
213 PartialEq,
214 Eq,
215 Hash,
216 AsRefStr,
217 Display,
218 EnumIter,
219 EnumString,
220 Serialize,
221 Deserialize,
222)]
223#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
224#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
225pub enum PriceData {
226 SpAvailable,
227 SpTraded,
228 ExBestOffers,
229 ExAllOffers,
230 ExTraded,
231}
232
233#[derive(
235 Clone,
236 Copy,
237 Debug,
238 PartialEq,
239 Eq,
240 Hash,
241 AsRefStr,
242 Display,
243 EnumIter,
244 EnumString,
245 Serialize,
246 Deserialize,
247)]
248#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
249#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
250pub enum MatchProjection {
251 NoRollup,
252 RolledUpByPrice,
253 RolledUpByAvgPrice,
254}
255
256#[derive(
258 Clone,
259 Copy,
260 Debug,
261 PartialEq,
262 Eq,
263 Hash,
264 AsRefStr,
265 Display,
266 EnumIter,
267 EnumString,
268 Serialize,
269 Deserialize,
270)]
271#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
272#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
273pub enum PriceLadderType {
274 Classic,
275 Finest,
276 LineRange,
277}
278
279#[derive(
281 Clone,
282 Copy,
283 Debug,
284 PartialEq,
285 Eq,
286 Hash,
287 AsRefStr,
288 Display,
289 EnumIter,
290 EnumString,
291 Serialize,
292 Deserialize,
293)]
294#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
295#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
296pub enum OrderProjection {
297 All,
298 Executable,
299 ExecutionComplete,
300}
301
302#[derive(
304 Clone,
305 Copy,
306 Debug,
307 PartialEq,
308 Eq,
309 Hash,
310 AsRefStr,
311 Display,
312 EnumIter,
313 EnumString,
314 Serialize,
315 Deserialize,
316)]
317#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
318#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
319pub enum OrderBy {
320 ByBet,
321 ByMarket,
322 ByMatchTime,
323 ByPlaceTime,
324 BySettledTime,
325 ByVoidTime,
326}
327
328#[derive(
330 Clone,
331 Copy,
332 Debug,
333 PartialEq,
334 Eq,
335 Hash,
336 AsRefStr,
337 Display,
338 EnumIter,
339 EnumString,
340 Serialize,
341 Deserialize,
342)]
343#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
344#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
345pub enum SortDir {
346 EarliestToLatest,
347 LatestToEarliest,
348}
349
350#[derive(
352 Clone,
353 Copy,
354 Debug,
355 PartialEq,
356 Eq,
357 Hash,
358 AsRefStr,
359 Display,
360 EnumIter,
361 EnumString,
362 Serialize,
363 Deserialize,
364)]
365#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
366#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
367pub enum BetfairTimeInForce {
368 FillOrKill,
369}
370
371#[derive(
373 Clone,
374 Copy,
375 Debug,
376 PartialEq,
377 Eq,
378 Hash,
379 AsRefStr,
380 Display,
381 EnumIter,
382 EnumString,
383 Serialize,
384 Deserialize,
385)]
386#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
387#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
388pub enum PersistenceType {
389 Lapse,
391 Persist,
393 MarketOnClose,
395}
396
397#[derive(
399 Clone,
400 Copy,
401 Debug,
402 PartialEq,
403 Eq,
404 Hash,
405 AsRefStr,
406 Display,
407 EnumIter,
408 EnumString,
409 Serialize,
410 Deserialize,
411)]
412#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
413#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
414pub enum ExecutionReportStatus {
415 Success,
416 Failure,
417 ProcessedWithErrors,
418 Timeout,
419}
420
421#[derive(
423 Clone,
424 Copy,
425 Debug,
426 PartialEq,
427 Eq,
428 Hash,
429 AsRefStr,
430 Display,
431 EnumIter,
432 EnumString,
433 Serialize,
434 Deserialize,
435)]
436#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
437#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
438pub enum ExecutionReportErrorCode {
439 ErrorInMatcher,
440 ProcessedWithErrors,
441 BetActionError,
442 InvalidAccountState,
443 InvalidWalletStatus,
444 InsufficientFunds,
445 LossLimitExceeded,
446 MarketSuspended,
447 MarketNotOpenForBetting,
448 DuplicateTransaction,
449 InvalidOrder,
450 InvalidMarketId,
451 PermissionDenied,
452 DuplicateBetids,
453 NoActionRequired,
454 ServiceUnavailable,
455 RejectedByRegulator,
456 NoChasing,
457 RegulatorIsNotAvailable,
458 TooManyInstructions,
459 InvalidMarketVersion,
460 InvalidProfitRatio,
461 EventExposureLimitExceeded,
462 EventMatchedExposureLimitExceeded,
463 EventBlocked,
464}
465
466#[derive(
468 Clone,
469 Copy,
470 Debug,
471 PartialEq,
472 Eq,
473 Hash,
474 AsRefStr,
475 Display,
476 EnumIter,
477 EnumString,
478 Serialize,
479 Deserialize,
480)]
481#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
482#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
483pub enum InstructionReportStatus {
484 Success,
485 Failure,
486 Timeout,
487}
488
489#[derive(
491 Clone,
492 Copy,
493 Debug,
494 PartialEq,
495 Eq,
496 Hash,
497 AsRefStr,
498 Display,
499 EnumIter,
500 EnumString,
501 Serialize,
502 Deserialize,
503)]
504#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
505#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
506pub enum InstructionReportErrorCode {
507 InvalidBetSize,
508 InvalidRunner,
509 BetTakenOrLapsed,
510 BetInProgress,
511 RunnerRemoved,
512 MarketNotOpenForBetting,
513 LossLimitExceeded,
514 MarketNotOpenForBspBetting,
515 InvalidPriceEdit,
516 InvalidOdds,
517 InsufficientFunds,
518 InvalidPersistenceType,
519 ErrorInMatcher,
520 InvalidBackLayCombination,
521 ErrorInOrder,
522 InvalidBidType,
523 InvalidBetId,
524 CancelledNotPlaced,
525 RelatedActionFailed,
526 NoActionRequired,
527 TimeInForceConflict,
528 UnexpectedPersistenceType,
529 InvalidOrderType,
530 UnexpectedMinFillSize,
531 InvalidCustomerOrderRef,
532 InvalidMinFillSize,
533 BetLapsedPriceImprovementTooLarge,
534 InvalidCustomerStrategyRef,
535 InvalidProfitRatio,
536}
537
538#[derive(
540 Clone,
541 Copy,
542 Debug,
543 PartialEq,
544 Eq,
545 Hash,
546 AsRefStr,
547 Display,
548 EnumIter,
549 EnumString,
550 Serialize,
551 Deserialize,
552)]
553#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
554#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
555pub enum RunnerStatus {
556 Active,
557 Winner,
558 Loser,
559 Placed,
560 RemovedVacant,
561 Removed,
562 Hidden,
563}
564
565#[derive(
567 Clone,
568 Copy,
569 Debug,
570 PartialEq,
571 Eq,
572 Hash,
573 AsRefStr,
574 Display,
575 EnumIter,
576 EnumString,
577 Serialize,
578 Deserialize,
579)]
580#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
581#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
582pub enum BetStatus {
583 Settled,
584 Voided,
585 Lapsed,
586 Cancelled,
587}
588
589#[derive(
591 Clone,
592 Copy,
593 Debug,
594 PartialEq,
595 Eq,
596 Hash,
597 AsRefStr,
598 Display,
599 EnumIter,
600 EnumString,
601 Serialize,
602 Deserialize,
603)]
604#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
605#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
606pub enum GroupBy {
607 EventType,
608 Event,
609 Market,
610 Side,
611 Bet,
612 Runner,
613 Strategy,
614}
615
616#[derive(
618 Clone,
619 Copy,
620 Debug,
621 PartialEq,
622 Eq,
623 Hash,
624 AsRefStr,
625 Display,
626 EnumIter,
627 EnumString,
628 Serialize,
629 Deserialize,
630)]
631#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
632#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
633pub enum TimeGranularity {
634 Days,
635 Hours,
636 Minutes,
637}
638
639#[derive(
641 Clone,
642 Copy,
643 Debug,
644 PartialEq,
645 Eq,
646 Hash,
647 AsRefStr,
648 Display,
649 EnumIter,
650 EnumString,
651 Serialize,
652 Deserialize,
653)]
654#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
655#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
656pub enum BetTargetType {
657 BackersProfit,
658 Payout,
659}
660
661#[derive(
663 Clone,
664 Copy,
665 Debug,
666 PartialEq,
667 Eq,
668 Hash,
669 AsRefStr,
670 Display,
671 EnumIter,
672 EnumString,
673 Serialize,
674 Deserialize,
675)]
676#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
677#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
678pub enum BetDelayModel {
679 Passive,
680 Dynamic,
681}
682
683#[derive(
685 Clone,
686 Copy,
687 Debug,
688 PartialEq,
689 Eq,
690 Hash,
691 AsRefStr,
692 Display,
693 EnumIter,
694 EnumString,
695 Serialize,
696 Deserialize,
697)]
698#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
699#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
700pub enum RollupModel {
701 Stake,
702 Payout,
703 ManagedLiability,
704 None,
705}
706
707#[derive(
709 Clone,
710 Copy,
711 Debug,
712 PartialEq,
713 Eq,
714 Hash,
715 AsRefStr,
716 Display,
717 EnumIter,
718 EnumString,
719 Serialize,
720 Deserialize,
721)]
722#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
723#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
724pub enum CertLoginStatus {
725 Success,
726 NoError,
727 Fail,
728 AccountAlreadyLocked,
729 AccountNowLocked,
730 AccountPendingPasswordChange,
731 ActionsRequired,
732 AgentClientMaster,
733 AgentClientMasterSuspended,
734 AuthorizedOnlyForDomainRo,
735 AuthorizedOnlyForDomainSe,
736 BettingRestrictedLocation,
737 CertAuthRequired,
738 ChangePasswordRequired,
739 Closed,
740 DanishAuthorizationRequired,
741 DenmarkMigrationRequired,
742 DuplicateCards,
743 EmailLoginNotAllowed,
744 InputValidationError,
745 InternalError,
746 InternationalTermsAcceptanceRequired,
747 InvalidConnectivityToRegulatorDk,
748 InvalidConnectivityToRegulatorIt,
749 InvalidUsernameOrPassword,
750 ItalianContractAcceptanceRequired,
751 ItalianProfilingAcceptanceRequired,
752 KycSuspend,
753 LoginRestricted,
754 MultipleUsersWithSameCredential,
755 NotAuthorizedByRegulatorDk,
756 NotAuthorizedByRegulatorIt,
757 PendingAuth,
758 PersonalMessageRequired,
759 #[serde(rename = "SECURITY_QUESTION_WRONG_3X")]
760 #[strum(serialize = "SECURITY_QUESTION_WRONG_3X")]
761 SecurityQuestionWrong3x,
762 SecurityRestrictedLocation,
763 SelfExcluded,
764 SpainMigrationRequired,
765 SpanishTermsAcceptanceRequired,
766 StrongAuthCodeRequired,
767 Suspended,
768 SwedenBankIdVerificationRequired,
769 SwedenNationalIdentifierRequired,
770 TelbetTermsConditionsNa,
771 TemporaryBanTooManyRequests,
772 TradingMaster,
773 TradingMasterSuspended,
774 #[serde(other)]
775 Other,
776}
777
778#[derive(
780 Clone,
781 Copy,
782 Debug,
783 PartialEq,
784 Eq,
785 Hash,
786 AsRefStr,
787 Display,
788 EnumIter,
789 EnumString,
790 Serialize,
791 Deserialize,
792)]
793pub enum StreamingSide {
794 #[serde(rename = "B")]
795 #[strum(serialize = "B")]
796 Back,
797 #[serde(rename = "L")]
798 #[strum(serialize = "L")]
799 Lay,
800}
801
802#[derive(
804 Clone,
805 Copy,
806 Debug,
807 PartialEq,
808 Eq,
809 Hash,
810 AsRefStr,
811 Display,
812 EnumIter,
813 EnumString,
814 Serialize,
815 Deserialize,
816)]
817pub enum StreamingOrderStatus {
818 #[serde(rename = "E")]
819 #[strum(serialize = "E")]
820 Executable,
821 #[serde(rename = "EC")]
822 #[strum(serialize = "EC")]
823 ExecutionComplete,
824}
825
826#[derive(
828 Clone,
829 Copy,
830 Debug,
831 PartialEq,
832 Eq,
833 Hash,
834 AsRefStr,
835 Display,
836 EnumIter,
837 EnumString,
838 Serialize,
839 Deserialize,
840)]
841pub enum StreamingPersistenceType {
842 #[serde(rename = "L")]
843 #[strum(serialize = "L")]
844 Lapse,
845 #[serde(rename = "P")]
846 #[strum(serialize = "P")]
847 Persist,
848 #[serde(rename = "MOC")]
849 #[strum(serialize = "MOC")]
850 MarketOnClose,
851}
852
853#[derive(
855 Clone,
856 Copy,
857 Debug,
858 PartialEq,
859 Eq,
860 Hash,
861 AsRefStr,
862 Display,
863 EnumIter,
864 EnumString,
865 Serialize,
866 Deserialize,
867)]
868pub enum StreamingOrderType {
869 #[serde(rename = "L")]
870 #[strum(serialize = "L")]
871 Limit,
872 #[serde(rename = "LOC")]
873 #[strum(serialize = "LOC")]
874 LimitOnClose,
875 #[serde(rename = "MOC")]
876 #[strum(serialize = "MOC")]
877 MarketOnClose,
878}
879
880#[derive(
882 Clone,
883 Copy,
884 Debug,
885 PartialEq,
886 Eq,
887 Hash,
888 AsRefStr,
889 Display,
890 EnumIter,
891 EnumString,
892 Serialize,
893 Deserialize,
894)]
895#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
896#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
897pub enum StatusErrorCode {
898 InvalidInput,
899 Timeout,
900 NoAppKey,
901 InvalidAppKey,
902 NoSession,
903 InvalidSessionInformation,
904 NotAuthorized,
905 MaxConnectionLimitExceeded,
906 TooManyRequests,
907 SubscriptionLimitExceeded,
908 InvalidClock,
909 UnexpectedError,
910 ConnectionFailed,
911 InvalidRequest,
912}
913
914impl StatusErrorCode {
915 #[must_use]
918 pub fn is_race_stream_fatal(&self) -> bool {
919 matches!(
920 self,
921 Self::NoAppKey
922 | Self::InvalidAppKey
923 | Self::NotAuthorized
924 | Self::SubscriptionLimitExceeded
925 | Self::MaxConnectionLimitExceeded
926 )
927 }
928}
929
930#[derive(
932 Clone,
933 Copy,
934 Debug,
935 PartialEq,
936 Eq,
937 Hash,
938 AsRefStr,
939 Display,
940 EnumIter,
941 EnumString,
942 Serialize,
943 Deserialize,
944)]
945#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
946#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
947pub enum ChangeType {
948 Heartbeat,
949 SubImage,
950 ResubDelta,
951}
952
953#[derive(
955 Clone,
956 Copy,
957 Debug,
958 PartialEq,
959 Eq,
960 Hash,
961 AsRefStr,
962 Display,
963 EnumIter,
964 EnumString,
965 Serialize,
966 Deserialize,
967)]
968#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
969#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
970pub enum SegmentType {
971 SegStart,
972 Seg,
973 SegEnd,
974}
975
976#[derive(
978 Clone,
979 Copy,
980 Debug,
981 PartialEq,
982 Eq,
983 Hash,
984 AsRefStr,
985 Display,
986 EnumIter,
987 EnumString,
988 Serialize,
989 Deserialize,
990)]
991#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
992#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
993pub enum LapseStatusReasonCode {
994 MktUnknown,
995 MktInvalid,
996 RnrUnknown,
997 TimeElapsed,
998 CurrencyUnknown,
999 PriceInvalid,
1000 MktSuspended,
1001 MktVersion,
1002 LineTarget,
1003 LineSp,
1004 SpInPlay,
1005 SmallStake,
1006 PriceImpTooLarge,
1007}
1008
1009#[derive(
1011 Clone,
1012 Copy,
1013 Debug,
1014 PartialEq,
1015 Eq,
1016 Hash,
1017 AsRefStr,
1018 Display,
1019 EnumIter,
1020 EnumString,
1021 Serialize,
1022 Deserialize,
1023)]
1024#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1025#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
1026pub enum MarketDataFilterField {
1027 ExBestOffersDisp,
1028 ExBestOffers,
1029 ExAllOffers,
1030 ExTraded,
1031 ExTradedVol,
1032 ExLtp,
1033 ExMarketDef,
1034 SpTraded,
1035 SpProjected,
1036}
1037
1038impl From<BetfairSide> for OrderSide {
1043 fn from(value: BetfairSide) -> Self {
1044 match value {
1045 BetfairSide::Back => Self::Sell,
1046 BetfairSide::Lay => Self::Buy,
1047 }
1048 }
1049}
1050
1051impl From<OrderSide> for BetfairSide {
1052 fn from(value: OrderSide) -> Self {
1053 match value {
1054 OrderSide::Buy => Self::Lay,
1055 OrderSide::Sell => Self::Back,
1056 _ => panic!("Invalid `OrderSide` for Betfair: {value}"),
1057 }
1058 }
1059}
1060
1061impl From<StreamingSide> for OrderSide {
1062 fn from(value: StreamingSide) -> Self {
1063 match value {
1064 StreamingSide::Back => Self::Sell,
1065 StreamingSide::Lay => Self::Buy,
1066 }
1067 }
1068}
1069
1070impl From<BetfairOrderType> for OrderType {
1071 fn from(value: BetfairOrderType) -> Self {
1072 match value {
1073 BetfairOrderType::Limit => Self::Limit,
1074 BetfairOrderType::LimitOnClose => Self::Limit,
1075 BetfairOrderType::MarketOnClose => Self::Market,
1076 BetfairOrderType::MarketAtTheClose => Self::Market,
1077 }
1078 }
1079}
1080
1081impl From<StreamingOrderType> for OrderType {
1082 fn from(value: StreamingOrderType) -> Self {
1083 match value {
1084 StreamingOrderType::Limit => Self::Limit,
1085 StreamingOrderType::LimitOnClose => Self::Limit,
1086 StreamingOrderType::MarketOnClose => Self::Market,
1087 }
1088 }
1089}
1090
1091#[must_use]
1096pub fn resolve_order_status(
1097 status: BetfairOrderStatus,
1098 size_matched: Decimal,
1099 size_cancelled: Decimal,
1100) -> OrderStatus {
1101 match status {
1102 BetfairOrderStatus::Pending => OrderStatus::Submitted,
1103 BetfairOrderStatus::Executable if size_matched > Decimal::ZERO => {
1104 OrderStatus::PartiallyFilled
1105 }
1106 BetfairOrderStatus::Executable => OrderStatus::Accepted,
1107 BetfairOrderStatus::Expired => OrderStatus::Expired,
1108 BetfairOrderStatus::ExecutionComplete => {
1109 resolve_terminal_status(size_matched, size_cancelled)
1110 }
1111 }
1112}
1113
1114#[must_use]
1118pub fn resolve_streaming_order_status(
1119 status: StreamingOrderStatus,
1120 size_matched: Decimal,
1121 size_cancelled: Decimal,
1122) -> OrderStatus {
1123 match status {
1124 StreamingOrderStatus::Executable if size_matched > Decimal::ZERO => {
1125 OrderStatus::PartiallyFilled
1126 }
1127 StreamingOrderStatus::Executable => OrderStatus::Accepted,
1128 StreamingOrderStatus::ExecutionComplete => {
1129 resolve_terminal_status(size_matched, size_cancelled)
1130 }
1131 }
1132}
1133
1134fn resolve_terminal_status(size_matched: Decimal, size_cancelled: Decimal) -> OrderStatus {
1135 if size_matched > Decimal::ZERO && size_cancelled <= Decimal::ZERO {
1136 OrderStatus::Filled
1137 } else {
1138 OrderStatus::Canceled
1142 }
1143}
1144
1145impl From<MarketStatus> for NautilusMarketStatus {
1146 fn from(value: MarketStatus) -> Self {
1147 match value {
1148 MarketStatus::Open => Self::Open,
1149 MarketStatus::Closed => Self::Closed,
1150 MarketStatus::Suspended => Self::Suspended,
1151 MarketStatus::Inactive => Self::NotAvailable,
1152 }
1153 }
1154}
1155
1156impl From<BetfairTimeInForce> for TimeInForce {
1157 fn from(value: BetfairTimeInForce) -> Self {
1158 match value {
1159 BetfairTimeInForce::FillOrKill => Self::Fok,
1160 }
1161 }
1162}
1163
1164impl From<PersistenceType> for TimeInForce {
1165 fn from(value: PersistenceType) -> Self {
1166 match value {
1167 PersistenceType::Lapse => Self::Day,
1168 PersistenceType::Persist => Self::Gtc,
1169 PersistenceType::MarketOnClose => Self::AtTheClose,
1170 }
1171 }
1172}
1173
1174impl From<StreamingPersistenceType> for TimeInForce {
1175 fn from(value: StreamingPersistenceType) -> Self {
1176 match value {
1177 StreamingPersistenceType::Lapse => Self::Day,
1178 StreamingPersistenceType::Persist => Self::Gtc,
1179 StreamingPersistenceType::MarketOnClose => Self::AtTheClose,
1180 }
1181 }
1182}
1183
1184#[cfg(test)]
1185mod tests {
1186 use rstest::rstest;
1187
1188 use super::*;
1189
1190 #[rstest]
1191 #[case(BetfairSide::Back, OrderSide::Sell)]
1192 #[case(BetfairSide::Lay, OrderSide::Buy)]
1193 fn test_betfair_side_to_order_side(#[case] input: BetfairSide, #[case] expected: OrderSide) {
1194 assert_eq!(OrderSide::from(input), expected);
1195 }
1196
1197 #[rstest]
1198 #[case(OrderSide::Buy, BetfairSide::Lay)]
1199 #[case(OrderSide::Sell, BetfairSide::Back)]
1200 fn test_order_side_to_betfair_side(#[case] input: OrderSide, #[case] expected: BetfairSide) {
1201 assert_eq!(BetfairSide::from(input), expected);
1202 }
1203
1204 #[rstest]
1205 #[should_panic(expected = "Invalid `OrderSide`")]
1206 fn test_order_side_no_order_side_panics() {
1207 let _ = BetfairSide::from(OrderSide::NoOrderSide);
1208 }
1209
1210 #[rstest]
1211 #[case(StreamingSide::Back, OrderSide::Sell)]
1212 #[case(StreamingSide::Lay, OrderSide::Buy)]
1213 fn test_streaming_side_to_order_side(
1214 #[case] input: StreamingSide,
1215 #[case] expected: OrderSide,
1216 ) {
1217 assert_eq!(OrderSide::from(input), expected);
1218 }
1219
1220 #[rstest]
1221 #[case(BetfairOrderType::Limit, OrderType::Limit)]
1222 #[case(BetfairOrderType::LimitOnClose, OrderType::Limit)]
1223 #[case(BetfairOrderType::MarketOnClose, OrderType::Market)]
1224 #[case(BetfairOrderType::MarketAtTheClose, OrderType::Market)]
1225 fn test_betfair_order_type(#[case] input: BetfairOrderType, #[case] expected: OrderType) {
1226 assert_eq!(OrderType::from(input), expected);
1227 }
1228
1229 #[rstest]
1230 #[case(StreamingOrderType::Limit, OrderType::Limit)]
1231 #[case(StreamingOrderType::LimitOnClose, OrderType::Limit)]
1232 #[case(StreamingOrderType::MarketOnClose, OrderType::Market)]
1233 fn test_streaming_order_type(#[case] input: StreamingOrderType, #[case] expected: OrderType) {
1234 assert_eq!(OrderType::from(input), expected);
1235 }
1236
1237 #[rstest]
1238 fn test_resolve_order_status_non_terminal() {
1239 assert_eq!(
1240 resolve_order_status(BetfairOrderStatus::Pending, Decimal::ZERO, Decimal::ZERO),
1241 OrderStatus::Submitted,
1242 );
1243 assert_eq!(
1244 resolve_order_status(BetfairOrderStatus::Executable, Decimal::ZERO, Decimal::ZERO),
1245 OrderStatus::Accepted,
1246 );
1247 assert_eq!(
1248 resolve_order_status(BetfairOrderStatus::Expired, Decimal::ZERO, Decimal::ZERO),
1249 OrderStatus::Expired,
1250 );
1251 }
1252
1253 #[rstest]
1254 fn test_resolve_order_status_executable_partially_matched() {
1255 assert_eq!(
1256 resolve_order_status(
1257 BetfairOrderStatus::Executable,
1258 Decimal::new(5, 0),
1259 Decimal::ZERO
1260 ),
1261 OrderStatus::PartiallyFilled,
1262 );
1263 }
1264
1265 #[rstest]
1266 #[case(Decimal::TEN, Decimal::ZERO, OrderStatus::Filled)]
1267 #[case(Decimal::new(5, 0), Decimal::new(5, 0), OrderStatus::Canceled)]
1268 #[case(Decimal::ZERO, Decimal::TEN, OrderStatus::Canceled)]
1269 fn test_resolve_order_status_execution_complete(
1270 #[case] size_matched: Decimal,
1271 #[case] size_cancelled: Decimal,
1272 #[case] expected: OrderStatus,
1273 ) {
1274 assert_eq!(
1275 resolve_order_status(
1276 BetfairOrderStatus::ExecutionComplete,
1277 size_matched,
1278 size_cancelled,
1279 ),
1280 expected,
1281 );
1282 }
1283
1284 #[rstest]
1285 fn test_resolve_streaming_order_status_executable() {
1286 assert_eq!(
1287 resolve_streaming_order_status(
1288 StreamingOrderStatus::Executable,
1289 Decimal::ZERO,
1290 Decimal::ZERO,
1291 ),
1292 OrderStatus::Accepted,
1293 );
1294 }
1295
1296 #[rstest]
1297 fn test_resolve_streaming_order_status_executable_partially_matched() {
1298 assert_eq!(
1299 resolve_streaming_order_status(
1300 StreamingOrderStatus::Executable,
1301 Decimal::new(5, 0),
1302 Decimal::ZERO,
1303 ),
1304 OrderStatus::PartiallyFilled,
1305 );
1306 }
1307
1308 #[rstest]
1309 #[case(Decimal::TEN, Decimal::ZERO, OrderStatus::Filled)]
1310 #[case(Decimal::new(5, 0), Decimal::new(5, 0), OrderStatus::Canceled)]
1311 #[case(Decimal::ZERO, Decimal::TEN, OrderStatus::Canceled)]
1312 fn test_resolve_streaming_order_status_execution_complete(
1313 #[case] size_matched: Decimal,
1314 #[case] size_cancelled: Decimal,
1315 #[case] expected: OrderStatus,
1316 ) {
1317 assert_eq!(
1318 resolve_streaming_order_status(
1319 StreamingOrderStatus::ExecutionComplete,
1320 size_matched,
1321 size_cancelled,
1322 ),
1323 expected,
1324 );
1325 }
1326
1327 #[rstest]
1328 #[case(MarketStatus::Open, NautilusMarketStatus::Open)]
1329 #[case(MarketStatus::Closed, NautilusMarketStatus::Closed)]
1330 #[case(MarketStatus::Suspended, NautilusMarketStatus::Suspended)]
1331 #[case(MarketStatus::Inactive, NautilusMarketStatus::NotAvailable)]
1332 fn test_market_status(#[case] input: MarketStatus, #[case] expected: NautilusMarketStatus) {
1333 assert_eq!(NautilusMarketStatus::from(input), expected);
1334 }
1335
1336 #[rstest]
1337 fn test_betfair_time_in_force() {
1338 assert_eq!(
1339 TimeInForce::from(BetfairTimeInForce::FillOrKill),
1340 TimeInForce::Fok
1341 );
1342 }
1343
1344 #[rstest]
1345 #[case(PersistenceType::Lapse, TimeInForce::Day)]
1346 #[case(PersistenceType::Persist, TimeInForce::Gtc)]
1347 #[case(PersistenceType::MarketOnClose, TimeInForce::AtTheClose)]
1348 fn test_persistence_type_to_time_in_force(
1349 #[case] input: PersistenceType,
1350 #[case] expected: TimeInForce,
1351 ) {
1352 assert_eq!(TimeInForce::from(input), expected);
1353 }
1354
1355 #[rstest]
1356 #[case(StreamingPersistenceType::Lapse, TimeInForce::Day)]
1357 #[case(StreamingPersistenceType::Persist, TimeInForce::Gtc)]
1358 #[case(StreamingPersistenceType::MarketOnClose, TimeInForce::AtTheClose)]
1359 fn test_streaming_persistence_type_to_time_in_force(
1360 #[case] input: StreamingPersistenceType,
1361 #[case] expected: TimeInForce,
1362 ) {
1363 assert_eq!(TimeInForce::from(input), expected);
1364 }
1365
1366 #[rstest]
1367 fn test_resolve_streaming_lapsed_and_voided_count_as_closed() {
1368 assert_eq!(
1371 resolve_streaming_order_status(
1372 StreamingOrderStatus::ExecutionComplete,
1373 Decimal::ZERO,
1374 Decimal::new(5, 0), ),
1376 OrderStatus::Canceled,
1377 );
1378 }
1379
1380 #[rstest]
1381 fn test_resolve_streaming_partial_match_then_cancel() {
1382 assert_eq!(
1384 resolve_streaming_order_status(
1385 StreamingOrderStatus::ExecutionComplete,
1386 Decimal::new(3, 0), Decimal::new(7, 0), ),
1389 OrderStatus::Canceled,
1390 );
1391 }
1392
1393 #[rstest]
1394 #[case(StatusErrorCode::NoAppKey, true)]
1395 #[case(StatusErrorCode::InvalidAppKey, true)]
1396 #[case(StatusErrorCode::NotAuthorized, true)]
1397 #[case(StatusErrorCode::SubscriptionLimitExceeded, true)]
1398 #[case(StatusErrorCode::MaxConnectionLimitExceeded, true)]
1399 #[case(StatusErrorCode::InvalidClock, false)]
1400 #[case(StatusErrorCode::Timeout, false)]
1401 #[case(StatusErrorCode::InvalidInput, false)]
1402 #[case(StatusErrorCode::TooManyRequests, false)]
1403 fn test_is_race_stream_fatal(#[case] code: StatusErrorCode, #[case] expected: bool) {
1404 assert_eq!(code.is_race_stream_fatal(), expected);
1405 }
1406}