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}