1use std::collections::HashMap;
2
3use rust_decimal::{Decimal, serde::str_option::deserialize as option_decimal};
4use serde::{Deserialize, Serialize};
5use serde_aux::prelude::deserialize_number_from_string as number;
6
7use crate::{
8 AccountType, DCPProduct, MarginMode, Second, SpotHedgingStatus, Timestamp, UnifiedMarginStatus,
9 UnifiedUpdateStatus,
10 enums::Category,
11 serde::{Unique, empty_string_as_none, hash_map},
12 ws::WalletMsg,
13};
14
15#[derive(Debug, Serialize, Clone)]
16#[serde(rename_all = "camelCase")]
17pub struct GetWalletBalanceParams {
18 pub account_type: AccountType,
24 pub coin: Option<String>,
28}
29
30impl GetWalletBalanceParams {
31 pub fn new(account_type: AccountType) -> Self {
32 Self {
33 account_type,
34 coin: None,
35 }
36 }
37
38 pub fn with_coin(mut self, v: impl Into<String>) -> Self {
39 self.coin = Some(v.into());
40 self
41 }
42}
43
44#[derive(Debug, Deserialize, PartialEq)]
45#[serde(rename_all = "camelCase")]
46pub struct WalletBalance {
47 pub account_type: AccountType,
49 #[serde(rename = "accountIMRate")]
56 pub account_im_rate: Decimal,
57 #[serde(rename = "accountIMRateByMp")]
59 pub account_im_rate_by_mp: Decimal,
60 #[serde(rename = "accountMMRate")]
62 pub account_mm_rate: Decimal,
63 #[serde(rename = "accountMMRateByMp")]
65 pub account_mm_rate_by_mp: Decimal,
66 pub total_equity: Decimal,
68 pub total_wallet_balance: Decimal,
70 pub total_margin_balance: Decimal,
72 pub total_available_balance: Decimal,
74 #[serde(rename = "totalPerpUPL")]
76 pub total_perp_upl: Decimal,
77 pub total_initial_margin: Decimal,
79 pub total_initial_margin_by_mp: Decimal,
81 pub total_maintenance_margin: Decimal,
83 pub total_maintenance_margin_by_mp: Decimal,
85 #[serde(deserialize_with = "hash_map")]
86 pub coin: HashMap<String, WalletCoin>,
87}
88
89impl WalletBalance {
90 pub fn update(&mut self, msg: WalletMsg) {
93 *self = msg.into();
94 }
95}
96
97impl From<WalletMsg> for WalletBalance {
98 fn from(msg: WalletMsg) -> Self {
99 Self {
100 account_type: msg.account_type,
101 account_im_rate: msg.account_im_rate,
102 account_im_rate_by_mp: msg.account_im_rate_by_mp,
103 account_mm_rate: msg.account_mm_rate,
104 account_mm_rate_by_mp: msg.account_mm_rate_by_mp,
105 total_equity: msg.total_equity,
106 total_wallet_balance: msg.total_wallet_balance,
107 total_margin_balance: msg.total_margin_balance,
108 total_available_balance: msg.total_available_balance,
109 total_perp_upl: msg.total_perp_upl,
110 total_initial_margin: msg.total_initial_margin,
111 total_initial_margin_by_mp: msg.total_initial_margin_by_mp,
112 total_maintenance_margin: msg.total_maintenance_margin,
113 total_maintenance_margin_by_mp: msg.total_maintenance_margin_by_mp,
114 coin: msg.coin,
115 }
116 }
117}
118
119#[derive(Debug, Deserialize, PartialEq, Clone)]
120#[serde(rename_all = "camelCase")]
121pub struct WalletCoin {
122 pub coin: String,
124 pub equity: Decimal,
126 pub usd_value: Decimal,
128 pub wallet_balance: Decimal,
130 pub locked: Decimal,
132 pub spot_hedging_qty: Decimal,
134 pub borrow_amount: Decimal,
136 pub accrued_interest: Decimal,
138 #[serde(rename = "totalOrderIM", default, deserialize_with = "option_decimal")]
140 pub total_order_im: Option<Decimal>,
141 #[serde(
143 rename = "totalPositionIM",
144 default,
145 deserialize_with = "option_decimal"
146 )]
147 pub total_position_im: Option<Decimal>,
148 #[serde(
150 rename = "totalPositionMM",
151 default,
152 deserialize_with = "option_decimal"
153 )]
154 pub total_position_mm: Option<Decimal>,
155 pub unrealised_pnl: Decimal,
157 pub cum_realised_pnl: Decimal,
159 pub bonus: Decimal,
161 pub collateral_switch: bool,
164 pub margin_collateral: bool,
167 #[serde(default, deserialize_with = "option_decimal")]
169 pub spot_borrow: Option<Decimal>,
170}
171
172impl Unique<String> for WalletCoin {
173 fn unique_key(&self) -> String {
174 self.coin.clone()
175 }
176}
177
178#[derive(Debug, Deserialize, PartialEq)]
179#[serde(rename_all = "camelCase")]
180pub struct AccountInfo {
181 pub unified_margin_status: UnifiedMarginStatus,
183 pub margin_mode: MarginMode,
185 pub is_master_trader: bool,
187 pub spot_hedging_status: SpotHedgingStatus,
189 #[serde(deserialize_with = "number")]
191 pub updated_time: Timestamp,
192}
193
194#[derive(Debug, Deserialize, PartialEq)]
195#[serde(rename_all = "camelCase")]
196pub struct DCPConfiguration {
197 pub dcp_infos: Vec<DCPInfo>,
199}
200
201#[derive(Debug, Deserialize, PartialEq)]
202#[serde(rename_all = "camelCase")]
203pub struct DCPInfo {
204 pub product: DCPProduct,
206 #[serde(rename = "dcpStatus")]
208 pub status: String,
209 #[serde(deserialize_with = "number")]
211 pub time_window: Second,
212}
213
214#[derive(Clone, Debug, Serialize)]
215#[serde(rename_all = "camelCase")]
216pub struct GetTransactionLogParams {
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub account_type: Option<AccountType>,
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub category: Option<Category>,
223 #[serde(skip_serializing_if = "Option::is_none")]
225 pub currency: Option<String>,
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub base_coin: Option<String>,
229 #[serde(skip_serializing_if = "Option::is_none")]
233 pub settle_coin: Option<String>,
234 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
236 pub r#type: Option<String>,
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub trans_sub_type: Option<String>,
240 #[serde(skip_serializing_if = "Option::is_none")]
246 pub start_time: Option<Timestamp>,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub end_time: Option<Timestamp>,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub limit: Option<u64>,
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub cursor: Option<String>,
256}
257
258impl GetTransactionLogParams {
259 pub fn new() -> Self {
260 Self {
261 account_type: None,
262 category: None,
263 currency: None,
264 base_coin: None,
265 settle_coin: None,
266 r#type: None,
267 trans_sub_type: None,
268 start_time: None,
269 end_time: None,
270 limit: None,
271 cursor: None,
272 }
273 }
274
275 pub fn with_account_type(mut self, v: AccountType) -> Self {
276 self.account_type = Some(v);
277 self
278 }
279 pub fn with_category(mut self, v: Category) -> Self {
280 self.category = Some(v);
281 self
282 }
283 pub fn with_currency(mut self, v: String) -> Self {
284 self.currency = Some(v);
285 self
286 }
287 pub fn with_base_coin(mut self, v: String) -> Self {
288 self.base_coin = Some(v);
289 self
290 }
291 pub fn with_settle_coin(mut self, v: String) -> Self {
292 self.settle_coin = Some(v);
293 self
294 }
295 pub fn with_type(mut self, v: String) -> Self {
296 self.r#type = Some(v);
297 self
298 }
299 pub fn with_trans_sub_type(mut self, v: String) -> Self {
300 self.trans_sub_type = Some(v);
301 self
302 }
303 pub fn with_start_time(mut self, v: Timestamp) -> Self {
304 self.start_time = Some(v);
305 self
306 }
307 pub fn with_end_time(mut self, v: Timestamp) -> Self {
308 self.end_time = Some(v);
309 self
310 }
311 pub fn with_limit(mut self, v: u64) -> Self {
312 self.limit = Some(v);
313 self
314 }
315 pub fn with_cursor(mut self, v: String) -> Self {
316 self.cursor = Some(v);
317 self
318 }
319}
320
321impl Default for GetTransactionLogParams {
322 fn default() -> Self {
323 Self::new()
324 }
325}
326
327#[derive(Debug, Deserialize, PartialEq)]
328#[serde(rename_all = "camelCase")]
329pub struct TransactionLog {
330 pub id: String,
332 pub symbol: String,
334 pub category: Category,
336 pub side: crate::Side,
338 #[serde(deserialize_with = "number")]
340 pub transaction_time: Timestamp,
341 #[serde(rename = "type")]
343 pub r#type: String,
344 pub trans_sub_type: String,
346 pub qty: Decimal,
350 pub size: Decimal,
352 pub currency: String,
354 pub trade_price: Decimal,
356 pub funding: Decimal,
360 pub fee: Decimal,
364 pub cash_flow: Decimal,
366 pub change: Decimal,
368 pub cash_balance: Decimal,
370 pub fee_rate: Decimal,
374 pub bonus_change: Decimal,
376 pub trade_id: String,
378 pub order_id: String,
380 #[serde(default, deserialize_with = "crate::serde::empty_string_as_none")]
382 pub order_link_id: Option<String>,
383 #[serde(default)]
385 pub extra_fees: Option<serde_json::Value>,
386}
387
388#[derive(Debug, Serialize, Clone)]
392#[serde(rename_all = "camelCase")]
393pub struct GetFeeRateParams {
394 pub category: Category,
395 pub symbol: Option<String>,
396 pub base_coin: Option<String>,
398}
399
400impl GetFeeRateParams {
401 pub fn new(category: Category) -> Self {
402 Self {
403 category,
404 symbol: None,
405 base_coin: None,
406 }
407 }
408
409 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
410 self.symbol = Some(v.into());
411 self
412 }
413
414 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
415 self.base_coin = Some(v.into());
416 self
417 }
418}
419
420#[derive(Debug, Deserialize, PartialEq)]
421#[serde(rename_all = "camelCase")]
422pub struct FeeRateEntry {
423 #[serde(default, deserialize_with = "empty_string_as_none")]
425 pub symbol: Option<String>,
426 #[serde(default, deserialize_with = "empty_string_as_none")]
428 pub base_coin: Option<String>,
429 pub taker_fee_rate: Decimal,
430 pub maker_fee_rate: Decimal,
431}
432
433#[derive(Debug, Serialize, Clone)]
437#[serde(rename_all = "camelCase")]
438pub struct SetMarginModeRequest {
439 pub set_margin_mode: MarginMode,
440}
441
442impl SetMarginModeRequest {
443 pub fn new(set_margin_mode: MarginMode) -> Self {
444 Self { set_margin_mode }
445 }
446}
447
448#[derive(Debug, Deserialize, PartialEq)]
453pub struct SetMarginModeResponse {
454 pub reasons: Vec<MarginModeFailureReason>,
455}
456
457#[derive(Debug, Deserialize, PartialEq)]
458#[serde(rename_all = "camelCase")]
459pub struct MarginModeFailureReason {
460 pub reason_code: String,
461 pub reason_msg: String,
462}
463
464#[derive(Debug, Deserialize, PartialEq)]
468#[serde(rename_all = "camelCase")]
469pub struct UpgradeToUtaResult {
470 pub unified_update_status: UnifiedUpdateStatus,
471 pub unified_update_msg: Option<UnifiedUpdateMsg>,
473}
474
475#[derive(Debug, Deserialize, PartialEq)]
476#[serde(rename_all = "camelCase")]
477pub struct UnifiedUpdateMsg {
478 pub msg: Vec<String>,
479}
480
481#[derive(Clone, Debug, Serialize)]
485#[serde(rename_all = "camelCase")]
486pub struct GetBorrowHistoryParams {
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub currency: Option<String>,
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub start_time: Option<Timestamp>,
493 #[serde(skip_serializing_if = "Option::is_none")]
495 pub end_time: Option<Timestamp>,
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub limit: Option<u64>,
499 #[serde(skip_serializing_if = "Option::is_none")]
501 pub cursor: Option<String>,
502}
503
504impl GetBorrowHistoryParams {
505 pub fn new() -> Self {
506 Self {
507 currency: None,
508 start_time: None,
509 end_time: None,
510 limit: None,
511 cursor: None,
512 }
513 }
514
515 pub fn with_currency(mut self, v: String) -> Self {
516 self.currency = Some(v);
517 self
518 }
519 pub fn with_start_time(mut self, v: Timestamp) -> Self {
520 self.start_time = Some(v);
521 self
522 }
523 pub fn with_end_time(mut self, v: Timestamp) -> Self {
524 self.end_time = Some(v);
525 self
526 }
527 pub fn with_limit(mut self, v: u64) -> Self {
528 self.limit = Some(v);
529 self
530 }
531 pub fn with_cursor(mut self, v: String) -> Self {
532 self.cursor = Some(v);
533 self
534 }
535}
536
537impl Default for GetBorrowHistoryParams {
538 fn default() -> Self {
539 Self::new()
540 }
541}
542
543#[derive(Debug, Deserialize, PartialEq)]
544#[serde(rename_all = "camelCase")]
545pub struct BorrowHistoryEntry {
546 pub currency: String,
548 #[serde(deserialize_with = "number")]
550 pub created_time: Timestamp,
551 pub borrow_cost: Decimal,
553 pub hourly_borrow_rate: Decimal,
555 #[serde(rename = "InterestBearingBorrowSize")]
557 pub interest_bearing_borrow_size: Decimal,
558 pub cost_exemption: Decimal,
560 pub borrow_amount: Decimal,
562 pub unrealised_loss: Decimal,
564 #[serde(default, deserialize_with = "option_decimal")]
566 pub free_borrowed_amount: Option<Decimal>,
567}
568
569#[derive(Debug, Serialize, Clone)]
573#[serde(rename_all = "camelCase")]
574pub struct GetCollateralInfoParams {
575 #[serde(skip_serializing_if = "Option::is_none")]
577 pub currency: Option<String>,
578}
579
580impl GetCollateralInfoParams {
581 pub fn new() -> Self {
582 Self { currency: None }
583 }
584
585 pub fn with_currency(mut self, v: String) -> Self {
586 self.currency = Some(v);
587 self
588 }
589}
590
591impl Default for GetCollateralInfoParams {
592 fn default() -> Self {
593 Self::new()
594 }
595}
596
597#[derive(Debug, Deserialize, PartialEq)]
598#[serde(rename_all = "camelCase")]
599pub struct CollateralInfoEntry {
600 pub currency: String,
602 pub hourly_borrow_rate: Decimal,
604 pub max_borrowing_amount: Decimal,
606 pub free_borrowing_limit: Decimal,
608 pub free_borrow_amount: Decimal,
610 pub borrow_amount: Decimal,
612 pub other_borrow_amount: Decimal,
614 pub available_to_borrow: Decimal,
616 pub borrowable: bool,
618 pub borrow_usage_rate: Decimal,
620 pub margin_collateral: bool,
622 pub collateral_switch: bool,
624 #[serde(default, deserialize_with = "option_decimal")]
626 pub collateral_ratio: Option<Decimal>,
627 #[serde(default, deserialize_with = "empty_string_as_none")]
629 pub free_borrowing_amount: Option<String>,
630}