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
30#[derive(Debug, Deserialize, PartialEq)]
31#[serde(rename_all = "camelCase")]
32pub struct WalletBalance {
33 pub account_type: AccountType,
35 #[serde(rename = "accountIMRate")]
42 pub account_im_rate: Decimal,
43 #[serde(rename = "accountIMRateByMp")]
45 pub account_im_rate_by_mp: Decimal,
46 #[serde(rename = "accountMMRate")]
48 pub account_mm_rate: Decimal,
49 #[serde(rename = "accountMMRateByMp")]
51 pub account_mm_rate_by_mp: Decimal,
52 pub total_equity: Decimal,
54 pub total_wallet_balance: Decimal,
56 pub total_margin_balance: Decimal,
58 pub total_available_balance: Decimal,
60 #[serde(rename = "totalPerpUPL")]
62 pub total_perp_upl: Decimal,
63 pub total_initial_margin: Decimal,
65 pub total_initial_margin_by_mp: Decimal,
67 pub total_maintenance_margin: Decimal,
69 pub total_maintenance_margin_by_mp: Decimal,
71 #[serde(deserialize_with = "hash_map")]
72 pub coin: HashMap<String, WalletCoin>,
73}
74
75impl WalletBalance {
76 pub fn update(&mut self, msg: WalletMsg) {
77 self.account_type = msg.account_type;
78 self.account_im_rate = msg.account_im_rate;
79 self.account_mm_rate = msg.account_mm_rate;
80 self.total_equity = msg.total_equity;
81 self.total_wallet_balance = msg.total_wallet_balance;
82 self.total_margin_balance = msg.total_margin_balance;
83 self.total_available_balance = msg.total_available_balance;
84 self.total_perp_upl = msg.total_perp_upl;
85 self.total_initial_margin = msg.total_initial_margin;
86 self.total_maintenance_margin = msg.total_maintenance_margin;
87 self.coin = msg.coin;
88 }
89}
90
91#[derive(Debug, Deserialize, PartialEq, Clone)]
92#[serde(rename_all = "camelCase")]
93pub struct WalletCoin {
94 pub coin: String,
96 pub equity: Decimal,
98 pub usd_value: Decimal,
100 pub wallet_balance: Decimal,
102 pub locked: Decimal,
104 pub spot_hedging_qty: Decimal,
106 pub borrow_amount: Decimal,
108 pub accrued_interest: Decimal,
110 #[serde(rename = "totalOrderIM", default, deserialize_with = "option_decimal")]
112 pub total_order_im: Option<Decimal>,
113 #[serde(
115 rename = "totalPositionIM",
116 default,
117 deserialize_with = "option_decimal"
118 )]
119 pub total_position_im: Option<Decimal>,
120 #[serde(
122 rename = "totalPositionMM",
123 default,
124 deserialize_with = "option_decimal"
125 )]
126 pub total_position_mm: Option<Decimal>,
127 pub unrealised_pnl: Decimal,
129 pub cum_realised_pnl: Decimal,
131 pub bonus: Decimal,
133 pub collateral_switch: bool,
136 pub margin_collateral: bool,
139 #[serde(default, deserialize_with = "option_decimal")]
141 pub spot_borrow: Option<Decimal>,
142}
143
144impl Unique<String> for WalletCoin {
145 fn unique_key(&self) -> String {
146 self.coin.clone()
147 }
148}
149
150#[derive(Debug, Deserialize, PartialEq)]
151#[serde(rename_all = "camelCase")]
152pub struct AccountInfo {
153 pub unified_margin_status: UnifiedMarginStatus,
155 pub margin_mode: MarginMode,
157 pub is_master_trader: bool,
159 pub spot_hedging_status: SpotHedgingStatus,
161 #[serde(deserialize_with = "number")]
163 pub updated_time: Timestamp,
164}
165
166#[derive(Debug, Deserialize, PartialEq)]
167#[serde(rename_all = "camelCase")]
168pub struct DCPConfiguration {
169 pub dcp_infos: Vec<DCPInfo>,
171}
172
173#[derive(Debug, Deserialize, PartialEq)]
174#[serde(rename_all = "camelCase")]
175pub struct DCPInfo {
176 pub product: DCPProduct,
178 #[serde(rename = "dcpStatus")]
180 pub status: String,
181 #[serde(deserialize_with = "number")]
183 pub time_window: Second,
184}
185
186#[derive(Clone, Debug, Serialize)]
187#[serde(rename_all = "camelCase")]
188pub struct GetTransactionLogParams {
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub account_type: Option<AccountType>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub category: Option<Category>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub currency: Option<String>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub base_coin: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
205 pub settle_coin: Option<String>,
206 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
208 pub r#type: Option<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub trans_sub_type: Option<String>,
212 #[serde(skip_serializing_if = "Option::is_none")]
218 pub start_time: Option<Timestamp>,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub end_time: Option<Timestamp>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub limit: Option<u64>,
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub cursor: Option<String>,
228}
229
230impl GetTransactionLogParams {
231 pub fn new() -> Self {
232 Self {
233 account_type: None,
234 category: None,
235 currency: None,
236 base_coin: None,
237 settle_coin: None,
238 r#type: None,
239 trans_sub_type: None,
240 start_time: None,
241 end_time: None,
242 limit: None,
243 cursor: None,
244 }
245 }
246
247 pub fn with_account_type(mut self, v: AccountType) -> Self {
248 self.account_type = Some(v);
249 self
250 }
251 pub fn with_category(mut self, v: Category) -> Self {
252 self.category = Some(v);
253 self
254 }
255 pub fn with_currency(mut self, v: String) -> Self {
256 self.currency = Some(v);
257 self
258 }
259 pub fn with_base_coin(mut self, v: String) -> Self {
260 self.base_coin = Some(v);
261 self
262 }
263 pub fn with_settle_coin(mut self, v: String) -> Self {
264 self.settle_coin = Some(v);
265 self
266 }
267 pub fn with_type(mut self, v: String) -> Self {
268 self.r#type = Some(v);
269 self
270 }
271 pub fn with_trans_sub_type(mut self, v: String) -> Self {
272 self.trans_sub_type = Some(v);
273 self
274 }
275 pub fn with_start_time(mut self, v: Timestamp) -> Self {
276 self.start_time = Some(v);
277 self
278 }
279 pub fn with_end_time(mut self, v: Timestamp) -> Self {
280 self.end_time = Some(v);
281 self
282 }
283 pub fn with_limit(mut self, v: u64) -> Self {
284 self.limit = Some(v);
285 self
286 }
287 pub fn with_cursor(mut self, v: String) -> Self {
288 self.cursor = Some(v);
289 self
290 }
291}
292
293impl Default for GetTransactionLogParams {
294 fn default() -> Self {
295 Self::new()
296 }
297}
298
299#[derive(Debug, Deserialize, PartialEq)]
300#[serde(rename_all = "camelCase")]
301pub struct TransactionLog {
302 pub id: String,
304 pub symbol: String,
306 pub category: Category,
308 pub side: crate::Side,
310 #[serde(deserialize_with = "number")]
312 pub transaction_time: Timestamp,
313 #[serde(rename = "type")]
315 pub r#type: String,
316 pub trans_sub_type: String,
318 pub qty: Decimal,
322 pub size: Decimal,
324 pub currency: String,
326 pub trade_price: Decimal,
328 pub funding: Decimal,
332 pub fee: Decimal,
336 pub cash_flow: Decimal,
338 pub change: Decimal,
340 pub cash_balance: Decimal,
342 pub fee_rate: Decimal,
346 pub bonus_change: Decimal,
348 pub trade_id: String,
350 pub order_id: String,
352 #[serde(default, deserialize_with = "crate::serde::empty_string_as_none")]
354 pub order_link_id: Option<String>,
355 #[serde(default)]
357 pub extra_fees: Option<serde_json::Value>,
358}
359
360#[derive(Debug, Serialize, Clone)]
364#[serde(rename_all = "camelCase")]
365pub struct GetFeeRateParams {
366 pub category: Category,
367 pub symbol: Option<String>,
368 pub base_coin: Option<String>,
370}
371
372#[derive(Debug, Deserialize, PartialEq)]
373#[serde(rename_all = "camelCase")]
374pub struct FeeRateEntry {
375 #[serde(default, deserialize_with = "empty_string_as_none")]
377 pub symbol: Option<String>,
378 #[serde(default, deserialize_with = "empty_string_as_none")]
380 pub base_coin: Option<String>,
381 pub taker_fee_rate: Decimal,
382 pub maker_fee_rate: Decimal,
383}
384
385#[derive(Debug, Serialize, Clone)]
389#[serde(rename_all = "camelCase")]
390pub struct SetMarginModeRequest {
391 pub set_margin_mode: MarginMode,
392}
393
394#[derive(Debug, Deserialize, PartialEq)]
399pub struct SetMarginModeResponse {
400 pub reasons: Vec<MarginModeFailureReason>,
401}
402
403#[derive(Debug, Deserialize, PartialEq)]
404#[serde(rename_all = "camelCase")]
405pub struct MarginModeFailureReason {
406 pub reason_code: String,
407 pub reason_msg: String,
408}
409
410#[derive(Debug, Deserialize, PartialEq)]
414#[serde(rename_all = "camelCase")]
415pub struct UpgradeToUtaResult {
416 pub unified_update_status: UnifiedUpdateStatus,
417 pub unified_update_msg: Option<UnifiedUpdateMsg>,
419}
420
421#[derive(Debug, Deserialize, PartialEq)]
422#[serde(rename_all = "camelCase")]
423pub struct UnifiedUpdateMsg {
424 pub msg: Vec<String>,
425}
426
427#[derive(Clone, Debug, Serialize)]
431#[serde(rename_all = "camelCase")]
432pub struct GetBorrowHistoryParams {
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub currency: Option<String>,
436 #[serde(skip_serializing_if = "Option::is_none")]
438 pub start_time: Option<Timestamp>,
439 #[serde(skip_serializing_if = "Option::is_none")]
441 pub end_time: Option<Timestamp>,
442 #[serde(skip_serializing_if = "Option::is_none")]
444 pub limit: Option<u64>,
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub cursor: Option<String>,
448}
449
450impl GetBorrowHistoryParams {
451 pub fn new() -> Self {
452 Self {
453 currency: None,
454 start_time: None,
455 end_time: None,
456 limit: None,
457 cursor: None,
458 }
459 }
460
461 pub fn with_currency(mut self, v: String) -> Self {
462 self.currency = Some(v);
463 self
464 }
465 pub fn with_start_time(mut self, v: Timestamp) -> Self {
466 self.start_time = Some(v);
467 self
468 }
469 pub fn with_end_time(mut self, v: Timestamp) -> Self {
470 self.end_time = Some(v);
471 self
472 }
473 pub fn with_limit(mut self, v: u64) -> Self {
474 self.limit = Some(v);
475 self
476 }
477 pub fn with_cursor(mut self, v: String) -> Self {
478 self.cursor = Some(v);
479 self
480 }
481}
482
483impl Default for GetBorrowHistoryParams {
484 fn default() -> Self {
485 Self::new()
486 }
487}
488
489#[derive(Debug, Deserialize, PartialEq)]
490#[serde(rename_all = "camelCase")]
491pub struct BorrowHistoryEntry {
492 pub currency: String,
494 #[serde(deserialize_with = "number")]
496 pub created_time: Timestamp,
497 pub borrow_cost: Decimal,
499 pub hourly_borrow_rate: Decimal,
501 #[serde(rename = "InterestBearingBorrowSize")]
503 pub interest_bearing_borrow_size: Decimal,
504 pub cost_exemption: Decimal,
506 pub borrow_amount: Decimal,
508 pub unrealised_loss: Decimal,
510 #[serde(default, deserialize_with = "option_decimal")]
512 pub free_borrowed_amount: Option<Decimal>,
513}
514
515#[derive(Debug, Serialize, Clone)]
519#[serde(rename_all = "camelCase")]
520pub struct GetCollateralInfoParams {
521 #[serde(skip_serializing_if = "Option::is_none")]
523 pub currency: Option<String>,
524}
525
526impl GetCollateralInfoParams {
527 pub fn new() -> Self {
528 Self { currency: None }
529 }
530
531 pub fn with_currency(mut self, v: String) -> Self {
532 self.currency = Some(v);
533 self
534 }
535}
536
537impl Default for GetCollateralInfoParams {
538 fn default() -> Self {
539 Self::new()
540 }
541}
542
543#[derive(Debug, Deserialize, PartialEq)]
544#[serde(rename_all = "camelCase")]
545pub struct CollateralInfoEntry {
546 pub currency: String,
548 pub hourly_borrow_rate: Decimal,
550 pub max_borrowing_amount: Decimal,
552 pub free_borrowing_limit: Decimal,
554 pub free_borrow_amount: Decimal,
556 pub borrow_amount: Decimal,
558 pub other_borrow_amount: Decimal,
560 pub available_to_borrow: Decimal,
562 pub borrowable: bool,
564 pub borrow_usage_rate: Decimal,
566 pub margin_collateral: bool,
568 pub collateral_switch: bool,
570 #[serde(default, deserialize_with = "option_decimal")]
572 pub collateral_ratio: Option<Decimal>,
573 #[serde(default, deserialize_with = "empty_string_as_none")]
575 pub free_borrowing_amount: Option<String>,
576}