1use serde::{Deserialize, Serialize};
19
20use crate::common::enums::{
21 KrakenApiResult, KrakenFillType, KrakenFuturesOrderEventType, KrakenFuturesOrderStatus,
22 KrakenFuturesOrderType, KrakenInstrumentType, KrakenOrderSide, KrakenPositionSide,
23 KrakenSendStatus, KrakenTriggerSide, KrakenTriggerSignal,
24};
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct FuturesMarginLevel {
31 #[serde(alias = "numNonContractUnits", default)]
34 pub contracts: f64,
35 pub initial_margin: f64,
36 pub maintenance_margin: f64,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct FuturesInstrument {
42 pub symbol: String,
43 #[serde(rename = "type")]
44 pub instrument_type: KrakenInstrumentType,
45 #[serde(default)]
47 pub underlying: Option<String>,
48 pub tick_size: f64,
49 pub contract_size: f64,
50 pub tradeable: bool,
51 #[serde(default)]
52 pub impact_mid_size: Option<f64>,
53 #[serde(default)]
54 pub max_position_size: Option<f64>,
55 pub opening_date: String,
56 pub margin_levels: Vec<FuturesMarginLevel>,
57 #[serde(default)]
58 pub funding_rate_coefficient: Option<i32>,
59 #[serde(default)]
60 pub max_relative_funding_rate: Option<f64>,
61 #[serde(default)]
62 pub isin: Option<String>,
63 pub contract_value_trade_precision: i32,
64 pub post_only: bool,
65 pub fee_schedule_uid: String,
66 pub mtf: bool,
67 pub base: String,
68 pub quote: String,
69 pub pair: String,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct FuturesInstrumentsResponse {
74 pub result: KrakenApiResult,
75 pub instruments: Vec<FuturesInstrument>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub struct FuturesTicker {
83 pub symbol: String,
84 #[serde(default)]
85 pub last: Option<f64>,
86 #[serde(default)]
87 pub last_time: Option<String>,
88 pub tag: String,
89 pub pair: String,
90 #[serde(default)]
91 pub mark_price: Option<f64>,
92 #[serde(default)]
93 pub bid: Option<f64>,
94 #[serde(default)]
95 pub bid_size: Option<f64>,
96 #[serde(default)]
97 pub ask: Option<f64>,
98 #[serde(default)]
99 pub ask_size: Option<f64>,
100 #[serde(rename = "vol24h", default)]
101 pub vol_24h: Option<f64>,
102 #[serde(default)]
103 pub volume_quote: Option<f64>,
104 #[serde(default)]
105 pub open_interest: Option<f64>,
106 #[serde(rename = "open24h", default)]
107 pub open_24h: Option<f64>,
108 #[serde(rename = "high24h", default)]
109 pub high_24h: Option<f64>,
110 #[serde(rename = "low24h", default)]
111 pub low_24h: Option<f64>,
112 #[serde(default)]
113 pub last_size: Option<f64>,
114 #[serde(default)]
115 pub funding_rate: Option<f64>,
116 #[serde(default)]
117 pub funding_rate_prediction: Option<f64>,
118 #[serde(default)]
119 pub suspended: bool,
120 #[serde(default)]
121 pub index_price: Option<f64>,
122 #[serde(default)]
123 pub post_only: bool,
124 #[serde(rename = "change24h", default)]
125 pub change_24h: Option<f64>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct FuturesTickersResponse {
131 pub result: KrakenApiResult,
132 #[serde(default)]
133 pub server_time: Option<String>,
134 pub tickers: Vec<FuturesTicker>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct FuturesCandle {
141 pub time: i64,
142 pub open: String,
143 pub high: String,
144 pub low: String,
145 pub close: String,
146 pub volume: String,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct FuturesCandlesResponse {
151 pub candles: Vec<FuturesCandle>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
157#[serde(rename_all = "camelCase")]
158pub struct FuturesOpenOrder {
159 #[serde(rename = "order_id")]
160 pub order_id: String,
161 pub symbol: String,
162 pub side: KrakenOrderSide,
163 pub order_type: KrakenFuturesOrderType,
164 #[serde(default)]
165 pub limit_price: Option<f64>,
166 #[serde(default)]
167 pub stop_price: Option<f64>,
168 pub unfilled_size: f64,
169 pub received_time: String,
170 pub status: KrakenFuturesOrderStatus,
171 pub filled_size: f64,
172 #[serde(default)]
173 pub reduce_only: Option<bool>,
174 pub last_update_time: String,
175 #[serde(default)]
176 pub trigger_signal: Option<KrakenTriggerSignal>,
177 #[serde(rename = "cli_ord_id", default)]
178 pub cli_ord_id: Option<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
182#[serde(rename_all = "camelCase")]
183pub struct FuturesOpenOrdersResponse {
184 pub result: KrakenApiResult,
185 #[serde(default)]
186 pub server_time: Option<String>,
187 #[serde(default)]
188 pub error: Option<String>,
189 #[serde(default)]
190 pub open_orders: Vec<FuturesOpenOrder>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct FuturesOrderEventWrapper {
199 pub order: FuturesOrderEvent,
200 #[serde(rename = "type")]
201 pub event_type: String,
202 #[serde(default)]
203 pub reduced_quantity: Option<f64>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct FuturesOrderEvent {
210 pub order_id: String,
211 #[serde(default)]
212 pub cli_ord_id: Option<String>,
213 #[serde(rename = "type")]
214 pub order_type: KrakenFuturesOrderType,
215 pub symbol: String,
216 pub side: KrakenOrderSide,
217 pub quantity: f64,
218 pub filled: f64,
219 #[serde(default)]
220 pub limit_price: Option<f64>,
221 #[serde(default)]
222 pub stop_price: Option<f64>,
223 pub timestamp: String,
224 pub last_update_timestamp: String,
225 #[serde(default)]
226 pub reduce_only: bool,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct FuturesOrderEventsResponse {
233 #[serde(default)]
234 pub server_time: Option<String>,
235 #[serde(default)]
236 pub order_events: Vec<FuturesOrderEventWrapper>,
237 #[serde(default)]
238 pub continuation_token: Option<String>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
244#[serde(rename_all = "camelCase")]
245pub struct FuturesFill {
246 #[serde(rename = "fill_id")]
247 pub fill_id: String,
248 pub symbol: String,
249 pub side: KrakenOrderSide,
250 #[serde(rename = "order_id")]
251 pub order_id: String,
252 pub fill_time: String,
253 pub size: f64,
254 pub price: f64,
255 pub fill_type: KrakenFillType,
256 #[serde(rename = "cli_ord_id", default)]
257 pub cli_ord_id: Option<String>,
258 #[serde(rename = "fee_paid", default)]
259 pub fee_paid: Option<f64>,
260 #[serde(rename = "fee_currency", default)]
261 pub fee_currency: Option<String>,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct FuturesFillsResponse {
267 pub result: KrakenApiResult,
268 #[serde(default)]
269 pub server_time: Option<String>,
270 #[serde(default)]
271 pub error: Option<String>,
272 #[serde(default)]
273 pub fills: Vec<FuturesFill>,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
279#[serde(rename_all = "camelCase")]
280pub struct FuturesPosition {
281 pub side: KrakenPositionSide,
282 pub symbol: String,
283 pub price: f64,
284 pub fill_time: String,
285 pub size: f64,
286 #[serde(default)]
287 pub unrealized_funding: Option<f64>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291#[serde(rename_all = "camelCase")]
292pub struct FuturesOpenPositionsResponse {
293 pub result: KrakenApiResult,
294 #[serde(default)]
295 pub server_time: Option<String>,
296 #[serde(default)]
297 pub error: Option<String>,
298 #[serde(default)]
299 pub open_positions: Vec<FuturesPosition>,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306pub struct FuturesSendOrderResponse {
307 pub result: KrakenApiResult,
308 #[serde(default)]
309 pub server_time: Option<String>,
310 #[serde(default)]
311 pub error: Option<String>,
312 pub send_status: Option<FuturesSendStatus>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
316#[serde(rename_all = "camelCase")]
317pub struct FuturesSendStatus {
318 #[serde(rename = "order_id", default)]
319 pub order_id: Option<String>,
320 pub status: String,
321 #[serde(default)]
322 pub order_events: Option<Vec<FuturesSendOrderEvent>>,
323 #[serde(rename = "cli_ord_id", default)]
324 pub cli_ord_id: Option<String>,
325 #[serde(rename = "receivedTime", default)]
326 pub received_time: Option<String>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
330#[serde(rename_all = "camelCase")]
331pub struct FuturesSendOrderEvent {
332 #[serde(rename = "type")]
333 pub event_type: KrakenFuturesOrderEventType,
334 #[serde(default)]
335 pub order: Option<FuturesOrderEventData>,
336 #[serde(default)]
337 pub order_trigger: Option<FuturesOrderTriggerData>,
338 #[serde(default)]
339 pub reduced_quantity: Option<f64>,
340 #[serde(rename = "executionId", default)]
342 pub execution_id: Option<String>,
343 #[serde(default)]
344 pub price: Option<f64>,
345 #[serde(default)]
346 pub amount: Option<f64>,
347 #[serde(rename = "orderPriorEdit", default)]
348 pub order_prior_edit: Option<Box<FuturesOrderEventData>>,
349 #[serde(rename = "orderPriorExecution", default)]
350 pub order_prior_execution: Option<Box<FuturesOrderEventData>>,
351 #[serde(rename = "takerReducedQuantity", default)]
352 pub taker_reduced_quantity: Option<f64>,
353 #[serde(default)]
355 pub reason: Option<String>,
356 #[serde(default)]
357 pub uid: Option<String>,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
361#[serde(rename_all = "camelCase")]
362pub struct FuturesOrderEventData {
363 #[serde(rename = "orderId")]
364 pub order_id: String,
365 #[serde(rename = "cliOrdId", default)]
366 pub cli_ord_id: Option<String>,
367 #[serde(rename = "type")]
368 pub order_type: KrakenFuturesOrderType,
369 pub symbol: String,
370 pub side: KrakenOrderSide,
371 pub quantity: f64,
372 pub filled: f64,
373 #[serde(rename = "limitPrice", default)]
374 pub limit_price: Option<f64>,
375 #[serde(rename = "stopPrice", default)]
376 pub stop_price: Option<f64>,
377 pub timestamp: String,
378 #[serde(rename = "lastUpdateTimestamp")]
379 pub last_update_timestamp: String,
380 #[serde(rename = "reduceOnly", default)]
381 pub reduce_only: bool,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
385#[serde(rename_all = "camelCase")]
386pub struct FuturesOrderTriggerData {
387 pub uid: String,
388 #[serde(rename = "clientId", default)]
389 pub client_id: Option<String>,
390 #[serde(rename = "type")]
391 pub order_type: KrakenFuturesOrderType,
392 pub symbol: String,
393 pub side: KrakenOrderSide,
394 pub quantity: f64,
395 #[serde(rename = "limitPrice", default)]
396 pub limit_price: Option<f64>,
397 #[serde(rename = "limitPriceOffsetValue", default)]
398 pub limit_price_offset_value: Option<f64>,
399 #[serde(rename = "limitPriceOffsetUnit", default)]
400 pub limit_price_offset_unit: Option<String>,
401 #[serde(rename = "triggerPrice")]
402 pub trigger_price: f64,
403 #[serde(rename = "triggerSide")]
404 pub trigger_side: KrakenTriggerSide,
405 #[serde(rename = "triggerSignal")]
406 pub trigger_signal: KrakenTriggerSignal,
407 #[serde(rename = "reduceOnly", default)]
408 pub reduce_only: bool,
409 pub timestamp: String,
410 #[serde(rename = "lastUpdateTimestamp")]
411 pub last_update_timestamp: String,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
415#[serde(rename_all = "camelCase")]
416pub struct FuturesCancelOrderResponse {
417 pub result: KrakenApiResult,
418 #[serde(default)]
419 pub server_time: Option<String>,
420 pub cancel_status: FuturesCancelStatus,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
424#[serde(rename_all = "camelCase")]
425pub struct FuturesCancelStatus {
426 pub status: KrakenSendStatus,
427 #[serde(rename = "order_id", default)]
428 pub order_id: Option<String>,
429 #[serde(rename = "cli_ord_id", default)]
430 pub cli_ord_id: Option<String>,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
434#[serde(rename_all = "camelCase")]
435pub struct FuturesEditOrderResponse {
436 pub result: KrakenApiResult,
437 #[serde(default)]
438 pub server_time: Option<String>,
439 pub edit_status: FuturesEditStatus,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize)]
443#[serde(rename_all = "camelCase")]
444pub struct FuturesEditStatus {
445 pub status: String,
446 #[serde(rename = "order_id", default)]
447 pub order_id: Option<String>,
448 #[serde(rename = "cli_ord_id", default)]
449 pub cli_ord_id: Option<String>,
450}
451
452#[derive(Debug, Clone, Serialize, Deserialize)]
453#[serde(rename_all = "camelCase")]
454pub struct FuturesBatchOrderResponse {
455 pub result: KrakenApiResult,
456 #[serde(default)]
457 pub server_time: Option<String>,
458 pub batch_status: Vec<FuturesSendStatus>,
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct FuturesBatchCancelResponse {
468 pub result: KrakenApiResult,
469 #[serde(default)]
470 pub server_time: Option<String>,
471 #[serde(default)]
472 pub error: Option<String>,
473 #[serde(default)]
474 pub batch_status: Vec<FuturesBatchCancelStatus>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
478#[serde(rename_all = "camelCase")]
479pub struct FuturesBatchCancelStatus {
480 #[serde(default)]
481 pub order_id: Option<String>,
482 #[serde(default)]
483 pub cli_ord_id: Option<String>,
484 #[serde(default)]
485 pub status: Option<KrakenSendStatus>,
486 #[serde(default)]
487 pub cancel_status: Option<FuturesCancelStatus>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
491#[serde(rename_all = "camelCase")]
492pub struct FuturesCancelAllOrdersResponse {
493 pub result: KrakenApiResult,
494 #[serde(default)]
495 pub server_time: Option<String>,
496 pub cancel_status: FuturesCancelAllStatus,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
500#[serde(rename_all = "camelCase")]
501pub struct FuturesCancelAllStatus {
502 pub status: KrakenSendStatus,
503 #[serde(default)]
504 pub cancelled_orders: Vec<CancelledOrder>,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize)]
508#[serde(rename_all = "camelCase")]
509pub struct CancelledOrder {
510 #[serde(rename = "order_id", default)]
511 pub order_id: Option<String>,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
518#[serde(rename_all = "camelCase")]
519pub struct FuturesPublicExecutionsResponse {
520 pub elements: Vec<FuturesPublicExecutionElement>,
521 #[serde(default)]
522 pub len: Option<i64>,
523 #[serde(default)]
524 pub continuation_token: Option<String>,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct FuturesPublicExecutionElement {
530 pub uid: String,
531 pub timestamp: i64,
532 pub event: FuturesPublicExecutionEvent,
533}
534
535#[derive(Debug, Clone, Serialize, Deserialize)]
537pub struct FuturesPublicExecutionEvent {
538 #[serde(rename = "Execution")]
539 pub execution: FuturesPublicExecutionWrapper,
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize)]
544#[serde(rename_all = "camelCase")]
545pub struct FuturesPublicExecutionWrapper {
546 pub execution: FuturesPublicExecution,
547 #[serde(default)]
548 pub taker_reduced_quantity: Option<String>,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize)]
553#[serde(rename_all = "camelCase")]
554pub struct FuturesPublicExecution {
555 pub uid: String,
556 pub maker_order: FuturesPublicOrder,
557 pub taker_order: FuturesPublicOrder,
558 pub timestamp: i64,
559 pub quantity: String,
560 pub price: String,
561 #[serde(default)]
562 pub mark_price: Option<String>,
563 #[serde(default)]
564 pub limit_filled: Option<bool>,
565 #[serde(default)]
566 pub usd_value: Option<String>,
567}
568
569#[derive(Debug, Clone, Serialize, Deserialize)]
571#[serde(rename_all = "camelCase")]
572pub struct FuturesPublicOrder {
573 pub uid: String,
574 pub tradeable: String,
575 pub direction: String,
576 pub quantity: String,
577 pub timestamp: i64,
578 #[serde(default)]
579 pub limit_price: Option<String>,
580 #[serde(default)]
581 pub order_type: Option<String>,
582 #[serde(default)]
583 pub reduce_only: Option<bool>,
584 #[serde(default)]
585 pub last_update_timestamp: Option<i64>,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
592#[serde(rename_all = "camelCase")]
593pub struct FuturesAccountsResponse {
594 pub result: KrakenApiResult,
595 #[serde(default)]
596 pub accounts: std::collections::HashMap<String, FuturesAccount>,
597 #[serde(default)]
598 pub error: Option<String>,
599 #[serde(default)]
600 pub server_time: Option<String>,
601}
602
603#[derive(Debug, Clone, Serialize, Deserialize)]
605#[serde(rename_all = "camelCase")]
606pub struct FuturesAccount {
607 #[serde(rename = "type")]
608 pub account_type: String,
609 #[serde(default)]
611 pub balances: std::collections::HashMap<String, f64>,
612 #[serde(default)]
614 pub currencies: std::collections::HashMap<String, FuturesFlexCurrency>,
615 #[serde(default)]
617 pub auxiliary: Option<FuturesAuxiliary>,
618 #[serde(default)]
620 pub margin_requirements: Option<FuturesMarginRequirements>,
621 #[serde(default)]
623 pub portfolio_value: Option<f64>,
624 #[serde(default)]
626 pub available_margin: Option<f64>,
627 #[serde(default)]
629 pub initial_margin: Option<f64>,
630 #[serde(default)]
632 pub pnl: Option<f64>,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize)]
637#[serde(rename_all = "camelCase")]
638pub struct FuturesFlexCurrency {
639 pub quantity: f64,
640 #[serde(default)]
641 pub value: Option<f64>,
642 #[serde(default)]
643 pub collateral: Option<f64>,
644 #[serde(default)]
645 pub available: Option<f64>,
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
650#[serde(rename_all = "camelCase")]
651pub struct FuturesAuxiliary {
652 #[serde(default)]
653 pub usd: Option<f64>,
654 #[serde(default)]
656 pub pv: Option<f64>,
657 #[serde(default)]
659 pub pnl: Option<f64>,
660 #[serde(default)]
662 pub af: Option<f64>,
663 #[serde(default)]
664 pub funding: Option<f64>,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize)]
669#[serde(rename_all = "camelCase")]
670pub struct FuturesMarginRequirements {
671 #[serde(default)]
673 pub im: Option<f64>,
674 #[serde(default)]
676 pub mm: Option<f64>,
677 #[serde(default)]
679 pub lt: Option<f64>,
680 #[serde(default)]
682 pub tt: Option<f64>,
683}
684
685#[cfg(test)]
686mod tests {
687 use rstest::rstest;
688
689 use super::*;
690
691 fn load_test_data(filename: &str) -> String {
692 let path = format!("test_data/{filename}");
693 std::fs::read_to_string(&path)
694 .unwrap_or_else(|e| panic!("Failed to load test data from {path}: {e}"))
695 }
696
697 #[rstest]
698 fn test_parse_futures_open_orders() {
699 let data = load_test_data("http_futures_open_orders.json");
700 let response: FuturesOpenOrdersResponse =
701 serde_json::from_str(&data).expect("Failed to parse futures open orders");
702
703 assert_eq!(response.result, KrakenApiResult::Success);
704 assert_eq!(response.open_orders.len(), 3);
705
706 let order = &response.open_orders[0];
707 assert_eq!(order.order_id, "2ce038ae-c144-4de7-a0f1-82f7f4fca864");
708 assert_eq!(order.symbol, "PI_ETHUSD");
709 assert_eq!(order.side, KrakenOrderSide::Buy);
710 assert_eq!(order.order_type, KrakenFuturesOrderType::Limit);
711 assert_eq!(order.limit_price, Some(1200.0));
712 assert_eq!(order.unfilled_size, 100.0);
713 assert_eq!(order.filled_size, 0.0);
714 }
715
716 #[rstest]
717 fn test_parse_futures_fills() {
718 let data = load_test_data("http_futures_fills.json");
719 let response: FuturesFillsResponse =
720 serde_json::from_str(&data).expect("Failed to parse futures fills");
721
722 assert_eq!(response.result, KrakenApiResult::Success);
723 assert_eq!(response.fills.len(), 3);
724
725 let fill = &response.fills[0];
726 assert_eq!(fill.fill_id, "cad76f07-814e-4dc6-8478-7867407b6bff");
727 assert_eq!(fill.symbol, "PI_XBTUSD");
728 assert_eq!(fill.side, KrakenOrderSide::Buy);
729 assert_eq!(fill.size, 5000.0);
730 assert_eq!(fill.price, 27937.5);
731 assert_eq!(fill.fill_type, KrakenFillType::Maker);
732 assert_eq!(fill.fee_currency, Some("BTC".to_string()));
733 }
734
735 #[rstest]
736 fn test_parse_futures_open_positions() {
737 let data = load_test_data("http_futures_open_positions.json");
738 let response: FuturesOpenPositionsResponse =
739 serde_json::from_str(&data).expect("Failed to parse futures open positions");
740
741 assert_eq!(response.result, KrakenApiResult::Success);
742 assert_eq!(response.open_positions.len(), 2);
743
744 let position = &response.open_positions[0];
745 assert_eq!(position.side, KrakenPositionSide::Short);
746 assert_eq!(position.symbol, "PI_XBTUSD");
747 assert_eq!(position.size, 8000.0);
748 assert!(position.unrealized_funding.is_some());
749 }
750}