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(
480 Clone,
481 Copy,
482 Debug,
483 Display,
484 AsRefStr,
485 EnumString,
486 FromRepr,
487 PartialEq,
488 Eq,
489 Hash,
490 Serialize,
491 Deserialize,
492)]
493#[cfg_attr(
494 feature = "python",
495 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
496)]
497#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
498pub enum KrakenTriggerSignal {
499 #[serde(rename = "last", alias = "last_price")]
500 Last,
501 #[serde(rename = "mark", alias = "mark_price")]
502 Mark,
503 #[serde(
504 rename = "spot",
505 alias = "spot_price",
506 alias = "index",
507 alias = "index_price"
508 )]
509 #[strum(
510 serialize = "spot",
511 serialize = "spot_price",
512 serialize = "index",
513 serialize = "index_price"
514 )]
515 Index,
516 #[serde(rename = "unknown")]
517 Unknown,
518}
519
520#[derive(
522 Clone,
523 Copy,
524 Debug,
525 Display,
526 AsRefStr,
527 EnumString,
528 FromRepr,
529 PartialEq,
530 Eq,
531 Hash,
532 Serialize,
533 Deserialize,
534)]
535#[cfg_attr(
536 feature = "python",
537 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
538)]
539#[serde(rename_all = "lowercase")]
540#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
541pub enum KrakenSpotTrigger {
542 Last,
544 Index,
546}
547
548#[derive(
550 Clone,
551 Copy,
552 Debug,
553 Display,
554 AsRefStr,
555 EnumString,
556 FromRepr,
557 PartialEq,
558 Eq,
559 Hash,
560 Serialize,
561 Deserialize,
562)]
563#[cfg_attr(
564 feature = "python",
565 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
566)]
567#[serde(rename_all = "camelCase")]
568#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
569pub enum KrakenFillType {
570 Maker,
571 Taker,
572 Liquidation,
573 PartialLiquidation,
574 Assignor,
575 Assignee,
576 TakerAfterEdit,
577 UnwindBankrupt,
578 UnwindCounterparty,
579}
580
581#[derive(
583 Clone,
584 Copy,
585 Debug,
586 Display,
587 AsRefStr,
588 EnumString,
589 FromRepr,
590 PartialEq,
591 Eq,
592 Hash,
593 Serialize,
594 Deserialize,
595)]
596#[cfg_attr(
597 feature = "python",
598 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
599)]
600#[serde(rename_all = "lowercase")]
601#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
602pub enum KrakenApiResult {
603 Success,
604 Error,
605}
606
607#[derive(
609 Clone,
610 Copy,
611 Debug,
612 Display,
613 AsRefStr,
614 EnumString,
615 FromRepr,
616 PartialEq,
617 Eq,
618 Hash,
619 Serialize,
620 Deserialize,
621)]
622#[cfg_attr(
623 feature = "python",
624 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
625)]
626#[serde(rename_all = "snake_case")]
627#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
628pub enum KrakenInstrumentType {
629 FuturesInverse,
631 FlexibleFutures,
633}
634
635#[derive(
637 Clone,
638 Copy,
639 Debug,
640 Display,
641 AsRefStr,
642 EnumString,
643 FromRepr,
644 PartialEq,
645 Eq,
646 Hash,
647 Serialize,
648 Deserialize,
649)]
650#[cfg_attr(
651 feature = "python",
652 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
653)]
654#[serde(rename_all = "camelCase")]
655#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
656pub enum KrakenSendStatus {
657 Placed,
659 Cancelled,
661 Edited,
663 NotFound,
665 NoOrdersToCancel,
673 InsufficientAvailableFunds,
675 InvalidOrderType,
677 InvalidSize,
679 WouldCauseLiquidation,
681 PostWouldExecute,
683 ReduceOnlyWouldIncreasePosition,
685}
686
687#[derive(
689 Clone,
690 Copy,
691 Debug,
692 Display,
693 AsRefStr,
694 EnumString,
695 FromRepr,
696 PartialEq,
697 Eq,
698 Hash,
699 Serialize,
700 Deserialize,
701)]
702#[cfg_attr(
703 feature = "python",
704 pyo3::pyclass(module = "nautilus_trader.adapters.kraken", eq, eq_int, from_py_object)
705)]
706#[serde(rename_all = "snake_case")]
707#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
708pub enum KrakenTriggerSide {
709 #[serde(rename = "trigger_above")]
711 #[strum(serialize = "trigger_above")]
712 TriggerAbove,
713 #[serde(rename = "trigger_below")]
715 #[strum(serialize = "trigger_below")]
716 TriggerBelow,
717 #[serde(rename = "unknown")]
719 Unknown,
720}
721
722impl From<KrakenOrderSide> for OrderSide {
723 fn from(value: KrakenOrderSide) -> Self {
724 match value {
725 KrakenOrderSide::Buy => Self::Buy,
726 KrakenOrderSide::Sell => Self::Sell,
727 }
728 }
729}
730
731impl From<KrakenFillType> for LiquiditySide {
732 fn from(value: KrakenFillType) -> Self {
733 match value {
734 KrakenFillType::Maker => Self::Maker,
735 KrakenFillType::Taker | KrakenFillType::TakerAfterEdit => Self::Taker,
736 KrakenFillType::Liquidation
737 | KrakenFillType::PartialLiquidation
738 | KrakenFillType::Assignor
739 | KrakenFillType::Assignee
740 | KrakenFillType::UnwindBankrupt
741 | KrakenFillType::UnwindCounterparty => Self::NoLiquiditySide,
742 }
743 }
744}
745
746impl From<KrakenOrderType> for OrderType {
747 fn from(value: KrakenOrderType) -> Self {
753 match value {
754 KrakenOrderType::Market => Self::Market,
755 KrakenOrderType::Limit => Self::Limit,
756 KrakenOrderType::StopLoss => Self::StopMarket,
757 KrakenOrderType::TakeProfit => Self::MarketIfTouched,
758 KrakenOrderType::StopLossLimit => Self::StopLimit,
759 KrakenOrderType::TakeProfitLimit => Self::LimitIfTouched,
760 KrakenOrderType::TrailingStop => Self::StopMarket,
761 KrakenOrderType::TrailingStopLimit => Self::StopLimit,
762 KrakenOrderType::SettlePosition => Self::Market,
763 }
764 }
765}
766
767impl From<KrakenOrderStatus> for OrderStatus {
768 fn from(value: KrakenOrderStatus) -> Self {
769 match value {
770 KrakenOrderStatus::Pending => Self::Initialized,
771 KrakenOrderStatus::Open => Self::Accepted,
772 KrakenOrderStatus::Closed => Self::Filled,
773 KrakenOrderStatus::Canceled => Self::Canceled,
774 KrakenOrderStatus::Expired => Self::Expired,
775 }
776 }
777}
778
779impl From<KrakenFuturesOrderType> for OrderType {
780 fn from(value: KrakenFuturesOrderType) -> Self {
781 match value {
782 KrakenFuturesOrderType::Limit
783 | KrakenFuturesOrderType::Ioc
784 | KrakenFuturesOrderType::Post => Self::Limit,
785 KrakenFuturesOrderType::Market => Self::Market,
786 KrakenFuturesOrderType::Stop | KrakenFuturesOrderType::StopLower => Self::StopMarket,
787 KrakenFuturesOrderType::TakeProfit => Self::MarketIfTouched,
788 KrakenFuturesOrderType::StopLoss => Self::StopMarket,
789 KrakenFuturesOrderType::Unknown => {
790 log::warn!(
791 "KrakenFuturesOrderType::Unknown received from venue, defaulting to Market"
792 );
793 Self::Market
794 }
795 }
796 }
797}
798
799impl From<OrderSide> for KrakenOrderSide {
800 fn from(value: OrderSide) -> Self {
801 match value {
802 OrderSide::Buy => Self::Buy,
803 OrderSide::Sell => Self::Sell,
804 }
805 }
806}
807
808impl From<KrakenFuturesOrderStatus> for OrderStatus {
809 fn from(value: KrakenFuturesOrderStatus) -> Self {
810 match value {
811 KrakenFuturesOrderStatus::Untouched => Self::Accepted,
812 KrakenFuturesOrderStatus::PartiallyFilled => Self::PartiallyFilled,
813 KrakenFuturesOrderStatus::Filled => Self::Filled,
814 KrakenFuturesOrderStatus::Cancelled => Self::Canceled,
815 KrakenFuturesOrderStatus::Expired => Self::Expired,
816 }
817 }
818}
819
820impl From<KrakenPairStatus> for MarketStatusAction {
821 fn from(value: KrakenPairStatus) -> Self {
822 match value {
823 KrakenPairStatus::Online => Self::Trading,
824 KrakenPairStatus::CancelOnly => Self::Halt,
825 KrakenPairStatus::PostOnly => Self::Pause,
826 KrakenPairStatus::LimitOnly => Self::Pause,
827 KrakenPairStatus::ReduceOnly => Self::Pause,
828 }
829 }
830}
831
832#[must_use]
843pub fn product_type_from_symbol(symbol: &str) -> KrakenProductType {
844 if symbol.starts_with("PI_")
845 || symbol.starts_with("PF_")
846 || symbol.starts_with("PV_")
847 || symbol.starts_with("FI_")
848 || symbol.starts_with("FF_")
849 {
850 KrakenProductType::Futures
851 } else {
852 KrakenProductType::Spot
853 }
854}
855
856#[cfg(test)]
857mod tests {
858 use nautilus_model::enums::{LiquiditySide, MarketStatusAction, OrderType};
859 use rstest::rstest;
860
861 use super::*;
862
863 #[rstest]
864 #[case::online(KrakenPairStatus::Online, MarketStatusAction::Trading)]
865 #[case::cancel_only(KrakenPairStatus::CancelOnly, MarketStatusAction::Halt)]
866 #[case::post_only(KrakenPairStatus::PostOnly, MarketStatusAction::Pause)]
867 #[case::limit_only(KrakenPairStatus::LimitOnly, MarketStatusAction::Pause)]
868 #[case::reduce_only(KrakenPairStatus::ReduceOnly, MarketStatusAction::Pause)]
869 fn test_pair_status_to_market_status_action(
870 #[case] input: KrakenPairStatus,
871 #[case] expected: MarketStatusAction,
872 ) {
873 assert_eq!(MarketStatusAction::from(input), expected);
874 }
875
876 #[rstest]
877 #[case::trailing_stop(KrakenOrderType::TrailingStop, OrderType::StopMarket)]
878 #[case::trailing_stop_limit(KrakenOrderType::TrailingStopLimit, OrderType::StopLimit)]
879 fn test_trailing_stop_order_type_mapping(
880 #[case] input: KrakenOrderType,
881 #[case] expected: OrderType,
882 ) {
883 assert_eq!(OrderType::from(input), expected);
884 }
885
886 #[rstest]
887 #[case::maker(KrakenFillType::Maker, LiquiditySide::Maker)]
888 #[case::taker(KrakenFillType::Taker, LiquiditySide::Taker)]
889 #[case::liquidation(KrakenFillType::Liquidation, LiquiditySide::NoLiquiditySide)]
890 #[case::partial_liquidation(KrakenFillType::PartialLiquidation, LiquiditySide::NoLiquiditySide)]
891 #[case::assignor(KrakenFillType::Assignor, LiquiditySide::NoLiquiditySide)]
892 #[case::assignee(KrakenFillType::Assignee, LiquiditySide::NoLiquiditySide)]
893 #[case::taker_after_edit(KrakenFillType::TakerAfterEdit, LiquiditySide::Taker)]
894 #[case::unwind_bankrupt(KrakenFillType::UnwindBankrupt, LiquiditySide::NoLiquiditySide)]
895 #[case::unwind_counterparty(KrakenFillType::UnwindCounterparty, LiquiditySide::NoLiquiditySide)]
896 fn test_fill_type_to_liquidity_side(
897 #[case] fill_type: KrakenFillType,
898 #[case] expected: LiquiditySide,
899 ) {
900 assert_eq!(LiquiditySide::from(fill_type), expected);
901 }
902
903 #[rstest]
904 fn test_fill_type_deserialization() {
905 let json = include_str!("../../test_data/futures_fill_types.json");
906 let fill_types: Vec<KrakenFillType> = serde_json::from_str(json).unwrap();
907
908 assert_eq!(
909 fill_types,
910 vec![
911 KrakenFillType::Maker,
912 KrakenFillType::Taker,
913 KrakenFillType::Liquidation,
914 KrakenFillType::PartialLiquidation,
915 KrakenFillType::Assignor,
916 KrakenFillType::Assignee,
917 KrakenFillType::TakerAfterEdit,
918 KrakenFillType::UnwindBankrupt,
919 KrakenFillType::UnwindCounterparty,
920 ]
921 );
922 }
923
924 #[rstest]
925 fn test_time_in_force_deserializes_fok() {
926 let tif: KrakenTimeInForce = serde_json::from_str("\"FOK\"").unwrap();
929 assert_eq!(tif, KrakenTimeInForce::FillOrKill);
930 }
931
932 #[rstest]
933 #[case("\"placed\"", KrakenSendStatus::Placed)]
934 #[case("\"cancelled\"", KrakenSendStatus::Cancelled)]
935 #[case("\"edited\"", KrakenSendStatus::Edited)]
936 #[case("\"notFound\"", KrakenSendStatus::NotFound)]
937 #[case("\"noOrdersToCancel\"", KrakenSendStatus::NoOrdersToCancel)]
938 #[case(
939 "\"insufficientAvailableFunds\"",
940 KrakenSendStatus::InsufficientAvailableFunds
941 )]
942 #[case("\"invalidOrderType\"", KrakenSendStatus::InvalidOrderType)]
943 #[case("\"invalidSize\"", KrakenSendStatus::InvalidSize)]
944 #[case("\"wouldCauseLiquidation\"", KrakenSendStatus::WouldCauseLiquidation)]
945 #[case("\"postWouldExecute\"", KrakenSendStatus::PostWouldExecute)]
946 #[case(
947 "\"reduceOnlyWouldIncreasePosition\"",
948 KrakenSendStatus::ReduceOnlyWouldIncreasePosition
949 )]
950 fn test_send_status_deserialization(#[case] raw: &str, #[case] expected: KrakenSendStatus) {
951 let parsed: KrakenSendStatus = serde_json::from_str(raw).unwrap();
952 assert_eq!(parsed, expected);
953 }
954
955 #[rstest]
956 #[case("\"last\"", KrakenTriggerSignal::Last)]
957 #[case("\"last_price\"", KrakenTriggerSignal::Last)]
958 #[case("\"mark\"", KrakenTriggerSignal::Mark)]
959 #[case("\"mark_price\"", KrakenTriggerSignal::Mark)]
960 #[case("\"spot\"", KrakenTriggerSignal::Index)]
961 #[case("\"spot_price\"", KrakenTriggerSignal::Index)]
962 #[case("\"index\"", KrakenTriggerSignal::Index)]
963 #[case("\"index_price\"", KrakenTriggerSignal::Index)]
964 #[case("\"unknown\"", KrakenTriggerSignal::Unknown)]
965 fn test_trigger_signal_deserialization(
966 #[case] raw: &str,
967 #[case] expected: KrakenTriggerSignal,
968 ) {
969 let parsed: KrakenTriggerSignal = serde_json::from_str(raw).unwrap();
970 assert_eq!(parsed, expected);
971 }
972
973 #[rstest]
974 #[case("\"trigger_above\"", KrakenTriggerSide::TriggerAbove)]
975 #[case("\"trigger_below\"", KrakenTriggerSide::TriggerBelow)]
976 #[case("\"unknown\"", KrakenTriggerSide::Unknown)]
977 fn test_trigger_side_deserialization(#[case] raw: &str, #[case] expected: KrakenTriggerSide) {
978 let parsed: KrakenTriggerSide = serde_json::from_str(raw).unwrap();
979 assert_eq!(parsed, expected);
980 }
981
982 #[rstest]
983 #[case("\"lmt\"", KrakenFuturesOrderType::Limit)]
984 #[case("\"limit\"", KrakenFuturesOrderType::Limit)]
985 #[case("\"ioc\"", KrakenFuturesOrderType::Ioc)]
986 #[case("\"post\"", KrakenFuturesOrderType::Post)]
987 #[case("\"mkt\"", KrakenFuturesOrderType::Market)]
988 #[case("\"market\"", KrakenFuturesOrderType::Market)]
989 #[case("\"stp\"", KrakenFuturesOrderType::Stop)]
990 #[case("\"stop\"", KrakenFuturesOrderType::StopLower)]
991 #[case("\"take_profit\"", KrakenFuturesOrderType::TakeProfit)]
992 #[case("\"stop_loss\"", KrakenFuturesOrderType::StopLoss)]
993 #[case("\"unknown\"", KrakenFuturesOrderType::Unknown)]
994 fn test_futures_order_type_deserialization(
995 #[case] raw: &str,
996 #[case] expected: KrakenFuturesOrderType,
997 ) {
998 let parsed: KrakenFuturesOrderType = serde_json::from_str(raw).unwrap();
999 assert_eq!(parsed, expected);
1000 }
1001
1002 #[rstest]
1003 fn test_order_metadata_enums_fail_loud_on_unmodeled_value() {
1004 assert!(serde_json::from_str::<KrakenFuturesOrderType>("\"iceberg\"").is_err());
1007 assert!(serde_json::from_str::<KrakenTriggerSignal>("\"vwap\"").is_err());
1008 assert!(serde_json::from_str::<KrakenTriggerSide>("\"sideways\"").is_err());
1009 }
1010
1011 #[rstest]
1012 fn test_futures_order_type_unknown_maps_to_market_default() {
1013 assert_eq!(
1014 OrderType::from(KrakenFuturesOrderType::Unknown),
1015 OrderType::Market,
1016 );
1017 }
1018
1019 #[rstest]
1020 #[case("\"PLACE\"", KrakenFuturesOrderEventType::Place)]
1021 #[case("\"FILL\"", KrakenFuturesOrderEventType::Fill)]
1022 #[case("\"EXECUTION\"", KrakenFuturesOrderEventType::Execution)]
1023 #[case("\"REJECT\"", KrakenFuturesOrderEventType::Reject)]
1024 #[case("\"CANCEL\"", KrakenFuturesOrderEventType::Cancel)]
1025 #[case("\"EDIT\"", KrakenFuturesOrderEventType::Edit)]
1026 #[case("\"EXPIRE\"", KrakenFuturesOrderEventType::Expire)]
1027 #[case("\"EXPIRED\"", KrakenFuturesOrderEventType::Expire)]
1028 fn test_futures_order_event_type_deserialization(
1029 #[case] raw: &str,
1030 #[case] expected: KrakenFuturesOrderEventType,
1031 ) {
1032 let parsed: KrakenFuturesOrderEventType = serde_json::from_str(raw).unwrap();
1033 assert_eq!(parsed, expected);
1034 }
1035}