1use rust_decimal::{Decimal, serde::str_option::deserialize as option_decimal};
2use serde::{Deserialize, Serialize};
3use serde_aux::prelude::{
4 deserialize_number_from_string as number,
5 deserialize_option_number_from_string as option_number,
6};
7
8use crate::{
9 AdlRankIndicator, ExecType, OrderType, PositionIdx, PositionMode, PositionStatus, Side,
10 Timestamp, TpslMode, TradeMode, TriggerBy,
11 enums::{Category, StopOrderType},
12 serde::{empty_string_as_none, int_to_bool},
13 ws::PositionMsg,
14};
15
16use super::account::WalletCoin;
17
18#[derive(Clone, Debug, Serialize)]
19#[serde(rename_all = "camelCase")]
20pub struct GetPositionInfoParams {
21 pub category: Category,
25 #[serde(skip_serializing_if = "Option::is_none")]
29 pub symbol: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub base_coin: Option<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
36 pub settle_coin: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub limit: Option<u64>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub cursor: Option<String>,
43}
44
45impl GetPositionInfoParams {
46 pub fn new(category: Category) -> Self {
47 Self {
48 category,
49 symbol: None,
50 base_coin: None,
51 settle_coin: None,
52 limit: None,
53 cursor: None,
54 }
55 }
56
57 pub fn with_symbol(mut self, v: String) -> Self {
58 self.symbol = Some(v);
59 self
60 }
61 pub fn with_base_coin(mut self, v: String) -> Self {
62 self.base_coin = Some(v);
63 self
64 }
65 pub fn with_settle_coin(mut self, v: String) -> Self {
66 self.settle_coin = Some(v);
67 self
68 }
69 pub fn with_limit(mut self, v: u64) -> Self {
70 self.limit = Some(v);
71 self
72 }
73 pub fn with_cursor(mut self, v: String) -> Self {
74 self.cursor = Some(v);
75 self
76 }
77}
78
79#[derive(Debug, Deserialize, PartialEq)]
81#[serde(rename_all = "camelCase")]
82pub struct Position {
83 pub position_idx: PositionIdx,
88 pub risk_id: i64,
91 #[serde(default, deserialize_with = "option_decimal")]
94 pub risk_limit_value: Option<Decimal>,
95 pub symbol: String,
97 #[serde(default, deserialize_with = "empty_string_as_none")]
101 pub side: Option<Side>,
102 pub size: Decimal,
104 pub avg_price: Decimal,
107 #[serde(default, deserialize_with = "option_decimal")]
109 pub position_value: Option<Decimal>,
110 #[serde(deserialize_with = "int_to_bool")]
114 pub auto_add_margin: bool,
115 pub position_status: PositionStatus,
117 pub leverage: Decimal,
120 pub mark_price: Decimal,
122 #[serde(default, deserialize_with = "option_decimal")]
129 pub liq_price: Option<Decimal>,
130 #[serde(rename = "positionIM", default, deserialize_with = "option_decimal")]
134 pub position_im: Option<Decimal>,
135 #[serde(
139 rename = "positionIMByMp",
140 default,
141 deserialize_with = "option_decimal"
142 )]
143 pub position_im_by_mp: Option<Decimal>,
144 #[serde(rename = "positionMM", default, deserialize_with = "option_decimal")]
148 pub position_mm: Option<Decimal>,
149 #[serde(
153 rename = "positionMMByMp",
154 default,
155 deserialize_with = "option_decimal"
156 )]
157 pub position_mm_by_mp: Option<Decimal>,
158 #[serde(default, deserialize_with = "option_decimal")]
160 pub take_profit: Option<Decimal>,
161 #[serde(default, deserialize_with = "option_decimal")]
163 pub stop_loss: Option<Decimal>,
164 #[serde(default, deserialize_with = "option_decimal")]
166 pub trailing_stop: Option<Decimal>,
167 #[serde(default, deserialize_with = "option_decimal")]
169 pub session_avg_price: Option<Decimal>,
170 #[serde(default, deserialize_with = "empty_string_as_none")]
172 pub delta: Option<String>,
173 #[serde(default, deserialize_with = "empty_string_as_none")]
175 pub gamma: Option<String>,
176 #[serde(default, deserialize_with = "empty_string_as_none")]
178 pub vega: Option<String>,
179 #[serde(default, deserialize_with = "empty_string_as_none")]
181 pub theta: Option<String>,
182 #[serde(default, deserialize_with = "option_decimal")]
184 pub unrealised_pnl: Option<Decimal>,
185 pub cur_realised_pnl: Decimal,
187 pub cum_realised_pnl: Decimal,
191 pub adl_rank_indicator: AdlRankIndicator,
193 #[serde(deserialize_with = "number")]
195 pub created_time: Timestamp,
196 #[serde(deserialize_with = "number")]
198 pub updated_time: Timestamp,
199 pub seq: i64,
204 pub is_reduce_only: bool,
209 #[serde(deserialize_with = "option_number")]
216 pub mmr_sys_updated_time: Option<Timestamp>,
217 #[serde(deserialize_with = "option_number")]
224 pub leverage_sys_updated_time: Option<Timestamp>,
225}
226
227impl Position {
228 pub fn update(&mut self, msg: PositionMsg) {
229 self.position_idx = msg.position_idx;
230 self.risk_id = msg.risk_id;
231 self.risk_limit_value = msg.risk_limit_value;
232 self.symbol = msg.symbol;
233 self.side = msg.side;
234 self.size = msg.size;
235 self.avg_price = msg.entry_price;
236 self.position_value = Some(msg.position_value);
237 self.auto_add_margin = msg.auto_add_margin;
238 self.position_status = msg.position_status;
239 self.leverage = msg.leverage;
240 self.mark_price = msg.mark_price;
241 self.liq_price = msg.liq_price;
242 self.take_profit = Some(msg.take_profit);
245 self.stop_loss = Some(msg.stop_loss);
246 self.trailing_stop = Some(msg.trailing_stop);
247 self.session_avg_price = msg.session_avg_price;
249 self.delta = msg.delta;
250 self.gamma = msg.gamma;
251 self.vega = msg.vega;
252 self.theta = msg.theta;
253 self.cur_realised_pnl = msg.cur_realised_pnl;
255 self.cum_realised_pnl = msg.cum_realised_pnl;
256 self.adl_rank_indicator = msg.adl_rank_indicator;
257 self.created_time = msg.created_time;
258 self.updated_time = msg.updated_time;
259 self.seq = msg.seq;
260 self.is_reduce_only = msg.is_reduce_only;
261 self.mmr_sys_updated_time = msg.mmr_sys_updated_time;
262 self.leverage_sys_updated_time = msg.leverage_sys_updated_time;
263 }
264
265 pub fn update_with_a_wallet_coin(&mut self, msg: &WalletCoin) {
266 self.position_mm = msg.total_position_im;
267 self.position_im = msg.total_position_mm;
268 self.unrealised_pnl = Some(msg.unrealised_pnl);
269 }
270}
271
272#[derive(Debug, Serialize)]
275#[serde(rename_all = "camelCase")]
276pub struct SetLeverageRequest {
277 pub category: Category,
279 pub symbol: String,
280 pub buy_leverage: Decimal,
283 pub sell_leverage: Decimal,
284}
285
286impl SetLeverageRequest {
287 pub fn new(category: Category, symbol: String, leverage: Decimal) -> Self {
288 Self {
289 category,
290 symbol,
291 buy_leverage: leverage,
292 sell_leverage: leverage,
293 }
294 }
295
296 pub fn with_asymmetric(mut self, buy_leverage: Decimal, sell_leverage: Decimal) -> Self {
297 self.buy_leverage = buy_leverage;
298 self.sell_leverage = sell_leverage;
299 self
300 }
301}
302
303#[derive(Debug, Serialize)]
306#[serde(rename_all = "camelCase")]
307pub struct SetTradingStopRequest {
308 pub category: Category,
310 pub symbol: String,
311 pub position_idx: PositionIdx,
313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub take_profit: Option<Decimal>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub stop_loss: Option<Decimal>,
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub trailing_stop: Option<Decimal>,
320 #[serde(skip_serializing_if = "Option::is_none")]
321 pub tp_trigger_by: Option<TriggerBy>,
322 #[serde(skip_serializing_if = "Option::is_none")]
323 pub sl_trigger_by: Option<TriggerBy>,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub active_price: Option<Decimal>,
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub tp_size: Option<Decimal>,
330 #[serde(skip_serializing_if = "Option::is_none")]
332 pub sl_size: Option<Decimal>,
333 #[serde(skip_serializing_if = "Option::is_none")]
335 pub tp_limit_price: Option<Decimal>,
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub sl_limit_price: Option<Decimal>,
339 #[serde(skip_serializing_if = "Option::is_none")]
340 pub tp_order_type: Option<OrderType>,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub sl_order_type: Option<OrderType>,
343 #[serde(skip_serializing_if = "Option::is_none")]
344 pub tpsl_mode: Option<TpslMode>,
345}
346
347impl SetTradingStopRequest {
348 pub fn new(category: Category, symbol: String, position_idx: PositionIdx) -> Self {
349 Self {
350 category,
351 symbol,
352 position_idx,
353 take_profit: None,
354 stop_loss: None,
355 trailing_stop: None,
356 tp_trigger_by: None,
357 sl_trigger_by: None,
358 active_price: None,
359 tp_size: None,
360 sl_size: None,
361 tp_limit_price: None,
362 sl_limit_price: None,
363 tp_order_type: None,
364 sl_order_type: None,
365 tpsl_mode: None,
366 }
367 }
368
369 pub fn with_take_profit(mut self, v: Decimal) -> Self {
370 self.take_profit = Some(v);
371 self
372 }
373 pub fn with_stop_loss(mut self, v: Decimal) -> Self {
374 self.stop_loss = Some(v);
375 self
376 }
377 pub fn with_trailing_stop(mut self, v: Decimal) -> Self {
378 self.trailing_stop = Some(v);
379 self
380 }
381 pub fn with_tp_trigger_by(mut self, v: TriggerBy) -> Self {
382 self.tp_trigger_by = Some(v);
383 self
384 }
385 pub fn with_sl_trigger_by(mut self, v: TriggerBy) -> Self {
386 self.sl_trigger_by = Some(v);
387 self
388 }
389 pub fn with_active_price(mut self, v: Decimal) -> Self {
390 self.active_price = Some(v);
391 self
392 }
393 pub fn with_tp_size(mut self, v: Decimal) -> Self {
394 self.tp_size = Some(v);
395 self
396 }
397 pub fn with_sl_size(mut self, v: Decimal) -> Self {
398 self.sl_size = Some(v);
399 self
400 }
401 pub fn with_tp_limit_price(mut self, v: Decimal) -> Self {
402 self.tp_limit_price = Some(v);
403 self
404 }
405 pub fn with_sl_limit_price(mut self, v: Decimal) -> Self {
406 self.sl_limit_price = Some(v);
407 self
408 }
409 pub fn with_tp_order_type(mut self, v: OrderType) -> Self {
410 self.tp_order_type = Some(v);
411 self
412 }
413 pub fn with_sl_order_type(mut self, v: OrderType) -> Self {
414 self.sl_order_type = Some(v);
415 self
416 }
417 pub fn with_tpsl_mode(mut self, v: TpslMode) -> Self {
418 self.tpsl_mode = Some(v);
419 self
420 }
421}
422
423#[derive(Debug, Serialize)]
426#[serde(rename_all = "camelCase")]
427pub struct SwitchCrossIsolatedMarginRequest {
428 pub category: Category,
430 pub symbol: String,
431 pub trade_mode: TradeMode,
433 pub buy_leverage: Decimal,
434 pub sell_leverage: Decimal,
435}
436
437impl SwitchCrossIsolatedMarginRequest {
438 pub fn cross(category: Category, symbol: String, leverage: Decimal) -> Self {
439 Self {
440 category,
441 symbol,
442 trade_mode: TradeMode::CrossMargin,
443 buy_leverage: leverage,
444 sell_leverage: leverage,
445 }
446 }
447
448 pub fn isolated(
449 category: Category,
450 symbol: String,
451 buy_leverage: Decimal,
452 sell_leverage: Decimal,
453 ) -> Self {
454 Self {
455 category,
456 symbol,
457 trade_mode: TradeMode::IsolatedMargin,
458 buy_leverage,
459 sell_leverage,
460 }
461 }
462}
463
464#[derive(Debug, Serialize)]
467#[serde(rename_all = "camelCase")]
468pub struct SwitchPositionModeRequest {
469 pub category: Category,
471 #[serde(skip_serializing_if = "Option::is_none")]
473 pub symbol: Option<String>,
474 #[serde(skip_serializing_if = "Option::is_none")]
476 pub coin: Option<String>,
477 pub mode: PositionMode,
479}
480
481impl SwitchPositionModeRequest {
482 pub fn one_way(category: Category, symbol: String) -> Self {
483 Self {
484 category,
485 symbol: Some(symbol),
486 coin: None,
487 mode: PositionMode::OneWay,
488 }
489 }
490
491 pub fn hedge(category: Category, symbol: String) -> Self {
492 Self {
493 category,
494 symbol: Some(symbol),
495 coin: None,
496 mode: PositionMode::Hedge,
497 }
498 }
499
500 pub fn with_coin(mut self, v: String) -> Self {
501 self.symbol = None;
502 self.coin = Some(v);
503 self
504 }
505}
506
507#[derive(Debug, Serialize)]
510#[serde(rename_all = "camelCase")]
511pub struct SetAutoAddMarginRequest {
512 pub category: Category,
514 pub symbol: String,
515 pub auto_add_margin: i32,
517 #[serde(skip_serializing_if = "Option::is_none")]
518 pub position_idx: Option<PositionIdx>,
519}
520
521impl SetAutoAddMarginRequest {
522 pub fn new(category: Category, symbol: String, enabled: bool) -> Self {
523 Self {
524 category,
525 symbol,
526 auto_add_margin: if enabled { 1 } else { 0 },
527 position_idx: None,
528 }
529 }
530
531 pub fn with_position_idx(mut self, v: PositionIdx) -> Self {
532 self.position_idx = Some(v);
533 self
534 }
535}
536
537#[derive(Debug, Serialize)]
540#[serde(rename_all = "camelCase")]
541pub struct SetRiskLimitRequest {
542 pub category: Category,
544 pub symbol: String,
545 pub risk_id: i64,
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub position_idx: Option<PositionIdx>,
548}
549
550impl SetRiskLimitRequest {
551 pub fn new(category: Category, symbol: String, risk_id: i64) -> Self {
552 Self {
553 category,
554 symbol,
555 risk_id,
556 position_idx: None,
557 }
558 }
559
560 pub fn with_position_idx(mut self, v: PositionIdx) -> Self {
561 self.position_idx = Some(v);
562 self
563 }
564}
565
566#[derive(Debug, Deserialize, PartialEq)]
567#[serde(rename_all = "camelCase")]
568pub struct SetRiskLimitResponse {
569 pub risk_id: i64,
570 pub risk_limit_value: String,
571 pub category: String,
572 #[serde(default, deserialize_with = "empty_string_as_none")]
573 pub message: Option<String>,
574}
575
576#[derive(Clone, Debug, Serialize)]
579#[serde(rename_all = "camelCase")]
580pub struct GetClosedPnlParams {
581 pub category: Category,
583 #[serde(skip_serializing_if = "Option::is_none")]
585 pub symbol: Option<String>,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub start_time: Option<Timestamp>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub end_time: Option<Timestamp>,
590 #[serde(skip_serializing_if = "Option::is_none")]
592 pub limit: Option<i32>,
593 #[serde(skip_serializing_if = "Option::is_none")]
594 pub cursor: Option<String>,
595}
596
597impl GetClosedPnlParams {
598 pub fn new(category: Category) -> Self {
599 Self {
600 category,
601 symbol: None,
602 start_time: None,
603 end_time: None,
604 limit: None,
605 cursor: None,
606 }
607 }
608
609 pub fn with_symbol(mut self, v: String) -> Self {
610 self.symbol = Some(v);
611 self
612 }
613 pub fn with_start_time(mut self, v: Timestamp) -> Self {
614 self.start_time = Some(v);
615 self
616 }
617 pub fn with_end_time(mut self, v: Timestamp) -> Self {
618 self.end_time = Some(v);
619 self
620 }
621 pub fn with_limit(mut self, v: i32) -> Self {
622 self.limit = Some(v);
623 self
624 }
625 pub fn with_cursor(mut self, v: String) -> Self {
626 self.cursor = Some(v);
627 self
628 }
629}
630
631#[derive(Debug, Deserialize, PartialEq)]
632#[serde(rename_all = "camelCase")]
633pub struct ClosedPnl {
634 pub symbol: String,
635 pub order_id: String,
636 #[serde(default, deserialize_with = "empty_string_as_none")]
637 pub order_link_id: Option<String>,
638 pub side: Side,
639 #[serde(deserialize_with = "number")]
640 pub qty: Decimal,
641 #[serde(deserialize_with = "number")]
642 pub order_price: Decimal,
643 pub order_type: OrderType,
644 pub exec_type: ExecType,
645 #[serde(deserialize_with = "number")]
646 pub closed_size: Decimal,
647 pub cum_entry_value: Decimal,
648 pub avg_entry_price: Decimal,
649 pub cum_exit_value: Decimal,
650 pub avg_exit_price: Decimal,
651 pub closed_pnl: Decimal,
652 #[serde(deserialize_with = "number")]
653 pub fill_count: i64,
654 pub leverage: Decimal,
655 #[serde(deserialize_with = "number")]
656 pub created_time: Timestamp,
657 #[serde(deserialize_with = "number")]
658 pub updated_time: Timestamp,
659}
660
661#[derive(Clone, Debug, Serialize)]
664#[serde(rename_all = "camelCase")]
665pub struct GetExecutionListParams {
666 pub category: Category,
667 #[serde(skip_serializing_if = "Option::is_none")]
668 pub symbol: Option<String>,
669 #[serde(skip_serializing_if = "Option::is_none")]
670 pub order_id: Option<String>,
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub order_link_id: Option<String>,
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub base_coin: Option<String>,
675 #[serde(skip_serializing_if = "Option::is_none")]
676 pub start_time: Option<Timestamp>,
677 #[serde(skip_serializing_if = "Option::is_none")]
678 pub end_time: Option<Timestamp>,
679 #[serde(skip_serializing_if = "Option::is_none")]
680 pub exec_type: Option<ExecType>,
681 #[serde(skip_serializing_if = "Option::is_none")]
683 pub limit: Option<i32>,
684 #[serde(skip_serializing_if = "Option::is_none")]
685 pub cursor: Option<String>,
686}
687
688impl GetExecutionListParams {
689 pub fn new(category: Category) -> Self {
690 Self {
691 category,
692 symbol: None,
693 order_id: None,
694 order_link_id: None,
695 base_coin: None,
696 start_time: None,
697 end_time: None,
698 exec_type: None,
699 limit: None,
700 cursor: None,
701 }
702 }
703
704 pub fn with_symbol(mut self, v: String) -> Self {
705 self.symbol = Some(v);
706 self
707 }
708 pub fn with_order_id(mut self, v: String) -> Self {
709 self.order_id = Some(v);
710 self
711 }
712 pub fn with_order_link_id(mut self, v: String) -> Self {
713 self.order_link_id = Some(v);
714 self
715 }
716 pub fn with_base_coin(mut self, v: String) -> Self {
717 self.base_coin = Some(v);
718 self
719 }
720 pub fn with_start_time(mut self, v: Timestamp) -> Self {
721 self.start_time = Some(v);
722 self
723 }
724 pub fn with_end_time(mut self, v: Timestamp) -> Self {
725 self.end_time = Some(v);
726 self
727 }
728 pub fn with_exec_type(mut self, v: ExecType) -> Self {
729 self.exec_type = Some(v);
730 self
731 }
732 pub fn with_limit(mut self, v: i32) -> Self {
733 self.limit = Some(v);
734 self
735 }
736 pub fn with_cursor(mut self, v: String) -> Self {
737 self.cursor = Some(v);
738 self
739 }
740}
741
742#[derive(Debug, Deserialize, PartialEq)]
743#[serde(rename_all = "camelCase")]
744pub struct ExecutionEntry {
745 pub symbol: String,
746 pub order_id: String,
747 #[serde(default, deserialize_with = "empty_string_as_none")]
748 pub order_link_id: Option<String>,
749 pub side: Side,
750 pub order_price: Decimal,
751 pub order_qty: Decimal,
752 pub order_type: OrderType,
753 #[serde(default, deserialize_with = "empty_string_as_none")]
754 pub stop_order_type: Option<StopOrderType>,
755 pub exec_fee: Decimal,
756 pub exec_id: String,
757 pub exec_price: Decimal,
758 pub exec_qty: Decimal,
759 pub exec_type: ExecType,
760 pub exec_value: Decimal,
761 #[serde(deserialize_with = "number")]
762 pub exec_time: Timestamp,
763 pub fee_rate: Decimal,
764 #[serde(default, deserialize_with = "option_decimal")]
765 pub trade_iv: Option<Decimal>,
766 #[serde(default, deserialize_with = "option_decimal")]
767 pub mark_iv: Option<Decimal>,
768 pub mark_price: Decimal,
769 #[serde(default, deserialize_with = "option_decimal")]
770 pub index_price: Option<Decimal>,
771 #[serde(default, deserialize_with = "option_decimal")]
772 pub underlying_price: Option<Decimal>,
773 #[serde(default, deserialize_with = "empty_string_as_none")]
774 pub block_trade_id: Option<String>,
775 pub closed_size: Decimal,
776 pub seq: i64,
777 pub is_maker: bool,
778}