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