1use ahash::AHashMap;
19use rust_decimal::Decimal;
20use serde::{Deserialize, Serialize};
21
22use crate::common::{
23 enums::{
24 KrakenApiResult, KrakenFillType, KrakenFuturesOrderEventType,
25 KrakenFuturesOrderLifecycleStatus, KrakenFuturesOrderStatus, KrakenFuturesOrderType,
26 KrakenInstrumentType, KrakenOrderSide, KrakenPositionSide, KrakenSendStatus,
27 KrakenTriggerSide, KrakenTriggerSignal,
28 },
29 serialization::{decimal, decimal_map, deserialize_decimal_pair, optional_decimal},
30};
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct FuturesMarginLevel {
37 #[serde(alias = "numNonContractUnits", default, with = "decimal")]
40 pub contracts: Decimal,
41 #[serde(with = "decimal")]
42 pub initial_margin: Decimal,
43 #[serde(with = "decimal")]
44 pub maintenance_margin: Decimal,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct FuturesInstrument {
50 pub symbol: String,
51 #[serde(rename = "type")]
52 pub instrument_type: KrakenInstrumentType,
53 #[serde(default)]
55 pub underlying: Option<String>,
56 #[serde(with = "decimal")]
57 pub tick_size: Decimal,
58 #[serde(with = "decimal")]
59 pub contract_size: Decimal,
60 pub tradeable: bool,
61 #[serde(default, with = "optional_decimal")]
62 pub impact_mid_size: Option<Decimal>,
63 #[serde(default, with = "optional_decimal")]
64 pub max_position_size: Option<Decimal>,
65 pub opening_date: String,
66 pub margin_levels: Vec<FuturesMarginLevel>,
67 #[serde(default)]
68 pub funding_rate_coefficient: Option<i32>,
69 #[serde(default, with = "optional_decimal")]
70 pub max_relative_funding_rate: Option<Decimal>,
71 #[serde(default)]
72 pub isin: Option<String>,
73 pub contract_value_trade_precision: i32,
74 pub post_only: bool,
75 #[serde(default)]
80 pub maker_protection_millis: Option<i64>,
81 #[serde(default)]
82 pub fee_schedule_uid: Option<String>,
83 pub mtf: bool,
84 pub base: String,
85 pub quote: String,
86 pub pair: String,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct FuturesInstrumentsResponse {
91 pub result: KrakenApiResult,
92 pub instruments: Vec<FuturesInstrument>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct FuturesTicker {
100 pub symbol: String,
101 #[serde(default, with = "optional_decimal")]
102 pub last: Option<Decimal>,
103 #[serde(default)]
104 pub last_time: Option<String>,
105 pub tag: String,
106 pub pair: String,
107 #[serde(default, with = "optional_decimal")]
108 pub mark_price: Option<Decimal>,
109 #[serde(default, with = "optional_decimal")]
110 pub bid: Option<Decimal>,
111 #[serde(default, with = "optional_decimal")]
112 pub bid_size: Option<Decimal>,
113 #[serde(default, with = "optional_decimal")]
114 pub ask: Option<Decimal>,
115 #[serde(default, with = "optional_decimal")]
116 pub ask_size: Option<Decimal>,
117 #[serde(rename = "vol24h", default, with = "optional_decimal")]
118 pub vol_24h: Option<Decimal>,
119 #[serde(default, with = "optional_decimal")]
120 pub volume_quote: Option<Decimal>,
121 #[serde(default, with = "optional_decimal")]
122 pub open_interest: Option<Decimal>,
123 #[serde(rename = "open24h", default, with = "optional_decimal")]
124 pub open_24h: Option<Decimal>,
125 #[serde(rename = "high24h", default, with = "optional_decimal")]
126 pub high_24h: Option<Decimal>,
127 #[serde(rename = "low24h", default, with = "optional_decimal")]
128 pub low_24h: Option<Decimal>,
129 #[serde(default, with = "optional_decimal")]
130 pub last_size: Option<Decimal>,
131 #[serde(default, with = "optional_decimal")]
132 pub funding_rate: Option<Decimal>,
133 #[serde(default, with = "optional_decimal")]
134 pub funding_rate_prediction: Option<Decimal>,
135 #[serde(default)]
136 pub suspended: bool,
137 #[serde(default, with = "optional_decimal")]
138 pub index_price: Option<Decimal>,
139 #[serde(default)]
140 pub post_only: bool,
141 #[serde(rename = "change24h", default, with = "optional_decimal")]
142 pub change_24h: Option<Decimal>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct FuturesTickersResponse {
148 pub result: KrakenApiResult,
149 #[serde(default)]
150 pub server_time: Option<String>,
151 pub tickers: Vec<FuturesTicker>,
152}
153
154#[derive(Debug, Clone, Serialize)]
158pub struct FuturesOrderBookLevel {
159 #[serde(serialize_with = "decimal::serialize")]
160 pub price: Decimal,
161 #[serde(serialize_with = "decimal::serialize")]
162 pub qty: Decimal,
163}
164
165impl<'de> serde::Deserialize<'de> for FuturesOrderBookLevel {
166 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
167 where
168 D: serde::Deserializer<'de>,
169 {
170 let arr = deserialize_decimal_pair(deserializer)?;
171 Ok(Self {
172 price: arr.0,
173 qty: arr.1,
174 })
175 }
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct FuturesOrderBookData {
181 pub bids: Vec<FuturesOrderBookLevel>,
182 pub asks: Vec<FuturesOrderBookLevel>,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
186#[serde(rename_all = "camelCase")]
187pub struct FuturesOrderBookResponse {
188 pub result: KrakenApiResult,
189 pub order_book: FuturesOrderBookData,
190 #[serde(default)]
191 pub server_time: Option<String>,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct FuturesHistoricalFundingRate {
199 pub timestamp: String,
200 #[serde(with = "decimal")]
201 pub relative_funding_rate: Decimal,
202 #[serde(with = "decimal")]
203 pub funding_rate: Decimal,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct FuturesHistoricalFundingRatesResponse {
209 pub result: KrakenApiResult,
210 pub rates: Vec<FuturesHistoricalFundingRate>,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct FuturesCandle {
217 pub time: i64,
218 pub open: String,
219 pub high: String,
220 pub low: String,
221 pub close: String,
222 pub volume: String,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct FuturesCandlesResponse {
227 pub candles: Vec<FuturesCandle>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
233#[serde(rename_all = "camelCase")]
234pub struct FuturesOpenOrder {
235 #[serde(rename = "order_id")]
236 pub order_id: String,
237 pub symbol: String,
238 pub side: KrakenOrderSide,
239 pub order_type: KrakenFuturesOrderType,
240 #[serde(default, with = "optional_decimal")]
241 pub limit_price: Option<Decimal>,
242 #[serde(default, with = "optional_decimal")]
243 pub stop_price: Option<Decimal>,
244 #[serde(default, with = "optional_decimal")]
245 pub unfilled_size: Option<Decimal>,
246 pub received_time: String,
247 pub status: KrakenFuturesOrderStatus,
248 #[serde(with = "decimal")]
249 pub filled_size: Decimal,
250 #[serde(default)]
251 pub reduce_only: Option<bool>,
252 pub last_update_time: String,
253 #[serde(default)]
254 pub trigger_signal: Option<KrakenTriggerSignal>,
255 #[serde(rename = "cli_ord_id", default)]
256 pub cli_ord_id: Option<String>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct FuturesOpenOrdersResponse {
262 pub result: KrakenApiResult,
263 #[serde(default)]
264 pub server_time: Option<String>,
265 #[serde(default)]
266 pub error: Option<String>,
267 #[serde(default)]
268 pub open_orders: Vec<FuturesOpenOrder>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
275#[serde(rename_all = "camelCase")]
276pub struct FuturesCachedOrder {
277 pub order_id: String,
278 #[serde(default)]
279 pub cli_ord_id: Option<String>,
280 pub symbol: String,
281 pub side: KrakenOrderSide,
282 #[serde(default, with = "optional_decimal")]
283 pub quantity: Option<Decimal>,
284 #[serde(default, with = "optional_decimal")]
285 pub filled: Option<Decimal>,
286 #[serde(default, with = "optional_decimal")]
287 pub limit_price: Option<Decimal>,
288 #[serde(default)]
289 pub reduce_only: bool,
290 pub timestamp: String,
291 pub last_update_timestamp: String,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
296#[serde(rename_all = "camelCase")]
297pub struct FuturesOrderStatusDetails {
298 pub order: FuturesCachedOrder,
299 pub status: KrakenFuturesOrderLifecycleStatus,
300 #[serde(default)]
301 pub update_reason: Option<String>,
302 #[serde(default)]
303 pub error: Option<String>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
309#[serde(rename_all = "camelCase")]
310pub struct FuturesOrdersStatusResponse {
311 pub result: KrakenApiResult,
312 #[serde(default)]
313 pub server_time: Option<String>,
314 #[serde(default)]
315 pub error: Option<String>,
316 #[serde(default)]
317 pub orders: Vec<FuturesOrderStatusDetails>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
324#[serde(rename_all = "camelCase")]
325pub struct FuturesOrderEventWrapper {
326 pub order: FuturesOrderEvent,
327 #[serde(rename = "type")]
328 pub event_type: KrakenFuturesOrderEventType,
329 #[serde(default, with = "optional_decimal")]
330 pub reduced_quantity: Option<Decimal>,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
335#[serde(rename_all = "camelCase")]
336pub struct FuturesOrderEvent {
337 pub order_id: String,
338 #[serde(default)]
339 pub cli_ord_id: Option<String>,
340 #[serde(rename = "type")]
341 pub order_type: KrakenFuturesOrderType,
342 pub symbol: String,
343 pub side: KrakenOrderSide,
344 #[serde(with = "decimal")]
345 pub quantity: Decimal,
346 #[serde(with = "decimal")]
347 pub filled: Decimal,
348 #[serde(default, with = "optional_decimal")]
349 pub limit_price: Option<Decimal>,
350 #[serde(default, with = "optional_decimal")]
351 pub stop_price: Option<Decimal>,
352 pub timestamp: String,
353 pub last_update_timestamp: String,
354 #[serde(default)]
355 pub reduce_only: bool,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
360#[serde(rename_all = "camelCase")]
361pub struct FuturesOrderEventsResponse {
362 #[serde(default)]
363 pub server_time: Option<String>,
364 #[serde(default)]
365 pub order_events: Vec<FuturesOrderEventWrapper>,
366 #[serde(default)]
367 pub continuation_token: Option<String>,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
373#[serde(rename_all = "camelCase")]
374pub struct FuturesFill {
375 #[serde(rename = "fill_id")]
376 pub fill_id: String,
377 pub symbol: String,
378 pub side: KrakenOrderSide,
379 #[serde(rename = "order_id")]
380 pub order_id: String,
381 pub fill_time: String,
382 #[serde(with = "decimal")]
383 pub size: Decimal,
384 #[serde(with = "decimal")]
385 pub price: Decimal,
386 pub fill_type: KrakenFillType,
387 #[serde(rename = "cli_ord_id", default)]
388 pub cli_ord_id: Option<String>,
389 #[serde(rename = "fee_paid", default, with = "optional_decimal")]
390 pub fee_paid: Option<Decimal>,
391 #[serde(rename = "fee_currency", default)]
392 pub fee_currency: Option<String>,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
396#[serde(rename_all = "camelCase")]
397pub struct FuturesFillsResponse {
398 pub result: KrakenApiResult,
399 #[serde(default)]
400 pub server_time: Option<String>,
401 #[serde(default)]
402 pub error: Option<String>,
403 #[serde(default)]
404 pub fills: Vec<FuturesFill>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
410#[serde(rename_all = "camelCase")]
411pub struct FuturesPosition {
412 pub side: KrakenPositionSide,
413 pub symbol: String,
414 #[serde(with = "decimal")]
415 pub price: Decimal,
416 pub fill_time: String,
417 #[serde(with = "decimal")]
418 pub size: Decimal,
419 #[serde(default, with = "optional_decimal")]
420 pub unrealized_funding: Option<Decimal>,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
424#[serde(rename_all = "camelCase")]
425pub struct FuturesOpenPositionsResponse {
426 pub result: KrakenApiResult,
427 #[serde(default)]
428 pub server_time: Option<String>,
429 #[serde(default)]
430 pub error: Option<String>,
431 #[serde(default)]
432 pub open_positions: Vec<FuturesPosition>,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
438#[serde(rename_all = "camelCase")]
439pub struct FuturesSendOrderResponse {
440 pub result: KrakenApiResult,
441 #[serde(default)]
442 pub server_time: Option<String>,
443 #[serde(default)]
444 pub error: Option<String>,
445 pub send_status: Option<FuturesSendStatus>,
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize)]
449#[serde(rename_all = "camelCase")]
450pub struct FuturesSendStatus {
451 #[serde(rename = "order_id", default)]
452 pub order_id: Option<String>,
453 #[serde(rename = "order_tag", default)]
454 pub order_tag: Option<String>,
455 pub status: String,
456 #[serde(default)]
457 pub order_events: Option<Vec<FuturesSendOrderEvent>>,
458 #[serde(rename = "cli_ord_id", default)]
459 pub cli_ord_id: Option<String>,
460 #[serde(rename = "receivedTime", default)]
461 pub received_time: Option<String>,
462}
463
464#[derive(Debug, Clone, Serialize, Deserialize)]
465#[serde(rename_all = "camelCase")]
466pub struct FuturesSendOrderEvent {
467 #[serde(rename = "type")]
468 pub event_type: KrakenFuturesOrderEventType,
469 #[serde(default)]
470 pub order: Option<FuturesOrderEventData>,
471 #[serde(default)]
472 pub order_trigger: Option<FuturesOrderTriggerData>,
473 #[serde(default, with = "optional_decimal")]
474 pub reduced_quantity: Option<Decimal>,
475 #[serde(rename = "executionId", default)]
477 pub execution_id: Option<String>,
478 #[serde(default, with = "optional_decimal")]
479 pub price: Option<Decimal>,
480 #[serde(default, with = "optional_decimal")]
481 pub amount: Option<Decimal>,
482 #[serde(rename = "orderPriorEdit", default)]
483 pub order_prior_edit: Option<Box<FuturesOrderEventData>>,
484 #[serde(rename = "orderPriorExecution", default)]
485 pub order_prior_execution: Option<Box<FuturesOrderEventData>>,
486 #[serde(rename = "takerReducedQuantity", default, with = "optional_decimal")]
487 pub taker_reduced_quantity: Option<Decimal>,
488 #[serde(default)]
490 pub reason: Option<String>,
491 #[serde(default)]
492 pub uid: Option<String>,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
496#[serde(rename_all = "camelCase")]
497pub struct FuturesOrderEventData {
498 #[serde(rename = "orderId")]
499 pub order_id: String,
500 #[serde(rename = "cliOrdId", default)]
501 pub cli_ord_id: Option<String>,
502 #[serde(rename = "type")]
503 pub order_type: KrakenFuturesOrderType,
504 pub symbol: String,
505 pub side: KrakenOrderSide,
506 #[serde(with = "decimal")]
507 pub quantity: Decimal,
508 #[serde(with = "decimal")]
509 pub filled: Decimal,
510 #[serde(rename = "limitPrice", default, with = "optional_decimal")]
511 pub limit_price: Option<Decimal>,
512 #[serde(rename = "stopPrice", default, with = "optional_decimal")]
513 pub stop_price: Option<Decimal>,
514 pub timestamp: String,
515 #[serde(rename = "lastUpdateTimestamp")]
516 pub last_update_timestamp: String,
517 #[serde(rename = "reduceOnly", default)]
518 pub reduce_only: bool,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
522#[serde(rename_all = "camelCase")]
523pub struct FuturesOrderTriggerData {
524 pub uid: String,
525 #[serde(rename = "clientId", default)]
526 pub client_id: Option<String>,
527 #[serde(rename = "type")]
528 pub order_type: KrakenFuturesOrderType,
529 pub symbol: String,
530 pub side: KrakenOrderSide,
531 #[serde(with = "decimal")]
532 pub quantity: Decimal,
533 #[serde(rename = "limitPrice", default, with = "optional_decimal")]
534 pub limit_price: Option<Decimal>,
535 #[serde(rename = "limitPriceOffsetValue", default, with = "optional_decimal")]
536 pub limit_price_offset_value: Option<Decimal>,
537 #[serde(rename = "limitPriceOffsetUnit", default)]
538 pub limit_price_offset_unit: Option<String>,
539 #[serde(rename = "triggerPrice")]
540 #[serde(with = "decimal")]
541 pub trigger_price: Decimal,
542 #[serde(rename = "triggerSide")]
543 pub trigger_side: KrakenTriggerSide,
544 #[serde(rename = "triggerSignal")]
545 pub trigger_signal: KrakenTriggerSignal,
546 #[serde(rename = "reduceOnly", default)]
547 pub reduce_only: bool,
548 pub timestamp: String,
549 #[serde(rename = "lastUpdateTimestamp")]
550 pub last_update_timestamp: String,
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize)]
554#[serde(rename_all = "camelCase")]
555pub struct FuturesCancelOrderResponse {
556 pub result: KrakenApiResult,
557 #[serde(default)]
558 pub server_time: Option<String>,
559 pub cancel_status: FuturesCancelStatus,
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize)]
563#[serde(rename_all = "camelCase")]
564pub struct FuturesCancelStatus {
565 pub status: KrakenSendStatus,
566 #[serde(rename = "order_id", default)]
567 pub order_id: Option<String>,
568 #[serde(rename = "cli_ord_id", default)]
569 pub cli_ord_id: Option<String>,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
573#[serde(rename_all = "camelCase")]
574pub struct FuturesEditOrderResponse {
575 pub result: KrakenApiResult,
576 #[serde(default)]
577 pub server_time: Option<String>,
578 pub edit_status: FuturesEditStatus,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize)]
582#[serde(rename_all = "camelCase")]
583pub struct FuturesEditStatus {
584 pub status: String,
585 #[serde(rename = "order_id", default)]
586 pub order_id: Option<String>,
587 #[serde(rename = "cli_ord_id", default)]
588 pub cli_ord_id: Option<String>,
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
592#[serde(rename_all = "camelCase")]
593pub struct FuturesBatchOrderResponse {
594 pub result: KrakenApiResult,
595 #[serde(default)]
596 pub server_time: Option<String>,
597 pub batch_status: Vec<FuturesSendStatus>,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
605#[serde(rename_all = "camelCase")]
606pub struct FuturesBatchCancelResponse {
607 pub result: KrakenApiResult,
608 #[serde(default)]
609 pub server_time: Option<String>,
610 #[serde(default)]
611 pub error: Option<String>,
612 #[serde(default)]
613 pub batch_status: Vec<FuturesBatchCancelStatus>,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
617#[serde(rename_all = "camelCase")]
618pub struct FuturesBatchCancelStatus {
619 #[serde(default)]
620 pub order_id: Option<String>,
621 #[serde(default)]
622 pub cli_ord_id: Option<String>,
623 #[serde(default)]
624 pub status: Option<KrakenSendStatus>,
625 #[serde(default)]
626 pub cancel_status: Option<FuturesCancelStatus>,
627}
628
629#[derive(Debug, Clone, Serialize, Deserialize)]
630#[serde(rename_all = "camelCase")]
631pub struct FuturesCancelAllOrdersResponse {
632 pub result: KrakenApiResult,
633 #[serde(default)]
634 pub server_time: Option<String>,
635 pub cancel_status: FuturesCancelAllStatus,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize)]
639#[serde(rename_all = "camelCase")]
640pub struct FuturesCancelAllStatus {
641 pub status: KrakenSendStatus,
642 #[serde(default)]
643 pub cancelled_orders: Vec<CancelledOrder>,
644}
645
646#[derive(Debug, Clone, Serialize, Deserialize)]
647#[serde(rename_all = "camelCase")]
648pub struct CancelledOrder {
649 #[serde(rename = "order_id", default)]
650 pub order_id: Option<String>,
651 #[serde(default)]
652 pub cli_ord_id: Option<String>,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
659#[serde(rename_all = "camelCase")]
660pub struct FuturesPublicExecutionsResponse {
661 pub elements: Vec<FuturesPublicExecutionElement>,
662 #[serde(default)]
663 pub len: Option<i64>,
664 #[serde(default)]
665 pub continuation_token: Option<String>,
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize)]
670pub struct FuturesPublicExecutionElement {
671 pub uid: String,
672 pub timestamp: i64,
673 pub event: FuturesPublicExecutionEvent,
674}
675
676#[derive(Debug, Clone, Serialize, Deserialize)]
678pub struct FuturesPublicExecutionEvent {
679 #[serde(rename = "Execution")]
680 pub execution: FuturesPublicExecutionWrapper,
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize)]
685#[serde(rename_all = "camelCase")]
686pub struct FuturesPublicExecutionWrapper {
687 pub execution: FuturesPublicExecution,
688 #[serde(default)]
689 pub taker_reduced_quantity: Option<String>,
690}
691
692#[derive(Debug, Clone, Serialize, Deserialize)]
694#[serde(rename_all = "camelCase")]
695pub struct FuturesPublicExecution {
696 pub uid: String,
697 pub maker_order: FuturesPublicOrder,
698 pub taker_order: FuturesPublicOrder,
699 pub timestamp: i64,
700 pub quantity: String,
701 pub price: String,
702 #[serde(default)]
703 pub mark_price: Option<String>,
704 #[serde(default)]
705 pub limit_filled: Option<bool>,
706 #[serde(default)]
707 pub usd_value: Option<String>,
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize)]
712#[serde(rename_all = "camelCase")]
713pub struct FuturesPublicOrder {
714 pub uid: String,
715 pub tradeable: String,
716 pub direction: String,
717 pub quantity: String,
718 pub timestamp: i64,
719 #[serde(default)]
720 pub limit_price: Option<String>,
721 #[serde(default)]
722 pub order_type: Option<String>,
723 #[serde(default)]
724 pub reduce_only: Option<bool>,
725 #[serde(default)]
726 pub last_update_timestamp: Option<i64>,
727}
728
729#[derive(Debug, Clone, Serialize, Deserialize)]
733#[serde(rename_all = "camelCase")]
734pub struct FuturesAccountsResponse {
735 pub result: KrakenApiResult,
736 #[serde(default)]
737 pub accounts: AHashMap<String, FuturesAccount>,
738 #[serde(default)]
739 pub error: Option<String>,
740 #[serde(default)]
741 pub server_time: Option<String>,
742}
743
744#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
746#[serde(rename_all = "camelCase")]
747pub enum KrakenFuturesAccountType {
748 MultiCollateralMarginAccount,
750 MarginAccount,
752 CashAccount,
754 #[serde(other)]
756 Unknown,
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
761#[serde(rename_all = "camelCase")]
762pub struct FuturesAccount {
763 #[serde(rename = "type")]
764 pub account_type: KrakenFuturesAccountType,
765 #[serde(default, with = "decimal_map")]
767 pub balances: AHashMap<String, Decimal>,
768 #[serde(default)]
770 pub currencies: AHashMap<String, FuturesFlexCurrency>,
771 #[serde(default)]
773 pub auxiliary: Option<FuturesAuxiliary>,
774 #[serde(default)]
776 pub margin_requirements: Option<FuturesMarginRequirements>,
777 #[serde(default, with = "optional_decimal")]
779 pub portfolio_value: Option<Decimal>,
780 #[serde(default, with = "optional_decimal")]
782 pub available_margin: Option<Decimal>,
783 #[serde(default, with = "optional_decimal")]
785 pub initial_margin: Option<Decimal>,
786 #[serde(default, with = "optional_decimal")]
788 pub pnl: Option<Decimal>,
789}
790
791#[derive(Debug, Clone, Serialize, Deserialize)]
793#[serde(rename_all = "camelCase")]
794pub struct FuturesFlexCurrency {
795 #[serde(with = "decimal")]
796 pub quantity: Decimal,
797 #[serde(default, with = "optional_decimal")]
798 pub value: Option<Decimal>,
799 #[serde(default, with = "optional_decimal")]
800 pub collateral: Option<Decimal>,
801 #[serde(default, with = "optional_decimal")]
802 pub available: Option<Decimal>,
803}
804
805#[derive(Debug, Clone, Serialize, Deserialize)]
807#[serde(rename_all = "camelCase")]
808pub struct FuturesAuxiliary {
809 #[serde(default, with = "optional_decimal")]
810 pub usd: Option<Decimal>,
811 #[serde(default, with = "optional_decimal")]
813 pub pv: Option<Decimal>,
814 #[serde(default, with = "optional_decimal")]
816 pub pnl: Option<Decimal>,
817 #[serde(default, with = "optional_decimal")]
819 pub af: Option<Decimal>,
820 #[serde(default, with = "optional_decimal")]
821 pub funding: Option<Decimal>,
822}
823
824#[derive(Debug, Clone, Serialize, Deserialize)]
826#[serde(rename_all = "camelCase")]
827pub struct FuturesMarginRequirements {
828 #[serde(default, with = "optional_decimal")]
830 pub im: Option<Decimal>,
831 #[serde(default, with = "optional_decimal")]
833 pub mm: Option<Decimal>,
834 #[serde(default, with = "optional_decimal")]
836 pub lt: Option<Decimal>,
837 #[serde(default, with = "optional_decimal")]
839 pub tt: Option<Decimal>,
840}
841
842#[cfg(test)]
843mod tests {
844 use rstest::rstest;
845 use rust_decimal_macros::dec;
846
847 use super::*;
848
849 fn load_test_data(filename: &str) -> String {
850 let path = format!("test_data/{filename}");
851 std::fs::read_to_string(&path)
852 .unwrap_or_else(|e| panic!("Failed to load test data from {path}: {e}"))
853 }
854
855 #[rstest]
856 fn test_parse_futures_cancel_all_orders_with_no_orders_to_cancel_status() {
857 let raw = r#"{
862 "result": "success",
863 "cancelStatus": {
864 "receivedTime": "2026-04-10T13:17:23.291Z",
865 "cancelOnly": "PF_XBTUSD",
866 "status": "noOrdersToCancel",
867 "cancelledOrders": [
868 {
869 "order_id": "a182b1c0-cd01-4d1c-853b-605e936f412b",
870 "cliOrdId": "5f173994-f660-4809-b97a-586221fe5926"
871 }
872 ],
873 "orderEvents": []
874 },
875 "serverTime": "2026-04-10T13:17:23.291Z"
876 }"#;
877
878 let response: FuturesCancelAllOrdersResponse =
879 serde_json::from_str(raw).expect("Failed to parse cancel-all response");
880
881 assert_eq!(response.result, KrakenApiResult::Success);
882 assert_eq!(
883 response.cancel_status.status,
884 KrakenSendStatus::NoOrdersToCancel
885 );
886 assert_eq!(response.cancel_status.cancelled_orders.len(), 1);
887 assert_eq!(
888 response.cancel_status.cancelled_orders[0]
889 .order_id
890 .as_deref(),
891 Some("a182b1c0-cd01-4d1c-853b-605e936f412b")
892 );
893 assert_eq!(
894 response.cancel_status.cancelled_orders[0]
895 .cli_ord_id
896 .as_deref(),
897 Some("5f173994-f660-4809-b97a-586221fe5926")
898 );
899 }
900
901 #[rstest]
902 fn test_parse_futures_open_orders() {
903 let data = load_test_data("http_futures_open_orders.json");
904 let response: FuturesOpenOrdersResponse =
905 serde_json::from_str(&data).expect("Failed to parse futures open orders");
906
907 assert_eq!(response.result, KrakenApiResult::Success);
908 assert_eq!(response.open_orders.len(), 3);
909
910 let order = &response.open_orders[0];
911 assert_eq!(order.order_id, "2ce038ae-c144-4de7-a0f1-82f7f4fca864");
912 assert_eq!(order.symbol, "PI_ETHUSD");
913 assert_eq!(order.side, KrakenOrderSide::Buy);
914 assert_eq!(order.order_type, KrakenFuturesOrderType::Limit);
915 assert_eq!(order.limit_price, Some(dec!(1200)));
916 assert_eq!(order.unfilled_size, Some(dec!(100)));
917 assert_eq!(order.filled_size, dec!(0));
918
919 let trigger_order = &response.open_orders[1];
920 assert_eq!(
921 trigger_order.order_id,
922 "c8135f52-2a86-4e26-b629-43cc37da9dbf"
923 );
924 assert_eq!(trigger_order.order_type, KrakenFuturesOrderType::TakeProfit);
925 assert_eq!(trigger_order.symbol, "PI_XBTUSD");
926 assert_eq!(trigger_order.side, KrakenOrderSide::Buy);
927 assert_eq!(trigger_order.limit_price, None);
928 assert_eq!(trigger_order.stop_price, Some(dec!(1880.4)));
929 assert_eq!(trigger_order.unfilled_size, None);
930 assert_eq!(trigger_order.received_time, "2023-04-07T15:14:25.995Z");
931 assert_eq!(trigger_order.status, KrakenFuturesOrderStatus::Untouched);
932 assert_eq!(trigger_order.filled_size, dec!(0));
933 assert_eq!(trigger_order.reduce_only, Some(true));
934 assert_eq!(trigger_order.last_update_time, "2023-04-07T15:14:25.995Z");
935 assert_eq!(
936 trigger_order.trigger_signal,
937 Some(KrakenTriggerSignal::Last)
938 );
939 assert_eq!(trigger_order.cli_ord_id, None);
940 }
941
942 #[rstest]
943 fn test_parse_futures_orders_status() {
944 let data = load_test_data("http_futures_orders_status.json");
945 let response: FuturesOrdersStatusResponse =
946 serde_json::from_str(&data).expect("Failed to parse futures orders status");
947
948 assert_eq!(response.result, KrakenApiResult::Success);
949 assert_eq!(response.orders.len(), 2);
950
951 let part_filled = &response.orders[0];
952 assert_eq!(
953 part_filled.order.order_id,
954 "5f6d15a5-8c9e-4b0a-9d3f-5a2b7c8d9e0f"
955 );
956 assert_eq!(
957 part_filled.order.cli_ord_id.as_deref(),
958 Some("uuid-mp-held-001")
959 );
960 assert_eq!(part_filled.order.side, KrakenOrderSide::Buy);
961 assert_eq!(part_filled.order.quantity, Some(dec!(0.001)));
962 assert_eq!(part_filled.order.filled, Some(dec!(0.0004)));
963 assert_eq!(part_filled.order.limit_price, Some(dec!(70000)));
964 assert!(!part_filled.order.reduce_only);
965 assert_eq!(
966 part_filled.status,
967 KrakenFuturesOrderLifecycleStatus::Cancelled
968 );
969 assert_eq!(part_filled.update_reason.as_deref(), Some("PARTIAL_FILL"));
970
971 let open = &response.orders[1];
972 assert_eq!(open.order.quantity, Some(dec!(0.0002)));
973 assert_eq!(open.order.filled, Some(dec!(0)));
974 assert!(open.order.reduce_only);
975 assert_eq!(open.status, KrakenFuturesOrderLifecycleStatus::EnteredBook);
976 assert_eq!(open.update_reason, None);
977 }
978
979 #[rstest]
980 fn test_parse_futures_instruments_maker_protection() {
981 let data = load_test_data("http_futures_instruments_maker_protection.json");
982 let response: FuturesInstrumentsResponse =
983 serde_json::from_str(&data).expect("Failed to parse futures instruments");
984
985 assert_eq!(response.result, KrakenApiResult::Success);
986 assert_eq!(response.instruments.len(), 2);
987
988 let protected = &response.instruments[0];
989 assert_eq!(protected.symbol, "PF_ATOMUSD");
990 assert_eq!(protected.maker_protection_millis, Some(20));
991
992 let unprotected = &response.instruments[1];
995 assert_eq!(unprotected.symbol, "PF_ETHUSD");
996 assert_eq!(unprotected.maker_protection_millis, None);
997 }
998
999 #[rstest]
1000 fn test_parse_futures_fills() {
1001 let data = load_test_data("http_futures_fills.json");
1002 let response: FuturesFillsResponse =
1003 serde_json::from_str(&data).expect("Failed to parse futures fills");
1004
1005 assert_eq!(response.result, KrakenApiResult::Success);
1006 assert_eq!(response.fills.len(), 3);
1007
1008 let fill = &response.fills[0];
1009 assert_eq!(fill.fill_id, "cad76f07-814e-4dc6-8478-7867407b6bff");
1010 assert_eq!(fill.symbol, "PI_XBTUSD");
1011 assert_eq!(fill.side, KrakenOrderSide::Buy);
1012 assert_eq!(fill.size, dec!(5000));
1013 assert_eq!(fill.price, dec!(27937.5));
1014 assert_eq!(fill.fill_type, KrakenFillType::Maker);
1015 assert_eq!(fill.fee_currency, Some("BTC".to_string()));
1016 assert_eq!(response.fills[1].fill_type, KrakenFillType::Taker);
1017 assert_eq!(response.fills[2].fill_type, KrakenFillType::Assignee);
1018 }
1019
1020 #[rstest]
1021 fn test_parse_futures_open_positions() {
1022 let data = load_test_data("http_futures_open_positions.json");
1023 let response: FuturesOpenPositionsResponse =
1024 serde_json::from_str(&data).expect("Failed to parse futures open positions");
1025
1026 assert_eq!(response.result, KrakenApiResult::Success);
1027 assert_eq!(response.open_positions.len(), 2);
1028
1029 let position = &response.open_positions[0];
1030 assert_eq!(position.side, KrakenPositionSide::Short);
1031 assert_eq!(position.symbol, "PI_XBTUSD");
1032 assert_eq!(position.size, dec!(8000));
1033 assert!(position.unrealized_funding.is_some());
1034 }
1035
1036 #[rstest]
1037 fn test_parse_futures_orderbook() {
1038 let data = load_test_data("http_futures_orderbook.json");
1039 let response: FuturesOrderBookResponse =
1040 serde_json::from_str(&data).expect("Failed to parse futures orderbook");
1041
1042 assert_eq!(response.result, KrakenApiResult::Success);
1043 assert_eq!(response.order_book.bids.len(), 3);
1044 assert_eq!(response.order_book.asks.len(), 3);
1045
1046 let best_bid = &response.order_book.bids[0];
1047 assert_eq!(best_bid.price, dec!(105900));
1048 assert_eq!(best_bid.qty, dec!(0.5));
1049
1050 let best_ask = &response.order_book.asks[0];
1051 assert_eq!(best_ask.price, dec!(105950));
1052 assert_eq!(best_ask.qty, dec!(0.3));
1053 }
1054
1055 #[rstest]
1056 fn test_parse_futures_historical_funding_rates() {
1057 let data = load_test_data("http_futures_historical_funding_rates.json");
1058 let response: FuturesHistoricalFundingRatesResponse =
1059 serde_json::from_str(&data).expect("Failed to parse historical funding rates");
1060
1061 assert_eq!(response.result, KrakenApiResult::Success);
1062 assert_eq!(response.rates.len(), 3);
1063
1064 let rate = &response.rates[0];
1065 assert_eq!(rate.timestamp, "2025-07-11T08:00:00.000Z");
1066 assert_eq!(rate.relative_funding_rate, dec!(0.0001));
1067 assert_eq!(rate.funding_rate, dec!(0.00005));
1068
1069 let negative_rate = &response.rates[1];
1070 assert_eq!(negative_rate.relative_funding_rate, dec!(-0.00005));
1071 }
1072
1073 #[rstest]
1074 fn test_parse_futures_orderbook_preserves_decimal_precision() {
1075 let data = load_test_data("http_futures_orderbook_precision.json");
1076 let level: FuturesOrderBookLevel = serde_json::from_str(&data).unwrap();
1077
1078 assert_eq!(level.price, dec!(0.1234567890123456789012345678));
1079 assert_eq!(level.qty, dec!(123456789.123456789));
1080 }
1081
1082 #[rstest]
1083 fn test_parse_futures_order_events_uses_enum_event_type() {
1084 let data = load_test_data("http_futures_order_events.json");
1085 let response: FuturesOrderEventsResponse =
1086 serde_json::from_str(&data).expect("Failed to parse futures order events");
1087
1088 assert_eq!(response.order_events.len(), 3);
1089 assert_eq!(
1090 response.order_events[0].event_type,
1091 KrakenFuturesOrderEventType::Place
1092 );
1093 assert_eq!(
1094 response.order_events[1].event_type,
1095 KrakenFuturesOrderEventType::Fill
1096 );
1097 assert_eq!(
1098 response.order_events[2].event_type,
1099 KrakenFuturesOrderEventType::Cancel
1100 );
1101 }
1102
1103 #[rstest]
1104 fn test_parse_futures_order_events_tolerates_unknown_enum_values() {
1105 let data = load_test_data("http_futures_order_events_unknown.json");
1108 let response: FuturesOrderEventsResponse =
1109 serde_json::from_str(&data).expect("Failed to parse futures order events with unknown");
1110
1111 assert_eq!(response.order_events.len(), 1);
1112 assert_eq!(
1113 response.order_events[0].order.order_type,
1114 KrakenFuturesOrderType::Unknown
1115 );
1116 }
1117
1118 #[rstest]
1119 fn test_parse_futures_order_trigger_data_tolerates_unknown_enum_values() {
1120 let data = load_test_data("http_send_order_futures_unknown_trigger.json");
1123 let response: FuturesSendOrderResponse =
1124 serde_json::from_str(&data).expect("Failed to parse send-order response with unknown");
1125
1126 let send_status = response.send_status.expect("sendStatus missing");
1127 let order_events = send_status.order_events.expect("orderEvents missing");
1128 let trigger = order_events[0]
1129 .order_trigger
1130 .as_ref()
1131 .expect("orderTrigger missing");
1132
1133 assert_eq!(trigger.order_type, KrakenFuturesOrderType::Unknown);
1134 assert_eq!(trigger.trigger_side, KrakenTriggerSide::Unknown);
1135 assert_eq!(trigger.trigger_signal, KrakenTriggerSignal::Unknown);
1136 }
1137
1138 #[rstest]
1139 fn test_parse_futures_send_order_execution_event_uses_enum_event_type() {
1140 let data = r#"
1141 {
1142 "result": "success",
1143 "sendStatus": {
1144 "status": "placed",
1145 "orderEvents": [
1146 {
1147 "type": "EXECUTION",
1148 "executionId": "c8a35168-8d52-4609-944f-3f32bb0d5c77",
1149 "price": 35000.5,
1150 "amount": 1.25,
1151 "orderPriorExecution": {
1152 "orderId": "c8a35168-8d52-4609-944f-3f32bb0d5c77",
1153 "cliOrdId": "test-order-001",
1154 "type": "lmt",
1155 "symbol": "PI_XBTUSD",
1156 "side": "buy",
1157 "quantity": 2.0,
1158 "filled": 0.0,
1159 "limitPrice": 35000.5,
1160 "timestamp": "2024-01-15T10:30:45.123Z",
1161 "lastUpdateTimestamp": "2024-01-15T10:30:45.123Z",
1162 "reduceOnly": false
1163 }
1164 }
1165 ]
1166 }
1167 }
1168 "#;
1169 let response: FuturesSendOrderResponse =
1170 serde_json::from_str(data).expect("Failed to parse futures send order response");
1171
1172 let send_status = response.send_status.expect("sendStatus missing");
1173 let order_events = send_status.order_events.expect("orderEvents missing");
1174
1175 assert_eq!(order_events.len(), 1);
1176 assert_eq!(
1177 order_events[0].event_type,
1178 KrakenFuturesOrderEventType::Execution
1179 );
1180 }
1181}