bybit/http/account.rs
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/// Query parameters of `GET /v5/account/wallet-balance` ([`Client::get_wallet_balance`](crate::http::Client::get_wallet_balance)).
16#[derive(Debug, Serialize, Clone)]
17#[serde(rename_all = "camelCase")]
18pub struct GetWalletBalanceParams {
19 /// Account type
20 /// UTA2.0: UNIFIED
21 /// UTA1.0: UNIFIED, CONTRACT(inverse derivatives wallet)
22 /// Classic account: CONTRACT, SPOT
23 /// To get Funding wallet balance, please go to this endpoint
24 pub account_type: AccountType,
25 /// Coin name, uppercase only
26 /// If not passed, it returns non-zero asset info
27 /// You can pass multiple coins to query, separated by comma. USDT,USDC
28 pub coin: Option<String>,
29}
30
31impl GetWalletBalanceParams {
32 /// Create with the required fields; set the optional ones with the `with_*` methods.
33 pub fn new(account_type: AccountType) -> Self {
34 Self {
35 account_type,
36 coin: None,
37 }
38 }
39
40 /// Set `coin`: coin name, uppercase only If not passed, it returns non-zero asset info You can pass multiple coins to query, separated by comma. USDT,USDC.
41 pub fn with_coin(mut self, v: impl Into<String>) -> Self {
42 self.coin = Some(v.into());
43 self
44 }
45}
46
47/// Item of the list returned by `GET /v5/account/wallet-balance` ([`Client::get_wallet_balance`](crate::http::Client::get_wallet_balance)).
48#[derive(Debug, Deserialize, PartialEq)]
49#[serde(rename_all = "camelCase")]
50pub struct WalletBalance {
51 /// Account type
52 pub account_type: AccountType,
53 /// Account IM rate
54 /// You can refer to this Glossary to understand the below fields calculation and meaning
55 /// All account wide fields are not applicable to
56 /// UTA2.0(isolated margin),
57 /// UTA1.0(isolated margin), UTA1.0(CONTRACT),
58 /// classic account(SPOT, CONTRACT)
59 #[serde(rename = "accountIMRate")]
60 pub account_im_rate: Decimal,
61 /// Account initial margin (USD) calculated by mark price: ∑Asset Total Initial Margin Base Coin calculated by mark price
62 #[serde(rename = "accountIMRateByMp")]
63 pub account_im_rate_by_mp: Decimal,
64 /// Account MM rate
65 #[serde(rename = "accountMMRate")]
66 pub account_mm_rate: Decimal,
67 /// Account maintenance margin (USD) calculated by mark price: ∑ Asset Total Maintenance Margin Base Coin calculated by mark price
68 #[serde(rename = "accountMMRateByMp")]
69 pub account_mm_rate_by_mp: Decimal,
70 /// Account total equity (USD)
71 pub total_equity: Decimal,
72 /// Account wallet balance (USD): ∑Asset Wallet Balance By USD value of each asset
73 pub total_wallet_balance: Decimal,
74 /// Account margin balance (USD): totalWalletBalance + totalPerpUPL
75 pub total_margin_balance: Decimal,
76 /// Account available balance (USD), Cross Margin: totalMarginBalance - totalInitialMargin
77 pub total_available_balance: Decimal,
78 /// Account Perps and Futures unrealised p&l (USD): ∑Each Perp and USDC Futures upl by base coin
79 #[serde(rename = "totalPerpUPL")]
80 pub total_perp_upl: Decimal,
81 /// Account initial margin (USD): ∑Asset Total Initial Margin Base Coin
82 pub total_initial_margin: Decimal,
83 /// Account initial margin (USD) calculated by mark price: ∑Asset Total Initial Margin Base Coin calculated by mark price
84 pub total_initial_margin_by_mp: Decimal,
85 /// Account maintenance margin (USD): ∑ Asset Total Maintenance Margin Base Coin
86 pub total_maintenance_margin: Decimal,
87 /// Account maintenance margin (USD) calculated by mark price: ∑ Asset Total Maintenance Margin Base Coin calculated by mark price
88 pub total_maintenance_margin_by_mp: Decimal,
89 /// Coin name, uppercase only If not passed, it returns non-zero asset info You can pass multiple coins to query, separated by comma. USDT,USDC.
90 #[serde(deserialize_with = "hash_map")]
91 pub coin: HashMap<String, WalletCoin>,
92}
93
94impl WalletBalance {
95 /// Replace the balance with a `wallet` stream update (the stream always
96 /// sends the full balance of the account type).
97 pub fn update(&mut self, msg: WalletMsg) {
98 *self = msg.into();
99 }
100}
101
102impl From<WalletMsg> for WalletBalance {
103 fn from(msg: WalletMsg) -> Self {
104 Self {
105 account_type: msg.account_type,
106 account_im_rate: msg.account_im_rate,
107 account_im_rate_by_mp: msg.account_im_rate_by_mp,
108 account_mm_rate: msg.account_mm_rate,
109 account_mm_rate_by_mp: msg.account_mm_rate_by_mp,
110 total_equity: msg.total_equity,
111 total_wallet_balance: msg.total_wallet_balance,
112 total_margin_balance: msg.total_margin_balance,
113 total_available_balance: msg.total_available_balance,
114 total_perp_upl: msg.total_perp_upl,
115 total_initial_margin: msg.total_initial_margin,
116 total_initial_margin_by_mp: msg.total_initial_margin_by_mp,
117 total_maintenance_margin: msg.total_maintenance_margin,
118 total_maintenance_margin_by_mp: msg.total_maintenance_margin_by_mp,
119 coin: msg.coin,
120 }
121 }
122}
123
124/// Part of the response of `/v5/account/wallet-balance`.
125#[derive(Debug, Deserialize, PartialEq, Clone)]
126#[serde(rename_all = "camelCase")]
127pub struct WalletCoin {
128 /// Coin name, such as BTC, ETH, USDT, USDC
129 pub coin: String,
130 /// Equity of coin
131 pub equity: Decimal,
132 /// USD value of coin. If this coin cannot be collateral, then it is 0
133 pub usd_value: Decimal,
134 /// Wallet balance of coin
135 pub wallet_balance: Decimal,
136 /// Locked balance due to the Spot open order
137 pub locked: Decimal,
138 /// The spot asset qty that is used to hedge in the portfolio margin, truncate to 8 decimals and "0" by default. This is a unique field for Unified account.
139 pub spot_hedging_qty: Decimal,
140 /// Borrow amount of current coin
141 pub borrow_amount: Decimal,
142 /// Accrued interest
143 pub accrued_interest: Decimal,
144 /// Pre-occupied margin for order. For portfolio margin mode, it returns ""
145 #[serde(rename = "totalOrderIM", default, deserialize_with = "option_decimal")]
146 pub total_order_im: Option<Decimal>,
147 /// Sum of initial margin of all positions + Pre-occupied liquidation fee. For portfolio margin mode, it returns ""
148 #[serde(
149 rename = "totalPositionIM",
150 default,
151 deserialize_with = "option_decimal"
152 )]
153 pub total_position_im: Option<Decimal>,
154 /// Sum of maintenance margin for all positions. For portfolio margin mode, it returns ""
155 #[serde(
156 rename = "totalPositionMM",
157 default,
158 deserialize_with = "option_decimal"
159 )]
160 pub total_position_mm: Option<Decimal>,
161 /// Unrealised P&L
162 pub unrealised_pnl: Decimal,
163 /// Cumulative Realised P&L
164 pub cum_realised_pnl: Decimal,
165 /// Bonus. This is a unique field for accountType=UNIFIED
166 pub bonus: Decimal,
167 /// Whether the collateral is turned on by user (user), true: ON, false: OFF
168 /// When marginCollateral=true, then collateralSwitch is meaningful
169 pub collateral_switch: bool,
170 /// Whether it can be used as a margin collateral currency (platform), true: YES, false: NO
171 /// When marginCollateral=false, then collateralSwitch is meaningless
172 pub margin_collateral: bool,
173 /// Borrow amount by spot margin trade and manual borrow amount (does not include borrow amount by spot margin active order). spotBorrow field corresponding to spot liabilities is detailed in the announcement.
174 #[serde(default, deserialize_with = "option_decimal")]
175 pub spot_borrow: Option<Decimal>,
176}
177
178impl Unique<String> for WalletCoin {
179 fn unique_key(&self) -> String {
180 self.coin.clone()
181 }
182}
183
184/// Result of `GET /v5/account/info` ([`Client::get_account_info`](crate::http::Client::get_account_info)).
185#[derive(Debug, Deserialize, PartialEq)]
186#[serde(rename_all = "camelCase")]
187pub struct AccountInfo {
188 /// Account status
189 pub unified_margin_status: UnifiedMarginStatus,
190 /// ISOLATED_MARGIN, REGULAR_MARGIN, PORTFOLIO_MARGIN
191 pub margin_mode: MarginMode,
192 /// Whether this account is a leader (copytrading). true, false
193 pub is_master_trader: bool,
194 /// Whether the unified account enables Spot hedging. ON, OFF
195 pub spot_hedging_status: SpotHedgingStatus,
196 /// Account data updated timestamp (ms)
197 #[serde(deserialize_with = "number")]
198 pub updated_time: Timestamp,
199}
200
201/// Disconnection Cancellation Protection settings of one product.
202#[derive(Debug, Deserialize, PartialEq)]
203#[serde(rename_all = "camelCase")]
204pub struct DCPConfiguration {
205 /// DCP config for each product
206 pub dcp_infos: Vec<DCPInfo>,
207}
208
209/// Disconnection Cancellation Protection (DCP) settings of the account.
210#[derive(Debug, Deserialize, PartialEq)]
211#[serde(rename_all = "camelCase")]
212pub struct DCPInfo {
213 /// SPOT, DERIVATIVES, OPTIONS
214 pub product: DCPProduct,
215 /// Disconnected-CancelAll-Prevention status: ON
216 #[serde(rename = "dcpStatus")]
217 pub status: String,
218 /// DCP trigger time window which user pre-set. Between [3, 300] seconds, default: 10 sec
219 #[serde(deserialize_with = "number")]
220 pub time_window: Second,
221}
222
223/// Query parameters of `GET /v5/account/transaction-log` ([`Client::get_transaction_log`](crate::http::Client::get_transaction_log)).
224#[derive(Clone, Debug, Serialize)]
225#[serde(rename_all = "camelCase")]
226pub struct GetTransactionLogParams {
227 /// Account Type. UNIFIED
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub account_type: Option<AccountType>,
230 /// Product type spot,linear,option,inverse
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub category: Option<Category>,
233 /// Currency, uppercase only
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub currency: Option<String>,
236 /// BaseCoin, uppercase only. e.g., BTC of BTCPERP
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub base_coin: Option<String>,
239 /// Not documented.
240 /// Settle coin
241 /// linear: either symbol or settleCoin is required. symbol has a higher priority
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub settle_coin: Option<String>,
244 /// Types of transaction logs
245 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
246 pub r#type: Option<String>,
247 /// movePosition, used to filter trans logs of Move Position only
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub trans_sub_type: Option<String>,
250 /// The start timestamp (ms)
251 /// startTime and endTime are not passed, return 24 hours by default
252 /// Only startTime is passed, return range between startTime and startTime+24 hours
253 /// Only endTime is passed, return range between endTime-24 hours and endTime
254 /// If both are passed, the rule is endTime - startTime <= 7 days
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub start_time: Option<Timestamp>,
257 /// The end timestamp (ms)
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub end_time: Option<Timestamp>,
260 /// Limit for data size per page. [1, 50]. Default: 20
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub limit: Option<u64>,
263 /// Cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub cursor: Option<String>,
266}
267
268impl GetTransactionLogParams {
269 /// Create with the required fields; set the optional ones with the `with_*` methods.
270 pub fn new() -> Self {
271 Self {
272 account_type: None,
273 category: None,
274 currency: None,
275 base_coin: None,
276 settle_coin: None,
277 r#type: None,
278 trans_sub_type: None,
279 start_time: None,
280 end_time: None,
281 limit: None,
282 cursor: None,
283 }
284 }
285
286 /// Set `account_type`: account Type. UNIFIED.
287 pub fn with_account_type(mut self, v: AccountType) -> Self {
288 self.account_type = Some(v);
289 self
290 }
291 /// Set `category`: product type spot,linear,option,inverse.
292 pub fn with_category(mut self, v: Category) -> Self {
293 self.category = Some(v);
294 self
295 }
296 /// Set `currency`: currency, uppercase only.
297 pub fn with_currency(mut self, v: impl Into<String>) -> Self {
298 self.currency = Some(v.into());
299 self
300 }
301 /// Set `base_coin`: baseCoin, uppercase only. e.g., BTC of BTCPERP.
302 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
303 self.base_coin = Some(v.into());
304 self
305 }
306 /// Set `settle_coin`: not documented. Settle coin linear: either symbol or settleCoin is required. symbol has a higher priority.
307 pub fn with_settle_coin(mut self, v: impl Into<String>) -> Self {
308 self.settle_coin = Some(v.into());
309 self
310 }
311 /// Set `type`: types of transaction logs.
312 pub fn with_type(mut self, v: impl Into<String>) -> Self {
313 self.r#type = Some(v.into());
314 self
315 }
316 /// Set `trans_sub_type`: movePosition, used to filter trans logs of Move Position only.
317 pub fn with_trans_sub_type(mut self, v: impl Into<String>) -> Self {
318 self.trans_sub_type = Some(v.into());
319 self
320 }
321 /// Set `start_time`: the start timestamp (ms) startTime and endTime are not passed, return 24 hours by default Only startTime is passed, return range between startTime and startTime+24 hours Only endTime is passed, return range between endTime-24 hours and ...
322 pub fn with_start_time(mut self, v: Timestamp) -> Self {
323 self.start_time = Some(v);
324 self
325 }
326 /// Set `end_time`: the end timestamp (ms).
327 pub fn with_end_time(mut self, v: Timestamp) -> Self {
328 self.end_time = Some(v);
329 self
330 }
331 /// Set `limit`: limit for data size per page. [1, 50]. Default: 20.
332 pub fn with_limit(mut self, v: u64) -> Self {
333 self.limit = Some(v);
334 self
335 }
336 /// Set `cursor`: cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set.
337 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
338 self.cursor = Some(v.into());
339 self
340 }
341}
342
343impl Default for GetTransactionLogParams {
344 fn default() -> Self {
345 Self::new()
346 }
347}
348
349/// Item of the list returned by `GET /v5/account/transaction-log` ([`Client::get_transaction_log`](crate::http::Client::get_transaction_log)).
350#[derive(Debug, Deserialize, PartialEq)]
351#[serde(rename_all = "camelCase")]
352pub struct TransactionLog {
353 /// Unique id
354 pub id: String,
355 /// Symbol name
356 pub symbol: String,
357 /// Product type
358 pub category: Category,
359 /// Side. Buy,Sell,None
360 pub side: crate::Side,
361 /// Transaction timestamp (ms)
362 #[serde(deserialize_with = "number")]
363 pub transaction_time: Timestamp,
364 /// Type
365 #[serde(rename = "type")]
366 pub r#type: String,
367 /// Transaction sub type, movePosition, used for the logs generated by move position. "" by default
368 pub trans_sub_type: String,
369 /// Quantity
370 /// Spot: the negative means the qty of this currency is decreased, the positive means the qty of this currency is increased
371 /// Perps & Futures: it is the quantity for each trade entry and it does not have direction
372 pub qty: Decimal,
373 /// Size. The rest position size after the trade is executed, and it has direction, i.e., short with "-"
374 pub size: Decimal,
375 /// e.g., USDC, USDT, BTC, ETH
376 pub currency: String,
377 /// Trade price
378 pub trade_price: Decimal,
379 /// Funding fee
380 /// Positive fee value means receive funding; negative fee value means pay funding. This is opposite to the execFee from Get Trade History.
381 /// For USDC Perp, as funding settlement and session settlement occur at the same time, they are represented in a single record at settlement. Please refer to funding to understand funding fee, and cashFlow to understand 8-hour P&L.
382 pub funding: Decimal,
383 /// Trading fee
384 /// Positive fee value means expense
385 /// Negative fee value means rebates
386 pub fee: Decimal,
387 /// Cash flow, e.g., (1) close the position, and unRPL converts to RPL, (2) 8-hour session settlement for USDC Perp and Futures, (3) transfer in or transfer out. This does not include trading fee, funding fee
388 pub cash_flow: Decimal,
389 /// Change = cashFlow + funding - fee
390 pub change: Decimal,
391 /// Cash balance. This is the wallet balance after a cash change
392 pub cash_balance: Decimal,
393 ///
394 /// When type=TRADE, then it is trading fee rate
395 /// When type=SETTLEMENT, it means funding fee rate. For side=Buy, feeRate=market fee rate; For side=Sell, feeRate= - market fee rate
396 pub fee_rate: Decimal,
397 /// The change of bonus
398 pub bonus_change: Decimal,
399 /// Trade ID
400 pub trade_id: String,
401 /// Order ID
402 pub order_id: String,
403 /// User customised order ID
404 #[serde(default, deserialize_with = "crate::serde::empty_string_as_none")]
405 pub order_link_id: Option<String>,
406 /// Trading fee rate information. Currently, this data is returned only for spot orders placed on the Indonesian site or spot fiat currency orders placed on the EU site. In other cases, an empty string is returned. Enum: feeType, subFeeType
407 #[serde(default)]
408 pub extra_fees: Option<serde_json::Value>,
409}
410
411// --- Fee Rate ---
412
413/// Query params for [`Client::get_fee_rate`](crate::http::Client::get_fee_rate).
414#[derive(Debug, Serialize, Clone)]
415#[serde(rename_all = "camelCase")]
416pub struct GetFeeRateParams {
417 /// Product type. spot, linear, inverse, option. Passing linear or inverse returns the same result - fee rates for all contracts (both linear and inverse).
418 pub category: Category,
419 /// Symbol name, like BTCUSDT, uppercase only. Valid for linear, inverse, spot.
420 pub symbol: Option<String>,
421 /// Only for `category = option`. Base coin, e.g. `"BTC"`.
422 pub base_coin: Option<String>,
423}
424
425impl GetFeeRateParams {
426 /// Create with the required fields; set the optional ones with the `with_*` methods.
427 pub fn new(category: Category) -> Self {
428 Self {
429 category,
430 symbol: None,
431 base_coin: None,
432 }
433 }
434
435 /// Set `symbol`: symbol name, like BTCUSDT, uppercase only. Valid for linear, inverse, spot.
436 pub fn with_symbol(mut self, v: impl Into<String>) -> Self {
437 self.symbol = Some(v.into());
438 self
439 }
440
441 /// Set `base_coin`: only for `category = option`. Base coin, e.g. `"BTC"`.
442 pub fn with_base_coin(mut self, v: impl Into<String>) -> Self {
443 self.base_coin = Some(v.into());
444 self
445 }
446}
447
448/// Item of the list returned by `GET /v5/account/fee-rate` ([`Client::get_fee_rate`](crate::http::Client::get_fee_rate)).
449#[derive(Debug, Deserialize, PartialEq)]
450#[serde(rename_all = "camelCase")]
451pub struct FeeRateEntry {
452 /// Symbol name. Spot/linear/inverse only.
453 #[serde(default, deserialize_with = "empty_string_as_none")]
454 pub symbol: Option<String>,
455 /// Base coin. Option only.
456 #[serde(default, deserialize_with = "empty_string_as_none")]
457 pub base_coin: Option<String>,
458 /// Taker fee rate.
459 pub taker_fee_rate: Decimal,
460 /// Maker fee rate.
461 pub maker_fee_rate: Decimal,
462}
463
464// --- Set Margin Mode ---
465
466/// Request body for [`Client::set_margin_mode`](crate::http::Client::set_margin_mode).
467#[derive(Debug, Serialize, Clone)]
468#[serde(rename_all = "camelCase")]
469pub struct SetMarginModeRequest {
470 /// ISOLATED_MARGIN, REGULAR_MARGIN (i.e. Cross margin), PORTFOLIO_MARGIN.
471 pub set_margin_mode: MarginMode,
472}
473
474impl SetMarginModeRequest {
475 /// Create with the required fields; set the optional ones with the `with_*` methods.
476 pub fn new(set_margin_mode: MarginMode) -> Self {
477 Self { set_margin_mode }
478 }
479}
480
481/// Response for [`Client::set_margin_mode`](crate::http::Client::set_margin_mode).
482///
483/// On success `reasons` is empty. On failure it contains error details
484/// explaining why the switch was rejected.
485#[derive(Debug, Deserialize, PartialEq)]
486pub struct SetMarginModeResponse {
487 /// Object. If requested successfully, it is an empty array.
488 pub reasons: Vec<MarginModeFailureReason>,
489}
490
491/// Part of the response of `/v5/account/set-margin-mode`.
492#[derive(Debug, Deserialize, PartialEq)]
493#[serde(rename_all = "camelCase")]
494pub struct MarginModeFailureReason {
495 /// Fail reason code.
496 pub reason_code: String,
497 /// Fail reason msg.
498 pub reason_msg: String,
499}
500
501// --- Upgrade to Unified Account (Pro) ---
502
503/// Response for [`Client::upgrade_to_uta`](crate::http::Client::upgrade_to_uta).
504#[derive(Debug, Deserialize, PartialEq)]
505#[serde(rename_all = "camelCase")]
506pub struct UpgradeToUtaResult {
507 /// Upgrade status. FAIL, PROCESS, SUCCESS.
508 pub unified_update_status: UnifiedUpdateStatus,
509 /// `None` unless `unified_update_status` is `Fail`.
510 pub unified_update_msg: Option<UnifiedUpdateMsg>,
511}
512
513/// Part of the response of `/v5/account/upgrade-to-uta`.
514#[derive(Debug, Deserialize, PartialEq)]
515#[serde(rename_all = "camelCase")]
516pub struct UnifiedUpdateMsg {
517 /// Error message array. Only FAIL will have this field.
518 pub msg: Vec<String>,
519}
520
521// --- Get Borrow History ---
522
523/// Query params for [`Client::get_borrow_history`](crate::http::Client::get_borrow_history).
524#[derive(Clone, Debug, Serialize)]
525#[serde(rename_all = "camelCase")]
526pub struct GetBorrowHistoryParams {
527 /// Currency, uppercase only
528 #[serde(skip_serializing_if = "Option::is_none")]
529 pub currency: Option<String>,
530 /// The start timestamp (ms). Combined with end_time, cannot exceed 30 days
531 #[serde(skip_serializing_if = "Option::is_none")]
532 pub start_time: Option<Timestamp>,
533 /// The end timestamp (ms). Combined with start_time, cannot exceed 30 days
534 #[serde(skip_serializing_if = "Option::is_none")]
535 pub end_time: Option<Timestamp>,
536 /// Limit for data size per page. [1, 50]. Default: 20
537 #[serde(skip_serializing_if = "Option::is_none")]
538 pub limit: Option<u64>,
539 /// Cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set
540 #[serde(skip_serializing_if = "Option::is_none")]
541 pub cursor: Option<String>,
542}
543
544impl GetBorrowHistoryParams {
545 /// Create with the required fields; set the optional ones with the `with_*` methods.
546 pub fn new() -> Self {
547 Self {
548 currency: None,
549 start_time: None,
550 end_time: None,
551 limit: None,
552 cursor: None,
553 }
554 }
555
556 /// Set `currency`: currency, uppercase only.
557 pub fn with_currency(mut self, v: impl Into<String>) -> Self {
558 self.currency = Some(v.into());
559 self
560 }
561 /// Set `start_time`: the start timestamp (ms). Combined with end_time, cannot exceed 30 days.
562 pub fn with_start_time(mut self, v: Timestamp) -> Self {
563 self.start_time = Some(v);
564 self
565 }
566 /// Set `end_time`: the end timestamp (ms). Combined with start_time, cannot exceed 30 days.
567 pub fn with_end_time(mut self, v: Timestamp) -> Self {
568 self.end_time = Some(v);
569 self
570 }
571 /// Set `limit`: limit for data size per page. [1, 50]. Default: 20.
572 pub fn with_limit(mut self, v: u64) -> Self {
573 self.limit = Some(v);
574 self
575 }
576 /// Set `cursor`: cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set.
577 pub fn with_cursor(mut self, v: impl Into<String>) -> Self {
578 self.cursor = Some(v.into());
579 self
580 }
581}
582
583impl Default for GetBorrowHistoryParams {
584 fn default() -> Self {
585 Self::new()
586 }
587}
588
589/// Item of the list returned by `GET /v5/account/borrow-history` ([`Client::get_borrow_history`](crate::http::Client::get_borrow_history)).
590#[derive(Debug, Deserialize, PartialEq)]
591#[serde(rename_all = "camelCase")]
592pub struct BorrowHistoryEntry {
593 /// Currency
594 pub currency: String,
595 /// Interest record timestamp (ms)
596 #[serde(deserialize_with = "number")]
597 pub created_time: Timestamp,
598 /// Borrow cost, i.e., accrued interest
599 pub borrow_cost: Decimal,
600 /// Hourly borrow rate
601 pub hourly_borrow_rate: Decimal,
602 /// Borrowed amount that accrues interest
603 #[serde(rename = "InterestBearingBorrowSize")]
604 pub interest_bearing_borrow_size: Decimal,
605 /// Amount exempted from interest
606 pub cost_exemption: Decimal,
607 /// Borrowed amount
608 pub borrow_amount: Decimal,
609 /// Unrealised loss
610 pub unrealised_loss: Decimal,
611 /// Amount borrowed within the interest-free threshold
612 #[serde(default, deserialize_with = "option_decimal")]
613 pub free_borrowed_amount: Option<Decimal>,
614}
615
616// --- Get Collateral Info ---
617
618/// Query params for [`Client::get_collateral_info`](crate::http::Client::get_collateral_info).
619#[derive(Debug, Serialize, Clone)]
620#[serde(rename_all = "camelCase")]
621pub struct GetCollateralInfoParams {
622 /// Asset currency of all current collateral, uppercase only
623 #[serde(skip_serializing_if = "Option::is_none")]
624 pub currency: Option<String>,
625}
626
627impl GetCollateralInfoParams {
628 /// Create with the required fields; set the optional ones with the `with_*` methods.
629 pub fn new() -> Self {
630 Self { currency: None }
631 }
632
633 /// Set `currency`: asset currency of all current collateral, uppercase only.
634 pub fn with_currency(mut self, v: impl Into<String>) -> Self {
635 self.currency = Some(v.into());
636 self
637 }
638}
639
640impl Default for GetCollateralInfoParams {
641 fn default() -> Self {
642 Self::new()
643 }
644}
645
646/// Item of the list returned by `GET /v5/account/collateral-info` ([`Client::get_collateral_info`](crate::http::Client::get_collateral_info)).
647#[derive(Debug, Deserialize, PartialEq)]
648#[serde(rename_all = "camelCase")]
649pub struct CollateralInfoEntry {
650 /// Currency of the collateral
651 pub currency: String,
652 /// Hourly interest rate for borrowing
653 pub hourly_borrow_rate: Decimal,
654 /// Maximum borrowable amount, shared across main and sub accounts
655 pub max_borrowing_amount: Decimal,
656 /// Maximum limit for interest-free borrowing
657 pub free_borrowing_limit: Decimal,
658 /// Amount borrowed within the interest-free threshold
659 pub free_borrow_amount: Decimal,
660 /// Current borrow amount
661 pub borrow_amount: Decimal,
662 /// Borrowing amount from other accounts under the same main account
663 pub other_borrow_amount: Decimal,
664 /// Remaining amount available to borrow
665 pub available_to_borrow: Decimal,
666 /// Whether currency can be borrowed
667 pub borrowable: bool,
668 /// Borrow usage ratio, e.g. 0.5 = 50%
669 pub borrow_usage_rate: Decimal,
670 /// Whether usable as margin collateral
671 pub margin_collateral: bool,
672 /// Whether collateral is enabled by user
673 pub collateral_switch: bool,
674 /// Deprecated. Refer to the tiered collateral ratio endpoint instead
675 #[serde(default, deserialize_with = "option_decimal")]
676 pub collateral_ratio: Option<Decimal>,
677 /// Deprecated. Always empty
678 #[serde(default, deserialize_with = "empty_string_as_none")]
679 pub free_borrowing_amount: Option<String>,
680}