Skip to main content

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    enums::Category,
10    serde::{Unique, hash_map},
11    ws::WalletMsg,
12};
13
14#[derive(Debug, Serialize)]
15#[serde(rename_all = "camelCase")]
16pub struct GetWalletBalanceParams {
17    /// Account type
18    /// UTA2.0: UNIFIED
19    /// UTA1.0: UNIFIED, CONTRACT(inverse derivatives wallet)
20    /// Classic account: CONTRACT, SPOT
21    /// To get Funding wallet balance, please go to this endpoint
22    pub account_type: AccountType,
23    /// Coin name, uppercase only
24    /// If not passed, it returns non-zero asset info
25    /// You can pass multiple coins to query, separated by comma. USDT,USDC
26    pub coin: Option<String>,
27}
28
29#[derive(Debug, Deserialize, PartialEq)]
30#[serde(rename_all = "camelCase")]
31pub struct WalletBalance {
32    /// Account type
33    pub account_type: AccountType,
34    /// Account IM rate
35    /// You can refer to this Glossary to understand the below fields calculation and meaning
36    /// All account wide fields are not applicable to
37    /// UTA2.0(isolated margin),
38    /// UTA1.0(isolated margin), UTA1.0(CONTRACT),
39    /// classic account(SPOT, CONTRACT)
40    #[serde(rename = "accountIMRate")]
41    pub account_im_rate: Decimal,
42    /// Account initial margin (USD) calculated by mark price: ∑Asset Total Initial Margin Base Coin calculated by mark price
43    #[serde(rename = "accountIMRateByMp")]
44    pub account_im_rate_by_mp: Decimal,
45    /// Account MM rate
46    #[serde(rename = "accountMMRate")]
47    pub account_mm_rate: Decimal,
48    /// Account maintenance margin (USD) calculated by mark price: ∑ Asset Total Maintenance Margin Base Coin calculated by mark price
49    #[serde(rename = "accountMMRateByMp")]
50    pub account_mm_rate_by_mp: Decimal,
51    /// Account total equity (USD)
52    pub total_equity: Decimal,
53    /// Account wallet balance (USD): ∑Asset Wallet Balance By USD value of each asset
54    pub total_wallet_balance: Decimal,
55    /// Account margin balance (USD): totalWalletBalance + totalPerpUPL
56    pub total_margin_balance: Decimal,
57    /// Account available balance (USD), Cross Margin: totalMarginBalance - totalInitialMargin
58    pub total_available_balance: Decimal,
59    /// Account Perps and Futures unrealised p&l (USD): ∑Each Perp and USDC Futures upl by base coin
60    #[serde(rename = "totalPerpUPL")]
61    pub total_perp_upl: Decimal,
62    /// Account initial margin (USD): ∑Asset Total Initial Margin Base Coin
63    pub total_initial_margin: Decimal,
64    /// Account initial margin (USD) calculated by mark price: ∑Asset Total Initial Margin Base Coin calculated by mark price
65    pub total_initial_margin_by_mp: Decimal,
66    /// Account maintenance margin (USD): ∑ Asset Total Maintenance Margin Base Coin
67    pub total_maintenance_margin: Decimal,
68    /// Account maintenance margin (USD) calculated by mark price: ∑ Asset Total Maintenance Margin Base Coin calculated by mark price
69    pub total_maintenance_margin_by_mp: Decimal,
70    #[serde(deserialize_with = "hash_map")]
71    pub coin: HashMap<String, WalletCoin>,
72}
73
74impl WalletBalance {
75    pub fn update(&mut self, msg: WalletMsg) {
76        self.account_type = msg.account_type;
77        self.account_im_rate = msg.account_im_rate;
78        self.account_mm_rate = msg.account_mm_rate;
79        self.total_equity = msg.total_equity;
80        self.total_wallet_balance = msg.total_wallet_balance;
81        self.total_margin_balance = msg.total_margin_balance;
82        self.total_available_balance = msg.total_available_balance;
83        self.total_perp_upl = msg.total_perp_upl;
84        self.total_initial_margin = msg.total_initial_margin;
85        self.total_maintenance_margin = msg.total_maintenance_margin;
86        self.coin = msg.coin;
87    }
88}
89
90#[derive(Debug, Deserialize, PartialEq, Clone)]
91#[serde(rename_all = "camelCase")]
92pub struct WalletCoin {
93    /// Coin name, such as BTC, ETH, USDT, USDC
94    pub coin: String,
95    /// Equity of coin
96    pub equity: Decimal,
97    /// USD value of coin. If this coin cannot be collateral, then it is 0
98    pub usd_value: Decimal,
99    /// Wallet balance of coin
100    pub wallet_balance: Decimal,
101    /// Locked balance due to the Spot open order
102    pub locked: Decimal,
103    /// 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.
104    pub spot_hedging_qty: Decimal,
105    /// Borrow amount of current coin
106    pub borrow_amount: Decimal,
107    /// Accrued interest
108    pub accrued_interest: Decimal,
109    /// Pre-occupied margin for order. For portfolio margin mode, it returns ""
110    #[serde(rename = "totalOrderIM", default, deserialize_with = "option_decimal")]
111    pub total_order_im: Option<Decimal>,
112    /// Sum of initial margin of all positions + Pre-occupied liquidation fee. For portfolio margin mode, it returns ""
113    #[serde(
114        rename = "totalPositionIM",
115        default,
116        deserialize_with = "option_decimal"
117    )]
118    pub total_position_im: Option<Decimal>,
119    /// Sum of maintenance margin for all positions. For portfolio margin mode, it returns ""
120    #[serde(
121        rename = "totalPositionMM",
122        default,
123        deserialize_with = "option_decimal"
124    )]
125    pub total_position_mm: Option<Decimal>,
126    /// Unrealised P&L
127    pub unrealised_pnl: Decimal,
128    /// Cumulative Realised P&L
129    pub cum_realised_pnl: Decimal,
130    /// Bonus. This is a unique field for accountType=UNIFIED
131    pub bonus: Decimal,
132    /// Whether the collateral is turned on by user (user), true: ON, false: OFF
133    /// When marginCollateral=true, then collateralSwitch is meaningful
134    pub collateral_switch: bool,
135    /// Whether it can be used as a margin collateral currency (platform), true: YES, false: NO
136    /// When marginCollateral=false, then collateralSwitch is meaningless
137    pub margin_collateral: bool,
138    /// 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.
139    #[serde(default, deserialize_with = "option_decimal")]
140    pub spot_borrow: Option<Decimal>,
141}
142
143impl Unique<String> for WalletCoin {
144    fn unique_key(&self) -> String {
145        self.coin.clone()
146    }
147}
148
149#[derive(Debug, Deserialize, PartialEq)]
150#[serde(rename_all = "camelCase")]
151pub struct AccountInfo {
152    /// Account status
153    pub unified_margin_status: UnifiedMarginStatus,
154    /// ISOLATED_MARGIN, REGULAR_MARGIN, PORTFOLIO_MARGIN
155    pub margin_mode: MarginMode,
156    /// Whether this account is a leader (copytrading). true, false
157    pub is_master_trader: bool,
158    /// Whether the unified account enables Spot hedging. ON, OFF
159    pub spot_hedging_status: SpotHedgingStatus,
160    /// Account data updated timestamp (ms)
161    #[serde(deserialize_with = "number")]
162    pub updated_time: Timestamp,
163}
164
165#[derive(Debug, Deserialize, PartialEq)]
166#[serde(rename_all = "camelCase")]
167pub struct DCPConfiguration {
168    /// DCP config for each product
169    pub dcp_infos: Vec<DCPInfo>,
170}
171
172#[derive(Debug, Deserialize, PartialEq)]
173#[serde(rename_all = "camelCase")]
174pub struct DCPInfo {
175    /// SPOT, DERIVATIVES, OPTIONS
176    pub product: DCPProduct,
177    /// Disconnected-CancelAll-Prevention status: ON
178    #[serde(rename = "dcpStatus")]
179    pub status: String,
180    /// DCP trigger time window which user pre-set. Between [3, 300] seconds, default: 10 sec
181    #[serde(deserialize_with = "number")]
182    pub time_window: Second,
183}
184
185#[derive(Clone, Debug, Serialize)]
186#[serde(rename_all = "camelCase")]
187pub struct GetTransactionLogParams {
188    /// Account Type. UNIFIED
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub account_type: Option<AccountType>,
191    /// Product type spot,linear,option,inverse
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub category: Option<Category>,
194    /// Currency, uppercase only
195    #[serde(skip_serializing_if = "Option::is_none")]
196    pub currency: Option<String>,
197    /// BaseCoin, uppercase only. e.g., BTC of BTCPERP
198    #[serde(skip_serializing_if = "Option::is_none")]
199    pub base_coin: Option<String>,
200    /// Not documented.
201    /// Settle coin
202    /// linear: either symbol or settleCoin is required. symbol has a higher priority
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub settle_coin: Option<String>,
205    /// Types of transaction logs
206    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
207    pub log_type: Option<String>,
208    /// movePosition, used to filter trans logs of Move Position only
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub trans_sub_type: Option<String>,
211    /// The start timestamp (ms)
212    /// startTime and endTime are not passed, return 24 hours by default
213    /// Only startTime is passed, return range between startTime and startTime+24 hours
214    /// Only endTime is passed, return range between endTime-24 hours and endTime
215    /// If both are passed, the rule is endTime - startTime <= 7 days
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub start_time: Option<Timestamp>,
218    /// The end timestamp (ms)
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub end_time: Option<Timestamp>,
221    /// Limit for data size per page. [1, 50]. Default: 20
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub limit: Option<u64>,
224    /// Cursor. Use the nextPageCursor token from the response to retrieve the next page of the result set
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub cursor: Option<String>,
227}
228
229impl GetTransactionLogParams {
230    pub fn new() -> Self {
231        Self {
232            account_type: None,
233            category: None,
234            currency: None,
235            base_coin: None,
236            settle_coin: None,
237            log_type: None,
238            trans_sub_type: None,
239            start_time: None,
240            end_time: None,
241            limit: None,
242            cursor: None,
243        }
244    }
245
246    pub fn with_account_type(mut self, v: AccountType) -> Self {
247        self.account_type = Some(v);
248        self
249    }
250    pub fn with_category(mut self, v: Category) -> Self {
251        self.category = Some(v);
252        self
253    }
254    pub fn with_currency(mut self, v: String) -> Self {
255        self.currency = Some(v);
256        self
257    }
258    pub fn with_base_coin(mut self, v: String) -> Self {
259        self.base_coin = Some(v);
260        self
261    }
262    pub fn with_settle_coin(mut self, v: String) -> Self {
263        self.settle_coin = Some(v);
264        self
265    }
266    pub fn with_log_type(mut self, v: String) -> Self {
267        self.log_type = Some(v);
268        self
269    }
270    pub fn with_trans_sub_type(mut self, v: String) -> Self {
271        self.trans_sub_type = Some(v);
272        self
273    }
274    pub fn with_start_time(mut self, v: Timestamp) -> Self {
275        self.start_time = Some(v);
276        self
277    }
278    pub fn with_end_time(mut self, v: Timestamp) -> Self {
279        self.end_time = Some(v);
280        self
281    }
282    pub fn with_limit(mut self, v: u64) -> Self {
283        self.limit = Some(v);
284        self
285    }
286    pub fn with_cursor(mut self, v: String) -> Self {
287        self.cursor = Some(v);
288        self
289    }
290}
291
292impl Default for GetTransactionLogParams {
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298#[derive(Debug, Deserialize, PartialEq)]
299#[serde(rename_all = "camelCase")]
300pub struct TransactionLog {
301    /// Unique id
302    pub id: String,
303    /// Symbol name
304    pub symbol: String,
305    /// Product type
306    pub category: Category,
307    /// Side. Buy,Sell,None
308    pub side: crate::Side,
309    /// Transaction timestamp (ms)
310    #[serde(deserialize_with = "number")]
311    pub transaction_time: Timestamp,
312    /// Type
313    #[serde(rename = "type")]
314    pub log_type: String,
315    /// Transaction sub type, movePosition, used for the logs generated by move position. "" by default
316    pub trans_sub_type: String,
317    /// Quantity
318    /// Spot: the negative means the qty of this currency is decreased, the positive means the qty of this currency is increased
319    /// Perps & Futures: it is the quantity for each trade entry and it does not have direction
320    pub qty: Decimal,
321    /// Size. The rest position size after the trade is executed, and it has direction, i.e., short with "-"
322    pub size: Decimal,
323    /// e.g., USDC, USDT, BTC, ETH
324    pub currency: String,
325    /// Trade price
326    pub trade_price: Decimal,
327    /// Funding fee
328    /// Positive fee value means receive funding; negative fee value means pay funding. This is opposite to the execFee from Get Trade History.
329    /// 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.
330    pub funding: Decimal,
331    /// Trading fee
332    /// Positive fee value means expense
333    /// Negative fee value means rebates
334    pub fee: Decimal,
335    /// 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
336    pub cash_flow: Decimal,
337    /// Change = cashFlow + funding - fee
338    pub change: Decimal,
339    /// Cash balance. This is the wallet balance after a cash change
340    pub cash_balance: Decimal,
341    ///
342    /// When type=TRADE, then it is trading fee rate
343    /// When type=SETTLEMENT, it means funding fee rate. For side=Buy, feeRate=market fee rate; For side=Sell, feeRate= - market fee rate
344    pub fee_rate: Decimal,
345    /// The change of bonus
346    pub bonus_change: Decimal,
347    /// Trade ID
348    pub trade_id: String,
349    /// Order ID
350    pub order_id: String,
351    /// User customised order ID
352    #[serde(default, deserialize_with = "crate::serde::empty_string_as_none")]
353    pub order_link_id: Option<String>,
354    /// 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
355    #[serde(default)]
356    pub extra_fees: Option<serde_json::Value>,
357}