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 _ => panic!("Invalid `OrderSide` for Betfair: {value}"),
1065 }
1066 }
1067}
1068
1069impl From<StreamingSide> for OrderSide {
1070 fn from(value: StreamingSide) -> Self {
1071 match value {
1072 StreamingSide::Back => Self::Sell,
1073 StreamingSide::Lay => Self::Buy,
1074 }
1075 }
1076}
1077
1078impl From<BetfairOrderType> for OrderType {
1079 fn from(value: BetfairOrderType) -> Self {
1080 match value {
1081 BetfairOrderType::Limit => Self::Limit,
1082 BetfairOrderType::LimitOnClose => Self::Limit,
1083 BetfairOrderType::MarketOnClose => Self::Market,
1084 BetfairOrderType::MarketAtTheClose => Self::Market,
1085 }
1086 }
1087}
1088
1089impl From<StreamingOrderType> for OrderType {
1090 fn from(value: StreamingOrderType) -> Self {
1091 match value {
1092 StreamingOrderType::Limit => Self::Limit,
1093 StreamingOrderType::LimitOnClose => Self::Limit,
1094 StreamingOrderType::MarketOnClose => Self::Market,
1095 }
1096 }
1097}
1098
1099#[must_use]
1104pub fn resolve_order_status(
1105 status: BetfairOrderStatus,
1106 size_matched: Decimal,
1107 size_cancelled: Decimal,
1108) -> OrderStatus {
1109 match status {
1110 BetfairOrderStatus::Pending => OrderStatus::Submitted,
1111 BetfairOrderStatus::Executable if size_matched > Decimal::ZERO => {
1112 OrderStatus::PartiallyFilled
1113 }
1114 BetfairOrderStatus::Executable => OrderStatus::Accepted,
1115 BetfairOrderStatus::Expired => OrderStatus::Expired,
1116 BetfairOrderStatus::ExecutionComplete => {
1117 resolve_terminal_status(size_matched, size_cancelled)
1118 }
1119 }
1120}
1121
1122#[must_use]
1126pub fn resolve_streaming_order_status(
1127 status: StreamingOrderStatus,
1128 size_matched: Decimal,
1129 size_cancelled: Decimal,
1130) -> OrderStatus {
1131 match status {
1132 StreamingOrderStatus::Executable if size_matched > Decimal::ZERO => {
1133 OrderStatus::PartiallyFilled
1134 }
1135 StreamingOrderStatus::Executable => OrderStatus::Accepted,
1136 StreamingOrderStatus::ExecutionComplete => {
1137 resolve_terminal_status(size_matched, size_cancelled)
1138 }
1139 }
1140}
1141
1142fn resolve_terminal_status(size_matched: Decimal, size_cancelled: Decimal) -> OrderStatus {
1143 if size_matched > Decimal::ZERO && size_cancelled <= Decimal::ZERO {
1144 OrderStatus::Filled
1145 } else {
1146 OrderStatus::Canceled
1150 }
1151}
1152
1153impl From<MarketStatus> for NautilusMarketStatus {
1154 fn from(value: MarketStatus) -> Self {
1155 match value {
1156 MarketStatus::Open => Self::Open,
1157 MarketStatus::Closed => Self::Closed,
1158 MarketStatus::Suspended => Self::Suspended,
1159 MarketStatus::Inactive | MarketStatus::Unknown => Self::NotAvailable,
1160 }
1161 }
1162}
1163
1164impl From<BetfairTimeInForce> for TimeInForce {
1165 fn from(value: BetfairTimeInForce) -> Self {
1166 match value {
1167 BetfairTimeInForce::FillOrKill => Self::Fok,
1168 }
1169 }
1170}
1171
1172impl From<PersistenceType> for TimeInForce {
1173 fn from(value: PersistenceType) -> Self {
1174 match value {
1175 PersistenceType::Lapse => Self::Day,
1176 PersistenceType::Persist => Self::Gtc,
1177 PersistenceType::MarketOnClose => Self::AtTheClose,
1178 }
1179 }
1180}
1181
1182impl From<StreamingPersistenceType> for TimeInForce {
1183 fn from(value: StreamingPersistenceType) -> Self {
1184 match value {
1185 StreamingPersistenceType::Lapse => Self::Day,
1186 StreamingPersistenceType::Persist => Self::Gtc,
1187 StreamingPersistenceType::MarketOnClose => Self::AtTheClose,
1188 }
1189 }
1190}
1191
1192#[cfg(test)]
1193mod tests {
1194 use rstest::rstest;
1195
1196 use super::*;
1197
1198 #[rstest]
1199 fn test_reference_enums_tolerate_unmodeled_values() {
1200 assert_eq!(
1203 serde_json::from_str::<MarketStatus>("\"NEW_STATUS\"").unwrap(),
1204 MarketStatus::Unknown
1205 );
1206 assert_eq!(
1207 serde_json::from_str::<MarketBettingType>("\"NEW_TYPE\"").unwrap(),
1208 MarketBettingType::Unknown
1209 );
1210 assert_eq!(
1211 serde_json::from_str::<PriceLadderType>("\"NEW_LADDER\"").unwrap(),
1212 PriceLadderType::Unknown
1213 );
1214 assert_eq!(
1215 serde_json::from_str::<RunnerStatus>("\"NEW_RUNNER\"").unwrap(),
1216 RunnerStatus::Unknown
1217 );
1218 }
1219
1220 #[rstest]
1221 #[case(BetfairSide::Back, OrderSide::Sell)]
1222 #[case(BetfairSide::Lay, OrderSide::Buy)]
1223 fn test_betfair_side_to_order_side(#[case] input: BetfairSide, #[case] expected: OrderSide) {
1224 assert_eq!(OrderSide::from(input), expected);
1225 }
1226
1227 #[rstest]
1228 #[case(OrderSide::Buy, BetfairSide::Lay)]
1229 #[case(OrderSide::Sell, BetfairSide::Back)]
1230 fn test_order_side_to_betfair_side(#[case] input: OrderSide, #[case] expected: BetfairSide) {
1231 assert_eq!(BetfairSide::from(input), expected);
1232 }
1233
1234 #[rstest]
1235 #[should_panic(expected = "Invalid `OrderSide`")]
1236 fn test_order_side_no_order_side_panics() {
1237 let _ = BetfairSide::from(OrderSide::NoOrderSide);
1238 }
1239
1240 #[rstest]
1241 #[case(StreamingSide::Back, OrderSide::Sell)]
1242 #[case(StreamingSide::Lay, OrderSide::Buy)]
1243 fn test_streaming_side_to_order_side(
1244 #[case] input: StreamingSide,
1245 #[case] expected: OrderSide,
1246 ) {
1247 assert_eq!(OrderSide::from(input), expected);
1248 }
1249
1250 #[rstest]
1251 #[case(BetfairOrderType::Limit, OrderType::Limit)]
1252 #[case(BetfairOrderType::LimitOnClose, OrderType::Limit)]
1253 #[case(BetfairOrderType::MarketOnClose, OrderType::Market)]
1254 #[case(BetfairOrderType::MarketAtTheClose, OrderType::Market)]
1255 fn test_betfair_order_type(#[case] input: BetfairOrderType, #[case] expected: OrderType) {
1256 assert_eq!(OrderType::from(input), expected);
1257 }
1258
1259 #[rstest]
1260 #[case(StreamingOrderType::Limit, OrderType::Limit)]
1261 #[case(StreamingOrderType::LimitOnClose, OrderType::Limit)]
1262 #[case(StreamingOrderType::MarketOnClose, OrderType::Market)]
1263 fn test_streaming_order_type(#[case] input: StreamingOrderType, #[case] expected: OrderType) {
1264 assert_eq!(OrderType::from(input), expected);
1265 }
1266
1267 #[rstest]
1268 fn test_resolve_order_status_non_terminal() {
1269 assert_eq!(
1270 resolve_order_status(BetfairOrderStatus::Pending, Decimal::ZERO, Decimal::ZERO),
1271 OrderStatus::Submitted,
1272 );
1273 assert_eq!(
1274 resolve_order_status(BetfairOrderStatus::Executable, Decimal::ZERO, Decimal::ZERO),
1275 OrderStatus::Accepted,
1276 );
1277 assert_eq!(
1278 resolve_order_status(BetfairOrderStatus::Expired, Decimal::ZERO, Decimal::ZERO),
1279 OrderStatus::Expired,
1280 );
1281 }
1282
1283 #[rstest]
1284 fn test_resolve_order_status_executable_partially_matched() {
1285 assert_eq!(
1286 resolve_order_status(
1287 BetfairOrderStatus::Executable,
1288 Decimal::new(5, 0),
1289 Decimal::ZERO
1290 ),
1291 OrderStatus::PartiallyFilled,
1292 );
1293 }
1294
1295 #[rstest]
1296 #[case(Decimal::TEN, Decimal::ZERO, OrderStatus::Filled)]
1297 #[case(Decimal::new(5, 0), Decimal::new(5, 0), OrderStatus::Canceled)]
1298 #[case(Decimal::ZERO, Decimal::TEN, OrderStatus::Canceled)]
1299 fn test_resolve_order_status_execution_complete(
1300 #[case] size_matched: Decimal,
1301 #[case] size_cancelled: Decimal,
1302 #[case] expected: OrderStatus,
1303 ) {
1304 assert_eq!(
1305 resolve_order_status(
1306 BetfairOrderStatus::ExecutionComplete,
1307 size_matched,
1308 size_cancelled,
1309 ),
1310 expected,
1311 );
1312 }
1313
1314 #[rstest]
1315 fn test_resolve_streaming_order_status_executable() {
1316 assert_eq!(
1317 resolve_streaming_order_status(
1318 StreamingOrderStatus::Executable,
1319 Decimal::ZERO,
1320 Decimal::ZERO,
1321 ),
1322 OrderStatus::Accepted,
1323 );
1324 }
1325
1326 #[rstest]
1327 fn test_resolve_streaming_order_status_executable_partially_matched() {
1328 assert_eq!(
1329 resolve_streaming_order_status(
1330 StreamingOrderStatus::Executable,
1331 Decimal::new(5, 0),
1332 Decimal::ZERO,
1333 ),
1334 OrderStatus::PartiallyFilled,
1335 );
1336 }
1337
1338 #[rstest]
1339 #[case(Decimal::TEN, Decimal::ZERO, OrderStatus::Filled)]
1340 #[case(Decimal::new(5, 0), Decimal::new(5, 0), OrderStatus::Canceled)]
1341 #[case(Decimal::ZERO, Decimal::TEN, OrderStatus::Canceled)]
1342 fn test_resolve_streaming_order_status_execution_complete(
1343 #[case] size_matched: Decimal,
1344 #[case] size_cancelled: Decimal,
1345 #[case] expected: OrderStatus,
1346 ) {
1347 assert_eq!(
1348 resolve_streaming_order_status(
1349 StreamingOrderStatus::ExecutionComplete,
1350 size_matched,
1351 size_cancelled,
1352 ),
1353 expected,
1354 );
1355 }
1356
1357 #[rstest]
1358 #[case(MarketStatus::Open, NautilusMarketStatus::Open)]
1359 #[case(MarketStatus::Closed, NautilusMarketStatus::Closed)]
1360 #[case(MarketStatus::Suspended, NautilusMarketStatus::Suspended)]
1361 #[case(MarketStatus::Inactive, NautilusMarketStatus::NotAvailable)]
1362 fn test_market_status(#[case] input: MarketStatus, #[case] expected: NautilusMarketStatus) {
1363 assert_eq!(NautilusMarketStatus::from(input), expected);
1364 }
1365
1366 #[rstest]
1367 fn test_betfair_time_in_force() {
1368 assert_eq!(
1369 TimeInForce::from(BetfairTimeInForce::FillOrKill),
1370 TimeInForce::Fok
1371 );
1372 }
1373
1374 #[rstest]
1375 #[case(PersistenceType::Lapse, TimeInForce::Day)]
1376 #[case(PersistenceType::Persist, TimeInForce::Gtc)]
1377 #[case(PersistenceType::MarketOnClose, TimeInForce::AtTheClose)]
1378 fn test_persistence_type_to_time_in_force(
1379 #[case] input: PersistenceType,
1380 #[case] expected: TimeInForce,
1381 ) {
1382 assert_eq!(TimeInForce::from(input), expected);
1383 }
1384
1385 #[rstest]
1386 #[case(StreamingPersistenceType::Lapse, TimeInForce::Day)]
1387 #[case(StreamingPersistenceType::Persist, TimeInForce::Gtc)]
1388 #[case(StreamingPersistenceType::MarketOnClose, TimeInForce::AtTheClose)]
1389 fn test_streaming_persistence_type_to_time_in_force(
1390 #[case] input: StreamingPersistenceType,
1391 #[case] expected: TimeInForce,
1392 ) {
1393 assert_eq!(TimeInForce::from(input), expected);
1394 }
1395
1396 #[rstest]
1397 fn test_resolve_streaming_lapsed_and_voided_count_as_closed() {
1398 assert_eq!(
1401 resolve_streaming_order_status(
1402 StreamingOrderStatus::ExecutionComplete,
1403 Decimal::ZERO,
1404 Decimal::new(5, 0), ),
1406 OrderStatus::Canceled,
1407 );
1408 }
1409
1410 #[rstest]
1411 fn test_resolve_streaming_partial_match_then_cancel() {
1412 assert_eq!(
1414 resolve_streaming_order_status(
1415 StreamingOrderStatus::ExecutionComplete,
1416 Decimal::new(3, 0), Decimal::new(7, 0), ),
1419 OrderStatus::Canceled,
1420 );
1421 }
1422
1423 #[rstest]
1424 #[case(StatusErrorCode::NoAppKey, true)]
1425 #[case(StatusErrorCode::InvalidAppKey, true)]
1426 #[case(StatusErrorCode::NotAuthorized, true)]
1427 #[case(StatusErrorCode::SubscriptionLimitExceeded, true)]
1428 #[case(StatusErrorCode::MaxConnectionLimitExceeded, true)]
1429 #[case(StatusErrorCode::InvalidClock, false)]
1430 #[case(StatusErrorCode::Timeout, false)]
1431 #[case(StatusErrorCode::InvalidInput, false)]
1432 #[case(StatusErrorCode::TooManyRequests, false)]
1433 fn test_is_race_stream_fatal(#[case] code: StatusErrorCode, #[case] expected: bool) {
1434 assert_eq!(code.is_race_stream_fatal(), expected);
1435 }
1436}