Skip to main content

nautilus_kraken/common/
enums.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Enumerations that model Kraken string/int enums across HTTP and WebSocket payloads.
17
18use nautilus_model::enums::{MarketStatusAction, OrderSide, OrderStatus, OrderType};
19use serde::{Deserialize, Serialize};
20use strum::{AsRefStr, Display, EnumString, FromRepr};
21
22/// Kraken API environment (mainnet or demo).
23#[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.core.nautilus_pyo3.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.kraken")
53)]
54#[serde(rename_all = "lowercase")]
55#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
56pub enum KrakenEnvironment {
57    #[default]
58    Mainnet,
59    Demo,
60}
61
62/// Kraken product type (spot or futures).
63#[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.core.nautilus_pyo3.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.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/// Kraken spot order type.
103#[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(
120        module = "nautilus_trader.core.nautilus_pyo3.kraken",
121        eq,
122        eq_int,
123        from_py_object
124    )
125)]
126#[cfg_attr(
127    feature = "python",
128    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
129)]
130#[serde(rename_all = "lowercase")]
131#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
132pub enum KrakenOrderType {
133    Market,
134    Limit,
135    #[serde(rename = "stop-loss")]
136    #[strum(serialize = "stop-loss")]
137    StopLoss,
138    #[serde(rename = "take-profit")]
139    #[strum(serialize = "take-profit")]
140    TakeProfit,
141    #[serde(rename = "stop-loss-limit")]
142    #[strum(serialize = "stop-loss-limit")]
143    StopLossLimit,
144    #[serde(rename = "take-profit-limit")]
145    #[strum(serialize = "take-profit-limit")]
146    TakeProfitLimit,
147    #[serde(rename = "trailing-stop")]
148    #[strum(serialize = "trailing-stop")]
149    TrailingStop,
150    #[serde(rename = "trailing-stop-limit")]
151    #[strum(serialize = "trailing-stop-limit")]
152    TrailingStopLimit,
153    #[serde(rename = "settle-position")]
154    #[strum(serialize = "settle-position")]
155    SettlePosition,
156}
157
158/// Kraken order side (buy or sell).
159#[derive(
160    Clone,
161    Copy,
162    Debug,
163    Display,
164    AsRefStr,
165    EnumString,
166    FromRepr,
167    PartialEq,
168    Eq,
169    Hash,
170    Serialize,
171    Deserialize,
172)]
173#[cfg_attr(
174    feature = "python",
175    pyo3::pyclass(
176        module = "nautilus_trader.core.nautilus_pyo3.kraken",
177        eq,
178        eq_int,
179        from_py_object
180    )
181)]
182#[cfg_attr(
183    feature = "python",
184    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
185)]
186#[serde(rename_all = "lowercase")]
187#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
188pub enum KrakenOrderSide {
189    Buy,
190    Sell,
191}
192
193/// Kraken time-in-force for orders.
194#[derive(
195    Clone,
196    Copy,
197    Debug,
198    Display,
199    AsRefStr,
200    EnumString,
201    FromRepr,
202    PartialEq,
203    Eq,
204    Hash,
205    Serialize,
206    Deserialize,
207)]
208#[cfg_attr(
209    feature = "python",
210    pyo3::pyclass(
211        module = "nautilus_trader.core.nautilus_pyo3.kraken",
212        eq,
213        eq_int,
214        from_py_object
215    )
216)]
217#[cfg_attr(
218    feature = "python",
219    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
220)]
221#[serde(rename_all = "UPPERCASE")]
222#[strum(ascii_case_insensitive, serialize_all = "UPPERCASE")]
223pub enum KrakenTimeInForce {
224    #[serde(rename = "GTC")]
225    #[strum(serialize = "GTC")]
226    GoodTilCancelled,
227    #[serde(rename = "IOC")]
228    #[strum(serialize = "IOC")]
229    ImmediateOrCancel,
230    #[serde(rename = "GTD")]
231    #[strum(serialize = "GTD")]
232    GoodTilDate,
233}
234
235/// Kraken order status.
236#[derive(
237    Clone,
238    Copy,
239    Debug,
240    Display,
241    AsRefStr,
242    EnumString,
243    FromRepr,
244    PartialEq,
245    Eq,
246    Hash,
247    Serialize,
248    Deserialize,
249)]
250#[cfg_attr(
251    feature = "python",
252    pyo3::pyclass(
253        module = "nautilus_trader.core.nautilus_pyo3.kraken",
254        eq,
255        eq_int,
256        from_py_object
257    )
258)]
259#[cfg_attr(
260    feature = "python",
261    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
262)]
263#[serde(rename_all = "lowercase")]
264#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
265pub enum KrakenOrderStatus {
266    Pending,
267    Open,
268    Closed,
269    Canceled,
270    Expired,
271}
272
273/// Kraken position side (long or short).
274#[derive(
275    Clone,
276    Copy,
277    Debug,
278    Display,
279    AsRefStr,
280    EnumString,
281    FromRepr,
282    PartialEq,
283    Eq,
284    Hash,
285    Serialize,
286    Deserialize,
287)]
288#[cfg_attr(
289    feature = "python",
290    pyo3::pyclass(
291        module = "nautilus_trader.core.nautilus_pyo3.kraken",
292        eq,
293        eq_int,
294        from_py_object
295    )
296)]
297#[cfg_attr(
298    feature = "python",
299    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
300)]
301#[serde(rename_all = "lowercase")]
302#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
303pub enum KrakenPositionSide {
304    Long,
305    Short,
306}
307
308/// Kraken trading pair status.
309#[derive(
310    Clone,
311    Copy,
312    Debug,
313    Display,
314    AsRefStr,
315    EnumString,
316    FromRepr,
317    PartialEq,
318    Eq,
319    Hash,
320    Serialize,
321    Deserialize,
322)]
323#[cfg_attr(
324    feature = "python",
325    pyo3::pyclass(
326        module = "nautilus_trader.core.nautilus_pyo3.kraken",
327        eq,
328        eq_int,
329        from_py_object
330    )
331)]
332#[cfg_attr(
333    feature = "python",
334    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
335)]
336#[serde(rename_all = "snake_case")]
337#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
338pub enum KrakenPairStatus {
339    Online,
340    #[serde(rename = "cancel_only")]
341    #[strum(serialize = "cancel_only")]
342    CancelOnly,
343    #[serde(rename = "post_only")]
344    #[strum(serialize = "post_only")]
345    PostOnly,
346    #[serde(rename = "limit_only")]
347    #[strum(serialize = "limit_only")]
348    LimitOnly,
349    #[serde(rename = "reduce_only")]
350    #[strum(serialize = "reduce_only")]
351    ReduceOnly,
352}
353
354/// Kraken system status.
355#[derive(
356    Clone,
357    Copy,
358    Debug,
359    Display,
360    AsRefStr,
361    EnumString,
362    FromRepr,
363    PartialEq,
364    Eq,
365    Hash,
366    Serialize,
367    Deserialize,
368)]
369#[cfg_attr(
370    feature = "python",
371    pyo3::pyclass(
372        module = "nautilus_trader.core.nautilus_pyo3.kraken",
373        eq,
374        eq_int,
375        from_py_object
376    )
377)]
378#[cfg_attr(
379    feature = "python",
380    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
381)]
382#[serde(rename_all = "lowercase")]
383#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
384pub enum KrakenSystemStatus {
385    Online,
386    Maintenance,
387    #[serde(rename = "cancel_only")]
388    #[strum(serialize = "cancel_only")]
389    CancelOnly,
390    #[serde(rename = "post_only")]
391    #[strum(serialize = "post_only")]
392    PostOnly,
393}
394
395/// Kraken asset class.
396#[derive(
397    Clone,
398    Copy,
399    Debug,
400    Display,
401    AsRefStr,
402    EnumString,
403    FromRepr,
404    PartialEq,
405    Eq,
406    Hash,
407    Serialize,
408    Deserialize,
409)]
410#[cfg_attr(
411    feature = "python",
412    pyo3::pyclass(
413        module = "nautilus_trader.core.nautilus_pyo3.kraken",
414        eq,
415        eq_int,
416        from_py_object
417    )
418)]
419#[cfg_attr(
420    feature = "python",
421    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
422)]
423#[serde(rename_all = "lowercase")]
424#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
425pub enum KrakenAssetClass {
426    Currency,
427    #[serde(rename = "tokenized_asset")]
428    #[strum(serialize = "tokenized_asset")]
429    TokenizedAsset,
430}
431
432/// Kraken futures order type.
433#[derive(
434    Clone,
435    Copy,
436    Debug,
437    Display,
438    AsRefStr,
439    EnumString,
440    FromRepr,
441    PartialEq,
442    Eq,
443    Hash,
444    Serialize,
445    Deserialize,
446)]
447#[cfg_attr(
448    feature = "python",
449    pyo3::pyclass(
450        module = "nautilus_trader.core.nautilus_pyo3.kraken",
451        eq,
452        eq_int,
453        from_py_object
454    )
455)]
456#[cfg_attr(
457    feature = "python",
458    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
459)]
460#[serde(rename_all = "lowercase")]
461#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
462pub enum KrakenFuturesOrderType {
463    #[serde(rename = "lmt", alias = "limit")]
464    #[strum(serialize = "lmt")]
465    Limit,
466    #[serde(rename = "ioc")]
467    #[strum(serialize = "ioc")]
468    Ioc,
469    #[serde(rename = "post")]
470    #[strum(serialize = "post")]
471    Post,
472    #[serde(rename = "mkt", alias = "market")]
473    #[strum(serialize = "mkt")]
474    Market,
475    #[serde(rename = "stp")]
476    #[strum(serialize = "stp")]
477    Stop,
478    #[serde(rename = "stop")]
479    #[strum(serialize = "stop")]
480    StopLower,
481    #[serde(rename = "take_profit")]
482    #[strum(serialize = "take_profit")]
483    TakeProfit,
484    #[serde(rename = "stop_loss")]
485    #[strum(serialize = "stop_loss")]
486    StopLoss,
487}
488
489/// Event types from Kraken Futures sendorder/editorder responses.
490#[derive(
491    Clone,
492    Copy,
493    Debug,
494    Display,
495    AsRefStr,
496    EnumString,
497    FromRepr,
498    PartialEq,
499    Eq,
500    Hash,
501    Serialize,
502    Deserialize,
503)]
504#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
505#[strum(ascii_case_insensitive, serialize_all = "SCREAMING_SNAKE_CASE")]
506pub enum KrakenFuturesOrderEventType {
507    /// Order was placed.
508    Place,
509    /// Legacy history endpoint fill event.
510    Fill,
511    /// Send-order execution event.
512    Execution,
513    /// Order was rejected.
514    Reject,
515    /// Order was cancelled.
516    Cancel,
517    /// Order was edited.
518    Edit,
519    /// Order expired.
520    #[serde(alias = "EXPIRED")]
521    #[strum(serialize = "EXPIRED")]
522    Expire,
523}
524
525/// Kraken futures order status.
526#[derive(
527    Clone,
528    Copy,
529    Debug,
530    Display,
531    AsRefStr,
532    EnumString,
533    FromRepr,
534    PartialEq,
535    Eq,
536    Hash,
537    Serialize,
538    Deserialize,
539)]
540#[cfg_attr(
541    feature = "python",
542    pyo3::pyclass(
543        module = "nautilus_trader.core.nautilus_pyo3.kraken",
544        eq,
545        eq_int,
546        from_py_object
547    )
548)]
549#[cfg_attr(
550    feature = "python",
551    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
552)]
553#[serde(rename_all = "camelCase")]
554#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
555pub enum KrakenFuturesOrderStatus {
556    Untouched,
557    PartiallyFilled,
558    Filled,
559    Cancelled,
560    Expired,
561}
562
563/// Kraken futures trigger signal type.
564#[derive(
565    Clone,
566    Copy,
567    Debug,
568    Display,
569    AsRefStr,
570    EnumString,
571    FromRepr,
572    PartialEq,
573    Eq,
574    Hash,
575    Serialize,
576    Deserialize,
577)]
578#[cfg_attr(
579    feature = "python",
580    pyo3::pyclass(
581        module = "nautilus_trader.core.nautilus_pyo3.kraken",
582        eq,
583        eq_int,
584        from_py_object
585    )
586)]
587#[cfg_attr(
588    feature = "python",
589    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
590)]
591#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
592pub enum KrakenTriggerSignal {
593    #[serde(rename = "last", alias = "last_price")]
594    Last,
595    #[serde(rename = "mark", alias = "mark_price")]
596    Mark,
597    #[serde(
598        rename = "spot",
599        alias = "spot_price",
600        alias = "index",
601        alias = "index_price"
602    )]
603    #[strum(
604        serialize = "spot",
605        serialize = "spot_price",
606        serialize = "index",
607        serialize = "index_price"
608    )]
609    Index,
610}
611
612/// Trigger reference price for Kraken spot conditional orders.
613#[derive(
614    Clone,
615    Copy,
616    Debug,
617    Display,
618    AsRefStr,
619    EnumString,
620    FromRepr,
621    PartialEq,
622    Eq,
623    Hash,
624    Serialize,
625    Deserialize,
626)]
627#[cfg_attr(
628    feature = "python",
629    pyo3::pyclass(
630        module = "nautilus_trader.core.nautilus_pyo3.kraken",
631        eq,
632        eq_int,
633        from_py_object
634    )
635)]
636#[cfg_attr(
637    feature = "python",
638    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
639)]
640#[serde(rename_all = "lowercase")]
641#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
642pub enum KrakenSpotTrigger {
643    /// Last traded price in the order book.
644    Last,
645    /// Index price for the broader market.
646    Index,
647}
648
649/// Kraken fill type (maker or taker).
650#[derive(
651    Clone,
652    Copy,
653    Debug,
654    Display,
655    AsRefStr,
656    EnumString,
657    FromRepr,
658    PartialEq,
659    Eq,
660    Hash,
661    Serialize,
662    Deserialize,
663)]
664#[cfg_attr(
665    feature = "python",
666    pyo3::pyclass(
667        module = "nautilus_trader.core.nautilus_pyo3.kraken",
668        eq,
669        eq_int,
670        from_py_object
671    )
672)]
673#[cfg_attr(
674    feature = "python",
675    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
676)]
677#[serde(rename_all = "lowercase")]
678#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
679pub enum KrakenFillType {
680    Maker,
681    Taker,
682}
683
684/// Kraken API result status.
685#[derive(
686    Clone,
687    Copy,
688    Debug,
689    Display,
690    AsRefStr,
691    EnumString,
692    FromRepr,
693    PartialEq,
694    Eq,
695    Hash,
696    Serialize,
697    Deserialize,
698)]
699#[cfg_attr(
700    feature = "python",
701    pyo3::pyclass(
702        module = "nautilus_trader.core.nautilus_pyo3.kraken",
703        eq,
704        eq_int,
705        from_py_object
706    )
707)]
708#[cfg_attr(
709    feature = "python",
710    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
711)]
712#[serde(rename_all = "lowercase")]
713#[strum(ascii_case_insensitive, serialize_all = "lowercase")]
714pub enum KrakenApiResult {
715    Success,
716    Error,
717}
718
719/// Kraken futures instrument type.
720#[derive(
721    Clone,
722    Copy,
723    Debug,
724    Display,
725    AsRefStr,
726    EnumString,
727    FromRepr,
728    PartialEq,
729    Eq,
730    Hash,
731    Serialize,
732    Deserialize,
733)]
734#[cfg_attr(
735    feature = "python",
736    pyo3::pyclass(
737        module = "nautilus_trader.core.nautilus_pyo3.kraken",
738        eq,
739        eq_int,
740        from_py_object
741    )
742)]
743#[cfg_attr(
744    feature = "python",
745    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
746)]
747#[serde(rename_all = "snake_case")]
748#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
749pub enum KrakenInstrumentType {
750    /// Inverse perpetual futures (e.g., PI_XBTUSD).
751    FuturesInverse,
752    /// Flexible/linear perpetual futures (e.g., PF_XBTUSD).
753    FlexibleFutures,
754}
755
756/// Kraken futures send order status.
757#[derive(
758    Clone,
759    Copy,
760    Debug,
761    Display,
762    AsRefStr,
763    EnumString,
764    FromRepr,
765    PartialEq,
766    Eq,
767    Hash,
768    Serialize,
769    Deserialize,
770)]
771#[cfg_attr(
772    feature = "python",
773    pyo3::pyclass(
774        module = "nautilus_trader.core.nautilus_pyo3.kraken",
775        eq,
776        eq_int,
777        from_py_object
778    )
779)]
780#[cfg_attr(
781    feature = "python",
782    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
783)]
784#[serde(rename_all = "camelCase")]
785#[strum(ascii_case_insensitive, serialize_all = "camelCase")]
786pub enum KrakenSendStatus {
787    /// Order was successfully placed.
788    Placed,
789    /// Order was cancelled.
790    Cancelled,
791    /// Order was edited.
792    Edited,
793    /// Order not found.
794    NotFound,
795    /// No orders matched the cancel-all request.
796    ///
797    /// Returned by the Kraken Futures `cancelallorders` endpoint as the
798    /// `cancelStatus.status` field. The accompanying `cancelledOrders` array
799    /// may still be populated for orders that were canceled in the same call,
800    /// so callers must inspect that array rather than treating this status
801    /// as an error.
802    NoOrdersToCancel,
803    /// Insufficient available funds.
804    InsufficientAvailableFunds,
805    /// Invalid order type.
806    InvalidOrderType,
807    /// Invalid size.
808    InvalidSize,
809    /// Would cause liquidation.
810    WouldCauseLiquidation,
811    /// Post-only order would have crossed.
812    PostWouldExecute,
813    /// Reduce-only order would increase position.
814    ReduceOnlyWouldIncreasePosition,
815}
816
817/// Kraken futures trigger side for conditional orders.
818#[derive(
819    Clone,
820    Copy,
821    Debug,
822    Display,
823    AsRefStr,
824    EnumString,
825    FromRepr,
826    PartialEq,
827    Eq,
828    Hash,
829    Serialize,
830    Deserialize,
831)]
832#[cfg_attr(
833    feature = "python",
834    pyo3::pyclass(
835        module = "nautilus_trader.core.nautilus_pyo3.kraken",
836        eq,
837        eq_int,
838        from_py_object
839    )
840)]
841#[cfg_attr(
842    feature = "python",
843    pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.kraken")
844)]
845#[serde(rename_all = "snake_case")]
846#[strum(ascii_case_insensitive, serialize_all = "snake_case")]
847pub enum KrakenTriggerSide {
848    /// Trigger when price goes above the trigger price.
849    #[serde(rename = "trigger_above")]
850    #[strum(serialize = "trigger_above")]
851    TriggerAbove,
852    /// Trigger when price goes below the trigger price.
853    #[serde(rename = "trigger_below")]
854    #[strum(serialize = "trigger_below")]
855    TriggerBelow,
856}
857
858impl From<KrakenOrderSide> for OrderSide {
859    fn from(value: KrakenOrderSide) -> Self {
860        match value {
861            KrakenOrderSide::Buy => Self::Buy,
862            KrakenOrderSide::Sell => Self::Sell,
863        }
864    }
865}
866
867impl From<KrakenOrderType> for OrderType {
868    /// Maps Kraken order types to Nautilus order types for reconciliation.
869    ///
870    /// Trailing stops map to their non-trailing equivalents because
871    /// Kraken reports lack the offset fields required to reconstruct
872    /// a trailing order during reconciliation.
873    fn from(value: KrakenOrderType) -> Self {
874        match value {
875            KrakenOrderType::Market => Self::Market,
876            KrakenOrderType::Limit => Self::Limit,
877            KrakenOrderType::StopLoss => Self::StopMarket,
878            KrakenOrderType::TakeProfit => Self::MarketIfTouched,
879            KrakenOrderType::StopLossLimit => Self::StopLimit,
880            KrakenOrderType::TakeProfitLimit => Self::LimitIfTouched,
881            KrakenOrderType::TrailingStop => Self::StopMarket,
882            KrakenOrderType::TrailingStopLimit => Self::StopLimit,
883            KrakenOrderType::SettlePosition => Self::Market,
884        }
885    }
886}
887
888impl From<KrakenOrderStatus> for OrderStatus {
889    fn from(value: KrakenOrderStatus) -> Self {
890        match value {
891            KrakenOrderStatus::Pending => Self::Initialized,
892            KrakenOrderStatus::Open => Self::Accepted,
893            KrakenOrderStatus::Closed => Self::Filled,
894            KrakenOrderStatus::Canceled => Self::Canceled,
895            KrakenOrderStatus::Expired => Self::Expired,
896        }
897    }
898}
899
900impl From<KrakenFuturesOrderType> for OrderType {
901    fn from(value: KrakenFuturesOrderType) -> Self {
902        match value {
903            KrakenFuturesOrderType::Limit
904            | KrakenFuturesOrderType::Ioc
905            | KrakenFuturesOrderType::Post => Self::Limit,
906            KrakenFuturesOrderType::Market => Self::Market,
907            KrakenFuturesOrderType::Stop | KrakenFuturesOrderType::StopLower => Self::StopMarket,
908            KrakenFuturesOrderType::TakeProfit => Self::MarketIfTouched,
909            KrakenFuturesOrderType::StopLoss => Self::StopMarket,
910        }
911    }
912}
913
914impl TryFrom<OrderSide> for KrakenOrderSide {
915    type Error = &'static str;
916
917    fn try_from(value: OrderSide) -> Result<Self, Self::Error> {
918        match value {
919            OrderSide::Buy => Ok(Self::Buy),
920            OrderSide::Sell => Ok(Self::Sell),
921            OrderSide::NoOrderSide => Err("Cannot convert NoOrderSide to KrakenOrderSide"),
922        }
923    }
924}
925
926impl From<KrakenFuturesOrderStatus> for OrderStatus {
927    fn from(value: KrakenFuturesOrderStatus) -> Self {
928        match value {
929            KrakenFuturesOrderStatus::Untouched => Self::Accepted,
930            KrakenFuturesOrderStatus::PartiallyFilled => Self::PartiallyFilled,
931            KrakenFuturesOrderStatus::Filled => Self::Filled,
932            KrakenFuturesOrderStatus::Cancelled => Self::Canceled,
933            KrakenFuturesOrderStatus::Expired => Self::Expired,
934        }
935    }
936}
937
938impl From<KrakenPairStatus> for MarketStatusAction {
939    fn from(value: KrakenPairStatus) -> Self {
940        match value {
941            KrakenPairStatus::Online => Self::Trading,
942            KrakenPairStatus::CancelOnly => Self::Halt,
943            KrakenPairStatus::PostOnly => Self::Pause,
944            KrakenPairStatus::LimitOnly => Self::Pause,
945            KrakenPairStatus::ReduceOnly => Self::Pause,
946        }
947    }
948}
949
950/// Determines the product type from a Kraken symbol.
951///
952/// Futures symbols have the following prefixes:
953/// - `PI_` - Perpetual Inverse futures (e.g., `PI_XBTUSD`)
954/// - `PF_` - Perpetual Fixed-margin futures (e.g., `PF_XBTUSD`)
955/// - `PV_` - Perpetual Vanilla futures (e.g., `PV_XRPXBT`)
956/// - `FI_` - Fixed maturity Inverse futures (e.g., `FI_XBTUSD_230929`)
957/// - `FF_` - Flex futures
958///
959/// All other symbols are considered spot.
960#[must_use]
961pub fn product_type_from_symbol(symbol: &str) -> KrakenProductType {
962    if symbol.starts_with("PI_")
963        || symbol.starts_with("PF_")
964        || symbol.starts_with("PV_")
965        || symbol.starts_with("FI_")
966        || symbol.starts_with("FF_")
967    {
968        KrakenProductType::Futures
969    } else {
970        KrakenProductType::Spot
971    }
972}
973
974#[cfg(test)]
975mod tests {
976    use nautilus_model::enums::{MarketStatusAction, OrderType};
977    use rstest::rstest;
978
979    use super::*;
980
981    #[rstest]
982    #[case::online(KrakenPairStatus::Online, MarketStatusAction::Trading)]
983    #[case::cancel_only(KrakenPairStatus::CancelOnly, MarketStatusAction::Halt)]
984    #[case::post_only(KrakenPairStatus::PostOnly, MarketStatusAction::Pause)]
985    #[case::limit_only(KrakenPairStatus::LimitOnly, MarketStatusAction::Pause)]
986    #[case::reduce_only(KrakenPairStatus::ReduceOnly, MarketStatusAction::Pause)]
987    fn test_pair_status_to_market_status_action(
988        #[case] input: KrakenPairStatus,
989        #[case] expected: MarketStatusAction,
990    ) {
991        assert_eq!(MarketStatusAction::from(input), expected);
992    }
993
994    #[rstest]
995    #[case::trailing_stop(KrakenOrderType::TrailingStop, OrderType::StopMarket)]
996    #[case::trailing_stop_limit(KrakenOrderType::TrailingStopLimit, OrderType::StopLimit)]
997    fn test_trailing_stop_order_type_mapping(
998        #[case] input: KrakenOrderType,
999        #[case] expected: OrderType,
1000    ) {
1001        assert_eq!(OrderType::from(input), expected);
1002    }
1003
1004    #[rstest]
1005    #[case("\"placed\"", KrakenSendStatus::Placed)]
1006    #[case("\"cancelled\"", KrakenSendStatus::Cancelled)]
1007    #[case("\"edited\"", KrakenSendStatus::Edited)]
1008    #[case("\"notFound\"", KrakenSendStatus::NotFound)]
1009    #[case("\"noOrdersToCancel\"", KrakenSendStatus::NoOrdersToCancel)]
1010    #[case(
1011        "\"insufficientAvailableFunds\"",
1012        KrakenSendStatus::InsufficientAvailableFunds
1013    )]
1014    #[case("\"invalidOrderType\"", KrakenSendStatus::InvalidOrderType)]
1015    #[case("\"invalidSize\"", KrakenSendStatus::InvalidSize)]
1016    #[case("\"wouldCauseLiquidation\"", KrakenSendStatus::WouldCauseLiquidation)]
1017    #[case("\"postWouldExecute\"", KrakenSendStatus::PostWouldExecute)]
1018    #[case(
1019        "\"reduceOnlyWouldIncreasePosition\"",
1020        KrakenSendStatus::ReduceOnlyWouldIncreasePosition
1021    )]
1022    fn test_send_status_deserialization(#[case] raw: &str, #[case] expected: KrakenSendStatus) {
1023        let parsed: KrakenSendStatus = serde_json::from_str(raw).unwrap();
1024        assert_eq!(parsed, expected);
1025    }
1026
1027    #[rstest]
1028    #[case("\"last\"", KrakenTriggerSignal::Last)]
1029    #[case("\"last_price\"", KrakenTriggerSignal::Last)]
1030    #[case("\"mark\"", KrakenTriggerSignal::Mark)]
1031    #[case("\"mark_price\"", KrakenTriggerSignal::Mark)]
1032    #[case("\"spot\"", KrakenTriggerSignal::Index)]
1033    #[case("\"spot_price\"", KrakenTriggerSignal::Index)]
1034    #[case("\"index\"", KrakenTriggerSignal::Index)]
1035    #[case("\"index_price\"", KrakenTriggerSignal::Index)]
1036    fn test_trigger_signal_deserialization(
1037        #[case] raw: &str,
1038        #[case] expected: KrakenTriggerSignal,
1039    ) {
1040        let parsed: KrakenTriggerSignal = serde_json::from_str(raw).unwrap();
1041        assert_eq!(parsed, expected);
1042    }
1043
1044    #[rstest]
1045    #[case("\"PLACE\"", KrakenFuturesOrderEventType::Place)]
1046    #[case("\"FILL\"", KrakenFuturesOrderEventType::Fill)]
1047    #[case("\"EXECUTION\"", KrakenFuturesOrderEventType::Execution)]
1048    #[case("\"REJECT\"", KrakenFuturesOrderEventType::Reject)]
1049    #[case("\"CANCEL\"", KrakenFuturesOrderEventType::Cancel)]
1050    #[case("\"EDIT\"", KrakenFuturesOrderEventType::Edit)]
1051    #[case("\"EXPIRE\"", KrakenFuturesOrderEventType::Expire)]
1052    #[case("\"EXPIRED\"", KrakenFuturesOrderEventType::Expire)]
1053    fn test_futures_order_event_type_deserialization(
1054        #[case] raw: &str,
1055        #[case] expected: KrakenFuturesOrderEventType,
1056    ) {
1057        let parsed: KrakenFuturesOrderEventType = serde_json::from_str(raw).unwrap();
1058        assert_eq!(parsed, expected);
1059    }
1060}