1use serde::{Deserialize, Serialize};
4
5use crate::types::{Category, OrderType, PositionIdx, TriggerBy};
6
7
8#[derive(Debug, Clone, Serialize)]
10#[serde(rename_all = "camelCase")]
11pub struct GetPositionInfoParams {
12 pub category: Category,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub symbol: Option<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub base_coin: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub settle_coin: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub limit: Option<u32>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub cursor: Option<String>,
29}
30
31impl GetPositionInfoParams {
32 pub fn new(category: Category) -> Self {
34 Self {
35 category,
36 symbol: None,
37 base_coin: None,
38 settle_coin: None,
39 limit: None,
40 cursor: None,
41 }
42 }
43
44 pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
46 self.symbol = Some(symbol.into());
47 self
48 }
49
50 pub fn base_coin(mut self, coin: impl Into<String>) -> Self {
52 self.base_coin = Some(coin.into());
53 self
54 }
55
56 pub fn settle_coin(mut self, coin: impl Into<String>) -> Self {
58 self.settle_coin = Some(coin.into());
59 self
60 }
61
62 pub fn limit(mut self, limit: u32) -> Self {
64 self.limit = Some(limit);
65 self
66 }
67
68 pub fn cursor(mut self, cursor: impl Into<String>) -> Self {
70 self.cursor = Some(cursor.into());
71 self
72 }
73}
74
75#[derive(Debug, Clone, Serialize)]
77#[serde(rename_all = "camelCase")]
78pub struct SetLeverageParams {
79 pub category: Category,
81 pub symbol: String,
83 pub buy_leverage: String,
85 pub sell_leverage: String,
87}
88
89impl SetLeverageParams {
90 pub fn new(
92 category: Category,
93 symbol: impl Into<String>,
94 buy_leverage: impl Into<String>,
95 sell_leverage: impl Into<String>,
96 ) -> Self {
97 Self {
98 category,
99 symbol: symbol.into(),
100 buy_leverage: buy_leverage.into(),
101 sell_leverage: sell_leverage.into(),
102 }
103 }
104
105 pub fn uniform(category: Category, symbol: impl Into<String>, leverage: impl Into<String>) -> Self {
107 let lev = leverage.into();
108 Self::new(category, symbol, lev.clone(), lev)
109 }
110}
111
112#[derive(Debug, Clone, Serialize)]
114#[serde(rename_all = "camelCase")]
115pub struct SwitchMarginModeParams {
116 pub category: Category,
118 pub symbol: String,
120 pub trade_mode: i32,
122 pub buy_leverage: String,
124 pub sell_leverage: String,
126}
127
128impl SwitchMarginModeParams {
129 pub fn cross_margin(
131 category: Category,
132 symbol: impl Into<String>,
133 buy_leverage: impl Into<String>,
134 sell_leverage: impl Into<String>,
135 ) -> Self {
136 Self {
137 category,
138 symbol: symbol.into(),
139 trade_mode: 0,
140 buy_leverage: buy_leverage.into(),
141 sell_leverage: sell_leverage.into(),
142 }
143 }
144
145 pub fn isolated_margin(
147 category: Category,
148 symbol: impl Into<String>,
149 buy_leverage: impl Into<String>,
150 sell_leverage: impl Into<String>,
151 ) -> Self {
152 Self {
153 category,
154 symbol: symbol.into(),
155 trade_mode: 1,
156 buy_leverage: buy_leverage.into(),
157 sell_leverage: sell_leverage.into(),
158 }
159 }
160}
161
162#[derive(Debug, Clone, Serialize)]
164#[serde(rename_all = "camelCase")]
165pub struct SwitchPositionModeParams {
166 pub category: Category,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub symbol: Option<String>,
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub coin: Option<String>,
174 pub mode: i32,
176}
177
178impl SwitchPositionModeParams {
179 pub fn one_way_by_symbol(category: Category, symbol: impl Into<String>) -> Self {
181 Self {
182 category,
183 symbol: Some(symbol.into()),
184 coin: None,
185 mode: 0,
186 }
187 }
188
189 pub fn hedge_by_symbol(category: Category, symbol: impl Into<String>) -> Self {
191 Self {
192 category,
193 symbol: Some(symbol.into()),
194 coin: None,
195 mode: 3,
196 }
197 }
198
199 pub fn one_way_by_coin(category: Category, coin: impl Into<String>) -> Self {
201 Self {
202 category,
203 symbol: None,
204 coin: Some(coin.into()),
205 mode: 0,
206 }
207 }
208
209 pub fn hedge_by_coin(category: Category, coin: impl Into<String>) -> Self {
211 Self {
212 category,
213 symbol: None,
214 coin: Some(coin.into()),
215 mode: 3,
216 }
217 }
218}
219
220#[derive(Debug, Clone, Serialize)]
222#[serde(rename_all = "camelCase")]
223pub struct SetTradingStopParams {
224 pub category: Category,
226 pub symbol: String,
228 pub position_idx: PositionIdx,
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub take_profit: Option<String>,
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub stop_loss: Option<String>,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub trailing_stop: Option<String>,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub tp_trigger_by: Option<TriggerBy>,
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub sl_trigger_by: Option<TriggerBy>,
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub active_price: Option<String>,
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub tpsl_mode: Option<String>,
251 #[serde(skip_serializing_if = "Option::is_none")]
253 pub tp_size: Option<String>,
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub sl_size: Option<String>,
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub tp_limit_price: Option<String>,
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub sl_limit_price: Option<String>,
263 #[serde(skip_serializing_if = "Option::is_none")]
265 pub tp_order_type: Option<OrderType>,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub sl_order_type: Option<OrderType>,
269}
270
271impl SetTradingStopParams {
272 pub fn new(category: Category, symbol: impl Into<String>, position_idx: PositionIdx) -> Self {
274 Self {
275 category,
276 symbol: symbol.into(),
277 position_idx,
278 take_profit: None,
279 stop_loss: None,
280 trailing_stop: None,
281 tp_trigger_by: None,
282 sl_trigger_by: None,
283 active_price: None,
284 tpsl_mode: None,
285 tp_size: None,
286 sl_size: None,
287 tp_limit_price: None,
288 sl_limit_price: None,
289 tp_order_type: None,
290 sl_order_type: None,
291 }
292 }
293
294 pub fn take_profit(mut self, tp: impl Into<String>) -> Self {
296 self.take_profit = Some(tp.into());
297 self
298 }
299
300 pub fn stop_loss(mut self, sl: impl Into<String>) -> Self {
302 self.stop_loss = Some(sl.into());
303 self
304 }
305
306 pub fn trailing_stop(mut self, ts: impl Into<String>) -> Self {
308 self.trailing_stop = Some(ts.into());
309 self
310 }
311
312 pub fn tp_trigger_by(mut self, by: TriggerBy) -> Self {
314 self.tp_trigger_by = Some(by);
315 self
316 }
317
318 pub fn sl_trigger_by(mut self, by: TriggerBy) -> Self {
320 self.sl_trigger_by = Some(by);
321 self
322 }
323
324 pub fn tpsl_mode(mut self, mode: impl Into<String>) -> Self {
326 self.tpsl_mode = Some(mode.into());
327 self
328 }
329}
330
331#[derive(Debug, Clone, Serialize)]
333#[serde(rename_all = "camelCase")]
334pub struct SetAutoAddMarginParams {
335 pub category: Category,
337 pub symbol: String,
339 pub auto_add_margin: i32,
341 #[serde(skip_serializing_if = "Option::is_none")]
343 pub position_idx: Option<PositionIdx>,
344}
345
346impl SetAutoAddMarginParams {
347 pub fn enable(category: Category, symbol: impl Into<String>) -> Self {
349 Self {
350 category,
351 symbol: symbol.into(),
352 auto_add_margin: 1,
353 position_idx: None,
354 }
355 }
356
357 pub fn disable(category: Category, symbol: impl Into<String>) -> Self {
359 Self {
360 category,
361 symbol: symbol.into(),
362 auto_add_margin: 0,
363 position_idx: None,
364 }
365 }
366
367 pub fn position_idx(mut self, idx: PositionIdx) -> Self {
369 self.position_idx = Some(idx);
370 self
371 }
372}
373
374#[derive(Debug, Clone, Serialize)]
376#[serde(rename_all = "camelCase")]
377pub struct AddReduceMarginParams {
378 pub category: Category,
380 pub symbol: String,
382 pub margin: String,
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub position_idx: Option<PositionIdx>,
387}
388
389impl AddReduceMarginParams {
390 pub fn new(category: Category, symbol: impl Into<String>, margin: impl Into<String>) -> Self {
392 Self {
393 category,
394 symbol: symbol.into(),
395 margin: margin.into(),
396 position_idx: None,
397 }
398 }
399
400 pub fn position_idx(mut self, idx: PositionIdx) -> Self {
402 self.position_idx = Some(idx);
403 self
404 }
405}
406
407#[derive(Debug, Clone, Serialize)]
409#[serde(rename_all = "camelCase")]
410pub struct GetClosedPnlParams {
411 pub category: Category,
413 #[serde(skip_serializing_if = "Option::is_none")]
415 pub symbol: Option<String>,
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub start_time: Option<u64>,
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub end_time: Option<u64>,
422 #[serde(skip_serializing_if = "Option::is_none")]
424 pub limit: Option<u32>,
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub cursor: Option<String>,
428}
429
430impl GetClosedPnlParams {
431 pub fn new(category: Category) -> Self {
433 Self {
434 category,
435 symbol: None,
436 start_time: None,
437 end_time: None,
438 limit: None,
439 cursor: None,
440 }
441 }
442
443 pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
445 self.symbol = Some(symbol.into());
446 self
447 }
448
449 pub fn start_time(mut self, start: u64) -> Self {
451 self.start_time = Some(start);
452 self
453 }
454
455 pub fn end_time(mut self, end: u64) -> Self {
457 self.end_time = Some(end);
458 self
459 }
460
461 pub fn limit(mut self, limit: u32) -> Self {
463 self.limit = Some(limit);
464 self
465 }
466
467 pub fn cursor(mut self, cursor: impl Into<String>) -> Self {
469 self.cursor = Some(cursor.into());
470 self
471 }
472}
473
474
475#[derive(Debug, Clone, Deserialize)]
477#[serde(rename_all = "camelCase")]
478pub struct PositionInfo {
479 pub position_idx: i32,
481 #[serde(default)]
483 pub risk_id: Option<i32>,
484 #[serde(default)]
486 pub risk_limit_value: Option<String>,
487 pub symbol: String,
489 pub side: String,
491 pub size: String,
493 pub avg_price: String,
495 pub position_value: String,
497 pub trade_mode: i32,
499 #[serde(default)]
501 pub auto_add_margin: Option<i32>,
502 #[serde(default)]
504 pub position_status: Option<String>,
505 pub leverage: String,
507 pub mark_price: String,
509 #[serde(default)]
511 pub liq_price: Option<String>,
512 #[serde(default)]
514 pub bust_price: Option<String>,
515 #[serde(default)]
517 pub position_i_m: Option<String>,
518 #[serde(default)]
520 pub position_m_m: Option<String>,
521 #[serde(default)]
523 pub position_balance: Option<String>,
524 #[serde(default)]
526 pub tpsl_mode: Option<String>,
527 #[serde(default)]
529 pub take_profit: Option<String>,
530 #[serde(default)]
532 pub stop_loss: Option<String>,
533 #[serde(default)]
535 pub trailing_stop: Option<String>,
536 #[serde(default)]
538 pub session_avg_price: Option<String>,
539 #[serde(default)]
541 pub delta: Option<String>,
542 #[serde(default)]
544 pub gamma: Option<String>,
545 #[serde(default)]
547 pub vega: Option<String>,
548 #[serde(default)]
550 pub theta: Option<String>,
551 pub unrealised_pnl: String,
553 #[serde(default)]
555 pub cur_realised_pnl: Option<String>,
556 pub cum_realised_pnl: String,
558 #[serde(default)]
560 pub adl_rank_indicator: Option<i32>,
561 #[serde(default)]
563 pub is_reduce_only: Option<bool>,
564 #[serde(default)]
566 pub mmr_sys_updated_time: Option<String>,
567 #[serde(default)]
569 pub leverage_sys_updated_time: Option<String>,
570 pub created_time: String,
572 pub updated_time: String,
574 #[serde(default)]
576 pub seq: Option<i64>,
577}
578
579#[derive(Debug, Clone, Deserialize)]
581#[serde(rename_all = "camelCase")]
582pub struct PositionListResult {
583 pub category: Category,
585 pub list: Vec<PositionInfo>,
587 #[serde(default)]
589 pub next_page_cursor: Option<String>,
590}
591
592#[derive(Debug, Clone, Deserialize)]
594#[serde(rename_all = "camelCase")]
595pub struct ClosedPnl {
596 pub symbol: String,
598 pub order_id: String,
600 pub side: String,
602 pub qty: String,
604 pub order_price: String,
606 pub order_type: String,
608 pub exec_type: String,
610 pub closed_size: String,
612 #[serde(default)]
614 pub open_fee: Option<String>,
615 #[serde(default)]
617 pub close_fee: Option<String>,
618 pub cum_entry_value: String,
620 pub avg_entry_price: String,
622 pub cum_exit_value: String,
624 pub avg_exit_price: String,
626 pub closed_pnl: String,
628 #[serde(default)]
630 pub fill_count: Option<String>,
631 pub leverage: String,
633 pub created_time: String,
635 pub updated_time: String,
637}
638
639#[derive(Debug, Clone, Deserialize)]
641#[serde(rename_all = "camelCase")]
642pub struct ClosedPnlListResult {
643 pub category: Category,
645 pub list: Vec<ClosedPnl>,
647 #[serde(default)]
649 pub next_page_cursor: Option<String>,
650}
651
652#[derive(Debug, Clone, Deserialize)]
654#[serde(rename_all = "camelCase")]
655pub struct MarginOperationResult {
656 pub category: Category,
658 pub symbol: String,
660 pub position_idx: i32,
662 #[serde(default)]
664 pub risk_id: Option<i32>,
665 #[serde(default)]
667 pub risk_limit_value: Option<String>,
668 pub size: String,
670 #[serde(default)]
672 pub avg_price: Option<String>,
673 #[serde(default)]
675 pub liq_price: Option<String>,
676 #[serde(default)]
678 pub bust_price: Option<String>,
679 #[serde(default)]
681 pub mark_price: Option<String>,
682 #[serde(default)]
684 pub position_value: Option<String>,
685 pub leverage: String,
687 #[serde(default)]
689 pub auto_add_margin: Option<i32>,
690 #[serde(default)]
692 pub position_status: Option<String>,
693 #[serde(default)]
695 pub position_i_m: Option<String>,
696 #[serde(default)]
698 pub position_m_m: Option<String>,
699 #[serde(default)]
701 pub take_profit: Option<String>,
702 #[serde(default)]
704 pub stop_loss: Option<String>,
705 #[serde(default)]
707 pub trailing_stop: Option<String>,
708 #[serde(default)]
710 pub unrealised_pnl: Option<String>,
711 #[serde(default)]
713 pub cum_realised_pnl: Option<String>,
714 #[serde(default)]
716 pub created_time: Option<String>,
717 #[serde(default)]
719 pub updated_time: Option<String>,
720}
721
722#[cfg(test)]
723mod tests {
724 use super::*;
725
726 #[test]
727 fn test_get_position_info_params() {
728 let params = GetPositionInfoParams::new(Category::Linear)
729 .symbol("BTCUSDT")
730 .limit(10);
731
732 assert_eq!(params.category, Category::Linear);
733 assert_eq!(params.symbol, Some("BTCUSDT".to_string()));
734 assert_eq!(params.limit, Some(10));
735 }
736
737 #[test]
738 fn test_set_leverage_params() {
739 let params = SetLeverageParams::uniform(Category::Linear, "BTCUSDT", "10");
740 assert_eq!(params.buy_leverage, "10");
741 assert_eq!(params.sell_leverage, "10");
742 }
743
744 #[test]
745 fn test_switch_position_mode_params() {
746 let params = SwitchPositionModeParams::hedge_by_symbol(Category::Linear, "BTCUSDT");
747 assert_eq!(params.mode, 3);
748 assert_eq!(params.symbol, Some("BTCUSDT".to_string()));
749 }
750
751 #[test]
752 fn test_trading_stop_params() {
753 let params = SetTradingStopParams::new(Category::Linear, "BTCUSDT", PositionIdx::OneWay)
754 .take_profit("55000")
755 .stop_loss("45000");
756
757 assert_eq!(params.take_profit, Some("55000".to_string()));
758 assert_eq!(params.stop_loss, Some("45000".to_string()));
759 }
760}