1use nautilus_model::enums::{LiquiditySide, MarketStatusAction, OrderSide, OrderStatus, OrderType};
19use serde::{Deserialize, Serialize};
20use strum::{AsRefStr, Display, EnumString, FromRepr};
21
22#[derive(
24 Clone,
25 Copy,
26 Debug,
27 Default,
28 Display,
29 AsRefStr,
30 EnumString,
31 FromRepr,
32 PartialEq,
33 Eq,
34 Hash,
35 Serialize,
36 Deserialize,
37)]
38#[cfg_attr(
39 feature = "python",
40 pyo3::pyclass(
41 module = "nautilus_trader.adapters.kraken",
42 eq,
43 eq_int,
44 frozen,
45 hash,
46 from_py_object,
47 rename_all = "SCREAMING_SNAKE_CASE",
48 )
49)]
50#[cfg_attr(
51 feature = "python",
52 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.kraken")
53)]
54#[serde(rename_all = "lowercase")]
55#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
56pub enum KrakenEnvironment {
57 #[default]
58 Live,
59 Demo,
60}
61
62#[derive(
64 Clone,
65 Copy,
66 Debug,
67 Default,
68 Display,
69 AsRefStr,
70 EnumString,
71 FromRepr,
72 PartialEq,
73 Eq,
74 Hash,
75 Serialize,
76 Deserialize,
77)]
78#[cfg_attr(
79 feature = "python",
80 pyo3::pyclass(
81 module = "nautilus_trader.adapters.kraken",
82 eq,
83 eq_int,
84 frozen,
85 hash,
86 from_py_object,
87 rename_all = "SCREAMING_SNAKE_CASE",
88 )
89)]
90#[cfg_attr(
91 feature = "python",
92 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.kraken")
93)]
94#[serde(rename_all = "lowercase")]
95#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
96pub enum KrakenProductType {
97 #[default]
98 Spot,
99 Futures,
100}
101
102#[derive(
104 Clone,
105 Copy,
106 Debug,
107 Display,
108 AsRefStr,
109 EnumString,
110 FromRepr,
111 PartialEq,
112 Eq,
113 Hash,
114 Serialize,
115 Deserialize,
116)]
117#[cfg_attr(
118 feature = "python",
119 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
120)]
121#[serde(rename_all = "lowercase")]
122#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
123pub enum KrakenOrderType {
124 Market,
125 Limit,
126 #[serde(rename = "stop-loss")]
127 #[strum(serialize = "stop-loss")]
128 StopLoss,
129 #[serde(rename = "take-profit")]
130 #[strum(serialize = "take-profit")]
131 TakeProfit,
132 #[serde(rename = "stop-loss-limit")]
133 #[strum(serialize = "stop-loss-limit")]
134 StopLossLimit,
135 #[serde(rename = "take-profit-limit")]
136 #[strum(serialize = "take-profit-limit")]
137 TakeProfitLimit,
138 #[serde(rename = "trailing-stop")]
139 #[strum(serialize = "trailing-stop")]
140 TrailingStop,
141 #[serde(rename = "trailing-stop-limit")]
142 #[strum(serialize = "trailing-stop-limit")]
143 TrailingStopLimit,
144 #[serde(rename = "settle-position")]
145 #[strum(serialize = "settle-position")]
146 SettlePosition,
147}
148
149#[derive(
151 Clone,
152 Copy,
153 Debug,
154 Display,
155 AsRefStr,
156 EnumString,
157 FromRepr,
158 PartialEq,
159 Eq,
160 Hash,
161 Serialize,
162 Deserialize,
163)]
164#[cfg_attr(
165 feature = "python",
166 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
167)]
168#[serde(rename_all = "lowercase")]
169#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
170pub enum KrakenOrderSide {
171 Buy,
172 Sell,
173}
174
175#[derive(
177 Clone,
178 Copy,
179 Debug,
180 Display,
181 AsRefStr,
182 EnumString,
183 FromRepr,
184 PartialEq,
185 Eq,
186 Hash,
187 Serialize,
188 Deserialize,
189)]
190#[cfg_attr(
191 feature = "python",
192 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
193)]
194#[serde(rename_all = "UPPERCASE")]
195#[strum(ascii_case_insensitive, serialize_all = "UPPERCASE")]
196pub enum KrakenTimeInForce {
197 #[serde(rename = "IOC")]
198 #[strum(serialize = "IOC")]
199 ImmediateOrCancel,
200 #[serde(rename = "FOK")]
201 #[strum(serialize = "FOK")]
202 FillOrKill,
203 #[serde(rename = "GTC")]
204 #[strum(serialize = "GTC")]
205 GoodTilCancelled,
206 #[serde(rename = "GTD")]
207 #[strum(serialize = "GTD")]
208 GoodTilDate,
209}
210
211#[derive(
213 Clone,
214 Copy,
215 Debug,
216 Display,
217 AsRefStr,
218 EnumString,
219 FromRepr,
220 PartialEq,
221 Eq,
222 Hash,
223 Serialize,
224 Deserialize,
225)]
226#[cfg_attr(
227 feature = "python",
228 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
229)]
230#[serde(rename_all = "lowercase")]
231#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
232pub enum KrakenOrderStatus {
233 Pending,
234 Open,
235 Closed,
236 Canceled,
237 Expired,
238}
239
240#[derive(
242 Clone,
243 Copy,
244 Debug,
245 Display,
246 AsRefStr,
247 EnumString,
248 FromRepr,
249 PartialEq,
250 Eq,
251 Hash,
252 Serialize,
253 Deserialize,
254)]
255#[cfg_attr(
256 feature = "python",
257 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
258)]
259#[serde(rename_all = "lowercase")]
260#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
261pub enum KrakenPositionSide {
262 Long,
263 Short,
264}
265
266#[derive(
268 Clone,
269 Copy,
270 Debug,
271 Display,
272 AsRefStr,
273 EnumString,
274 FromRepr,
275 PartialEq,
276 Eq,
277 Hash,
278 Serialize,
279 Deserialize,
280)]
281#[cfg_attr(
282 feature = "python",
283 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
284)]
285#[serde(rename_all = "snake_case")]
286#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
287pub enum KrakenPairStatus {
288 Online,
289 #[serde(rename = "cancel_only")]
290 #[strum(serialize = "cancel_only")]
291 CancelOnly,
292 #[serde(rename = "post_only")]
293 #[strum(serialize = "post_only")]
294 PostOnly,
295 #[serde(rename = "limit_only")]
296 #[strum(serialize = "limit_only")]
297 LimitOnly,
298 #[serde(rename = "reduce_only")]
299 #[strum(serialize = "reduce_only")]
300 ReduceOnly,
301}
302
303#[derive(
305 Clone,
306 Copy,
307 Debug,
308 Display,
309 AsRefStr,
310 EnumString,
311 FromRepr,
312 PartialEq,
313 Eq,
314 Hash,
315 Serialize,
316 Deserialize,
317)]
318#[cfg_attr(
319 feature = "python",
320 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
321)]
322#[serde(rename_all = "lowercase")]
323#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
324pub enum KrakenSystemStatus {
325 Online,
326 Maintenance,
327 #[serde(rename = "cancel_only")]
328 #[strum(serialize = "cancel_only")]
329 CancelOnly,
330 #[serde(rename = "post_only")]
331 #[strum(serialize = "post_only")]
332 PostOnly,
333}
334
335#[derive(
337 Clone,
338 Copy,
339 Debug,
340 Display,
341 AsRefStr,
342 EnumString,
343 FromRepr,
344 PartialEq,
345 Eq,
346 Hash,
347 Serialize,
348 Deserialize,
349)]
350#[cfg_attr(
351 feature = "python",
352 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
353)]
354#[serde(rename_all = "lowercase")]
355#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
356pub enum KrakenAssetClass {
357 Currency,
358 #[serde(rename = "tokenized_asset")]
359 #[strum(serialize = "tokenized_asset")]
360 TokenizedAsset,
361}
362
363#[derive(
365 Clone,
366 Copy,
367 Debug,
368 Display,
369 AsRefStr,
370 EnumString,
371 FromRepr,
372 PartialEq,
373 Eq,
374 Hash,
375 Serialize,
376 Deserialize,
377)]
378#[cfg_attr(
379 feature = "python",
380 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
381)]
382#[serde(rename_all = "lowercase")]
383#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
384pub enum KrakenFuturesOrderType {
385 #[serde(rename = "lmt", alias = "limit")]
386 #[strum(serialize = "lmt")]
387 Limit,
388 #[serde(rename = "ioc")]
389 #[strum(serialize = "ioc")]
390 Ioc,
391 #[serde(rename = "post")]
392 #[strum(serialize = "post")]
393 Post,
394 #[serde(rename = "mkt", alias = "market")]
395 #[strum(serialize = "mkt")]
396 Market,
397 #[serde(rename = "stp")]
398 #[strum(serialize = "stp")]
399 Stop,
400 #[serde(rename = "stop")]
401 #[strum(serialize = "stop")]
402 StopLower,
403 #[serde(rename = "take_profit")]
404 #[strum(serialize = "take_profit")]
405 TakeProfit,
406 #[serde(rename = "stop_loss")]
407 #[strum(serialize = "stop_loss")]
408 StopLoss,
409 #[serde(rename = "unknown")]
410 Unknown,
411}
412
413#[derive(
415 Clone,
416 Copy,
417 Debug,
418 Display,
419 AsRefStr,
420 EnumString,
421 FromRepr,
422 PartialEq,
423 Eq,
424 Hash,
425 Serialize,
426 Deserialize,
427)]
428#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
429#[strum(ascii_case_insensitive, serialize_all = "SCREAMING_SNAKE_CASE")]
430pub enum KrakenFuturesOrderEventType {
431 Place,
433 Fill,
435 Execution,
437 Reject,
439 Cancel,
441 Edit,
443 #[serde(alias = "EXPIRED")]
445 #[strum(serialize = "EXPIRED")]
446 Expire,
447}
448
449#[derive(
451 Clone,
452 Copy,
453 Debug,
454 Display,
455 AsRefStr,
456 EnumString,
457 FromRepr,
458 PartialEq,
459 Eq,
460 Hash,
461 Serialize,
462 Deserialize,
463)]
464#[cfg_attr(
465 feature = "python",
466 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
467)]
468#[serde(rename_all = "camelCase")]
469#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
470pub enum KrakenFuturesOrderStatus {
471 Untouched,
472 PartiallyFilled,
473 Filled,
474 Cancelled,
475 Expired,
476}
477
478#[derive(
481 Clone,
482 Copy,
483 Debug,
484 Display,
485 AsRefStr,
486 EnumString,
487 FromRepr,
488 PartialEq,
489 Eq,
490 Hash,
491 Serialize,
492 Deserialize,
493)]
494#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
495#[strum(ascii_case_insensitive, serialize_all = "SCREAMING_SNAKE_CASE")]
496pub enum KrakenFuturesOrderLifecycleStatus {
497 EnteredBook,
499 FullyExecuted,
501 Rejected,
503 Cancelled,
506 TriggerPlaced,
508 TriggerActivationFailure,
510}
511
512#[derive(
514 Clone,
515 Copy,
516 Debug,
517 Display,
518 AsRefStr,
519 EnumString,
520 FromRepr,
521 PartialEq,
522 Eq,
523 Hash,
524 Serialize,
525 Deserialize,
526)]
527#[cfg_attr(
528 feature = "python",
529 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
530)]
531#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
532pub enum KrakenTriggerSignal {
533 #[serde(rename = "last", alias = "last_price")]
534 Last,
535 #[serde(rename = "mark", alias = "mark_price")]
536 Mark,
537 #[serde(
538 rename = "spot",
539 alias = "spot_price",
540 alias = "index",
541 alias = "index_price"
542 )]
543 #[strum(
544 serialize = "spot",
545 serialize = "spot_price",
546 serialize = "index",
547 serialize = "index_price"
548 )]
549 Index,
550 #[serde(rename = "unknown")]
551 Unknown,
552}
553
554#[derive(
556 Clone,
557 Copy,
558 Debug,
559 Display,
560 AsRefStr,
561 EnumString,
562 FromRepr,
563 PartialEq,
564 Eq,
565 Hash,
566 Serialize,
567 Deserialize,
568)]
569#[cfg_attr(
570 feature = "python",
571 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
572)]
573#[serde(rename_all = "lowercase")]
574#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
575pub enum KrakenSpotTrigger {
576 Last,
578 Index,
580}
581
582#[derive(
584 Clone,
585 Copy,
586 Debug,
587 Display,
588 AsRefStr,
589 EnumString,
590 FromRepr,
591 PartialEq,
592 Eq,
593 Hash,
594 Serialize,
595 Deserialize,
596)]
597#[cfg_attr(
598 feature = "python",
599 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
600)]
601#[serde(rename_all = "camelCase")]
602#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
603pub enum KrakenFillType {
604 Maker,
605 Taker,
606 Liquidation,
607 PartialLiquidation,
608 Assignor,
609 Assignee,
610 TakerAfterEdit,
611 UnwindBankrupt,
612 UnwindCounterparty,
613}
614
615#[derive(
617 Clone,
618 Copy,
619 Debug,
620 Display,
621 AsRefStr,
622 EnumString,
623 FromRepr,
624 PartialEq,
625 Eq,
626 Hash,
627 Serialize,
628 Deserialize,
629)]
630#[cfg_attr(
631 feature = "python",
632 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
633)]
634#[serde(rename_all = "lowercase")]
635#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
636pub enum KrakenApiResult {
637 Success,
638 Error,
639}
640
641#[derive(
643 Clone,
644 Copy,
645 Debug,
646 Display,
647 AsRefStr,
648 EnumString,
649 FromRepr,
650 PartialEq,
651 Eq,
652 Hash,
653 Serialize,
654 Deserialize,
655)]
656#[cfg_attr(
657 feature = "python",
658 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
659)]
660#[serde(rename_all = "snake_case")]
661#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
662pub enum KrakenInstrumentType {
663 FuturesInverse,
665 FlexibleFutures,
667}
668
669#[derive(
671 Clone,
672 Copy,
673 Debug,
674 Display,
675 AsRefStr,
676 EnumString,
677 FromRepr,
678 PartialEq,
679 Eq,
680 Hash,
681 Serialize,
682 Deserialize,
683)]
684#[cfg_attr(
685 feature = "python",
686 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
687)]
688#[serde(rename_all = "camelCase")]
689#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
690pub enum KrakenSendStatus {
691 Placed,
693 Cancelled,
695 Edited,
697 NotFound,
699 NoOrdersToCancel,
707 InsufficientAvailableFunds,
709 InvalidOrderType,
711 InvalidSize,
713 WouldCauseLiquidation,
715 PostWouldExecute,
717 IocWouldNotExecute,
723 ReduceOnlyWouldIncreasePosition,
725}
726
727#[derive(
729 Clone,
730 Copy,
731 Debug,
732 Display,
733 AsRefStr,
734 EnumString,
735 FromRepr,
736 PartialEq,
737 Eq,
738 Hash,
739 Serialize,
740 Deserialize,
741)]
742#[cfg_attr(
743 feature = "python",
744 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
745)]
746#[serde(rename_all = "snake_case")]
747#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
748pub enum KrakenTriggerSide {
749 #[serde(rename = "trigger_above")]
751 #[strum(serialize = "trigger_above")]
752 TriggerAbove,
753 #[serde(rename = "trigger_below")]
755 #[strum(serialize = "trigger_below")]
756 TriggerBelow,
757 #[serde(rename = "unknown")]
759 Unknown,
760}
761
762impl From<KrakenOrderSide> for OrderSide {
763 fn from(value: KrakenOrderSide) -> Self {
764 match value {
765 KrakenOrderSide::Buy => Self::Buy,
766 KrakenOrderSide::Sell => Self::Sell,
767 }
768 }
769}
770
771impl From<KrakenFillType> for LiquiditySide {
772 fn from(value: KrakenFillType) -> Self {
773 match value {
774 KrakenFillType::Maker => Self::Maker,
775 KrakenFillType::Taker | KrakenFillType::TakerAfterEdit => Self::Taker,
776 KrakenFillType::Liquidation
777 | KrakenFillType::PartialLiquidation
778 | KrakenFillType::Assignor
779 | KrakenFillType::Assignee
780 | KrakenFillType::UnwindBankrupt
781 | KrakenFillType::UnwindCounterparty => Self::NoLiquiditySide,
782 }
783 }
784}
785
786impl From<KrakenOrderType> for OrderType {
787 fn from(value: KrakenOrderType) -> Self {
793 match value {
794 KrakenOrderType::Market => Self::Market,
795 KrakenOrderType::Limit => Self::Limit,
796 KrakenOrderType::StopLoss => Self::StopMarket,
797 KrakenOrderType::TakeProfit => Self::MarketIfTouched,
798 KrakenOrderType::StopLossLimit => Self::StopLimit,
799 KrakenOrderType::TakeProfitLimit => Self::LimitIfTouched,
800 KrakenOrderType::TrailingStop => Self::StopMarket,
801 KrakenOrderType::TrailingStopLimit => Self::StopLimit,
802 KrakenOrderType::SettlePosition => Self::Market,
803 }
804 }
805}
806
807impl From<KrakenOrderStatus> for OrderStatus {
808 fn from(value: KrakenOrderStatus) -> Self {
809 match value {
810 KrakenOrderStatus::Pending => Self::Initialized,
811 KrakenOrderStatus::Open => Self::Accepted,
812 KrakenOrderStatus::Closed => Self::Filled,
813 KrakenOrderStatus::Canceled => Self::Canceled,
814 KrakenOrderStatus::Expired => Self::Expired,
815 }
816 }
817}
818
819impl From<KrakenFuturesOrderType> for OrderType {
820 fn from(value: KrakenFuturesOrderType) -> Self {
821 match value {
822 KrakenFuturesOrderType::Limit
823 | KrakenFuturesOrderType::Ioc
824 | KrakenFuturesOrderType::Post => Self::Limit,
825 KrakenFuturesOrderType::Market => Self::Market,
826 KrakenFuturesOrderType::Stop | KrakenFuturesOrderType::StopLower => Self::StopMarket,
827 KrakenFuturesOrderType::TakeProfit => Self::MarketIfTouched,
828 KrakenFuturesOrderType::StopLoss => Self::StopMarket,
829 KrakenFuturesOrderType::Unknown => {
830 log::warn!(
831 "KrakenFuturesOrderType::Unknown received from venue, defaulting to Market"
832 );
833 Self::Market
834 }
835 }
836 }
837}
838
839impl From<OrderSide> for KrakenOrderSide {
840 fn from(value: OrderSide) -> Self {
841 match value {
842 OrderSide::Buy => Self::Buy,
843 OrderSide::Sell => Self::Sell,
844 }
845 }
846}
847
848impl From<KrakenFuturesOrderStatus> for OrderStatus {
849 fn from(value: KrakenFuturesOrderStatus) -> Self {
850 match value {
851 KrakenFuturesOrderStatus::Untouched => Self::Accepted,
852 KrakenFuturesOrderStatus::PartiallyFilled => Self::PartiallyFilled,
853 KrakenFuturesOrderStatus::Filled => Self::Filled,
854 KrakenFuturesOrderStatus::Cancelled => Self::Canceled,
855 KrakenFuturesOrderStatus::Expired => Self::Expired,
856 }
857 }
858}
859
860impl From<KrakenPairStatus> for MarketStatusAction {
861 fn from(value: KrakenPairStatus) -> Self {
862 match value {
863 KrakenPairStatus::Online => Self::Trading,
864 KrakenPairStatus::CancelOnly => Self::Halt,
865 KrakenPairStatus::PostOnly => Self::Pause,
866 KrakenPairStatus::LimitOnly => Self::Pause,
867 KrakenPairStatus::ReduceOnly => Self::Pause,
868 }
869 }
870}
871
872#[must_use]
883pub fn product_type_from_symbol(symbol: &str) -> KrakenProductType {
884 if symbol.starts_with("PI_")
885 || symbol.starts_with("PF_")
886 || symbol.starts_with("PV_")
887 || symbol.starts_with("FI_")
888 || symbol.starts_with("FF_")
889 {
890 KrakenProductType::Futures
891 } else {
892 KrakenProductType::Spot
893 }
894}
895
896#[cfg(test)]
897mod tests {
898 use nautilus_model::enums::{LiquiditySide, MarketStatusAction, OrderType};
899 use rstest::rstest;
900
901 use super::*;
902
903 #[rstest]
904 #[case::online(KrakenPairStatus::Online, MarketStatusAction::Trading)]
905 #[case::cancel_only(KrakenPairStatus::CancelOnly, MarketStatusAction::Halt)]
906 #[case::post_only(KrakenPairStatus::PostOnly, MarketStatusAction::Pause)]
907 #[case::limit_only(KrakenPairStatus::LimitOnly, MarketStatusAction::Pause)]
908 #[case::reduce_only(KrakenPairStatus::ReduceOnly, MarketStatusAction::Pause)]
909 fn test_pair_status_to_market_status_action(
910 #[case] input: KrakenPairStatus,
911 #[case] expected: MarketStatusAction,
912 ) {
913 assert_eq!(MarketStatusAction::from(input), expected);
914 }
915
916 #[rstest]
917 #[case::trailing_stop(KrakenOrderType::TrailingStop, OrderType::StopMarket)]
918 #[case::trailing_stop_limit(KrakenOrderType::TrailingStopLimit, OrderType::StopLimit)]
919 fn test_trailing_stop_order_type_mapping(
920 #[case] input: KrakenOrderType,
921 #[case] expected: OrderType,
922 ) {
923 assert_eq!(OrderType::from(input), expected);
924 }
925
926 #[rstest]
927 #[case::maker(KrakenFillType::Maker, LiquiditySide::Maker)]
928 #[case::taker(KrakenFillType::Taker, LiquiditySide::Taker)]
929 #[case::liquidation(KrakenFillType::Liquidation, LiquiditySide::NoLiquiditySide)]
930 #[case::partial_liquidation(KrakenFillType::PartialLiquidation, LiquiditySide::NoLiquiditySide)]
931 #[case::assignor(KrakenFillType::Assignor, LiquiditySide::NoLiquiditySide)]
932 #[case::assignee(KrakenFillType::Assignee, LiquiditySide::NoLiquiditySide)]
933 #[case::taker_after_edit(KrakenFillType::TakerAfterEdit, LiquiditySide::Taker)]
934 #[case::unwind_bankrupt(KrakenFillType::UnwindBankrupt, LiquiditySide::NoLiquiditySide)]
935 #[case::unwind_counterparty(KrakenFillType::UnwindCounterparty, LiquiditySide::NoLiquiditySide)]
936 fn test_fill_type_to_liquidity_side(
937 #[case] fill_type: KrakenFillType,
938 #[case] expected: LiquiditySide,
939 ) {
940 assert_eq!(LiquiditySide::from(fill_type), expected);
941 }
942
943 #[rstest]
944 fn test_fill_type_deserialization() {
945 let json = include_str!("../../test_data/futures_fill_types.json");
946 let fill_types: Vec<KrakenFillType> = serde_json::from_str(json).unwrap();
947
948 assert_eq!(
949 fill_types,
950 vec![
951 KrakenFillType::Maker,
952 KrakenFillType::Taker,
953 KrakenFillType::Liquidation,
954 KrakenFillType::PartialLiquidation,
955 KrakenFillType::Assignor,
956 KrakenFillType::Assignee,
957 KrakenFillType::TakerAfterEdit,
958 KrakenFillType::UnwindBankrupt,
959 KrakenFillType::UnwindCounterparty,
960 ]
961 );
962 }
963
964 #[rstest]
965 fn test_time_in_force_deserializes_fok() {
966 let tif: KrakenTimeInForce = serde_json::from_str("\"FOK\"").unwrap();
969 assert_eq!(tif, KrakenTimeInForce::FillOrKill);
970 }
971
972 #[rstest]
973 #[case("\"placed\"", KrakenSendStatus::Placed)]
974 #[case("\"cancelled\"", KrakenSendStatus::Cancelled)]
975 #[case("\"edited\"", KrakenSendStatus::Edited)]
976 #[case("\"notFound\"", KrakenSendStatus::NotFound)]
977 #[case("\"noOrdersToCancel\"", KrakenSendStatus::NoOrdersToCancel)]
978 #[case(
979 "\"insufficientAvailableFunds\"",
980 KrakenSendStatus::InsufficientAvailableFunds
981 )]
982 #[case("\"invalidOrderType\"", KrakenSendStatus::InvalidOrderType)]
983 #[case("\"invalidSize\"", KrakenSendStatus::InvalidSize)]
984 #[case("\"wouldCauseLiquidation\"", KrakenSendStatus::WouldCauseLiquidation)]
985 #[case("\"postWouldExecute\"", KrakenSendStatus::PostWouldExecute)]
986 #[case("\"iocWouldNotExecute\"", KrakenSendStatus::IocWouldNotExecute)]
987 #[case(
988 "\"reduceOnlyWouldIncreasePosition\"",
989 KrakenSendStatus::ReduceOnlyWouldIncreasePosition
990 )]
991 fn test_send_status_deserialization(#[case] raw: &str, #[case] expected: KrakenSendStatus) {
992 let parsed: KrakenSendStatus = serde_json::from_str(raw).unwrap();
993 assert_eq!(parsed, expected);
994 }
995
996 #[rstest]
997 #[case("\"last\"", KrakenTriggerSignal::Last)]
998 #[case("\"last_price\"", KrakenTriggerSignal::Last)]
999 #[case("\"mark\"", KrakenTriggerSignal::Mark)]
1000 #[case("\"mark_price\"", KrakenTriggerSignal::Mark)]
1001 #[case("\"spot\"", KrakenTriggerSignal::Index)]
1002 #[case("\"spot_price\"", KrakenTriggerSignal::Index)]
1003 #[case("\"index\"", KrakenTriggerSignal::Index)]
1004 #[case("\"index_price\"", KrakenTriggerSignal::Index)]
1005 #[case("\"unknown\"", KrakenTriggerSignal::Unknown)]
1006 fn test_trigger_signal_deserialization(
1007 #[case] raw: &str,
1008 #[case] expected: KrakenTriggerSignal,
1009 ) {
1010 let parsed: KrakenTriggerSignal = serde_json::from_str(raw).unwrap();
1011 assert_eq!(parsed, expected);
1012 }
1013
1014 #[rstest]
1015 #[case("\"trigger_above\"", KrakenTriggerSide::TriggerAbove)]
1016 #[case("\"trigger_below\"", KrakenTriggerSide::TriggerBelow)]
1017 #[case("\"unknown\"", KrakenTriggerSide::Unknown)]
1018 fn test_trigger_side_deserialization(#[case] raw: &str, #[case] expected: KrakenTriggerSide) {
1019 let parsed: KrakenTriggerSide = serde_json::from_str(raw).unwrap();
1020 assert_eq!(parsed, expected);
1021 }
1022
1023 #[rstest]
1024 #[case("\"lmt\"", KrakenFuturesOrderType::Limit)]
1025 #[case("\"limit\"", KrakenFuturesOrderType::Limit)]
1026 #[case("\"ioc\"", KrakenFuturesOrderType::Ioc)]
1027 #[case("\"post\"", KrakenFuturesOrderType::Post)]
1028 #[case("\"mkt\"", KrakenFuturesOrderType::Market)]
1029 #[case("\"market\"", KrakenFuturesOrderType::Market)]
1030 #[case("\"stp\"", KrakenFuturesOrderType::Stop)]
1031 #[case("\"stop\"", KrakenFuturesOrderType::StopLower)]
1032 #[case("\"take_profit\"", KrakenFuturesOrderType::TakeProfit)]
1033 #[case("\"stop_loss\"", KrakenFuturesOrderType::StopLoss)]
1034 #[case("\"unknown\"", KrakenFuturesOrderType::Unknown)]
1035 fn test_futures_order_type_deserialization(
1036 #[case] raw: &str,
1037 #[case] expected: KrakenFuturesOrderType,
1038 ) {
1039 let parsed: KrakenFuturesOrderType = serde_json::from_str(raw).unwrap();
1040 assert_eq!(parsed, expected);
1041 }
1042
1043 #[rstest]
1044 fn test_order_metadata_enums_fail_loud_on_unmodeled_value() {
1045 assert!(serde_json::from_str::<KrakenFuturesOrderType>("\"iceberg\"").is_err());
1048 assert!(serde_json::from_str::<KrakenTriggerSignal>("\"vwap\"").is_err());
1049 assert!(serde_json::from_str::<KrakenTriggerSide>("\"sideways\"").is_err());
1050 }
1051
1052 #[rstest]
1053 fn test_futures_order_type_unknown_maps_to_market_default() {
1054 assert_eq!(
1055 OrderType::from(KrakenFuturesOrderType::Unknown),
1056 OrderType::Market,
1057 );
1058 }
1059
1060 #[rstest]
1061 #[case("\"PLACE\"", KrakenFuturesOrderEventType::Place)]
1062 #[case("\"FILL\"", KrakenFuturesOrderEventType::Fill)]
1063 #[case("\"EXECUTION\"", KrakenFuturesOrderEventType::Execution)]
1064 #[case("\"REJECT\"", KrakenFuturesOrderEventType::Reject)]
1065 #[case("\"CANCEL\"", KrakenFuturesOrderEventType::Cancel)]
1066 #[case("\"EDIT\"", KrakenFuturesOrderEventType::Edit)]
1067 #[case("\"EXPIRE\"", KrakenFuturesOrderEventType::Expire)]
1068 #[case("\"EXPIRED\"", KrakenFuturesOrderEventType::Expire)]
1069 fn test_futures_order_event_type_deserialization(
1070 #[case] raw: &str,
1071 #[case] expected: KrakenFuturesOrderEventType,
1072 ) {
1073 let parsed: KrakenFuturesOrderEventType = serde_json::from_str(raw).unwrap();
1074 assert_eq!(parsed, expected);
1075 }
1076}