nautilus_bitmex/http/
models.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Data structures representing BitMEX REST API payloads.
17
18use chrono::{DateTime, Utc};
19use serde::{Deserialize, Serialize};
20use ustr::Ustr;
21use uuid::Uuid;
22
23use crate::common::enums::{
24    BitmexContingencyType, BitmexExecInstruction, BitmexExecType, BitmexFairMethod,
25    BitmexInstrumentState, BitmexInstrumentType, BitmexLiquidityIndicator, BitmexMarkMethod,
26    BitmexOrderStatus, BitmexOrderType, BitmexPegPriceType, BitmexSide, BitmexTickDirection,
27    BitmexTimeInForce,
28};
29
30/// Custom deserializer for comma-separated `ExecInstruction` values
31fn deserialize_exec_instructions<'de, D>(
32    deserializer: D,
33) -> Result<Option<Vec<BitmexExecInstruction>>, D::Error>
34where
35    D: serde::Deserializer<'de>,
36{
37    let s: Option<String> = Option::deserialize(deserializer)?;
38    match s {
39        None => Ok(None),
40        Some(ref s) if s.is_empty() => Ok(None),
41        Some(s) => {
42            let instructions: Result<Vec<BitmexExecInstruction>, _> = s
43                .split(',')
44                .map(|inst| {
45                    let trimmed = inst.trim();
46                    match trimmed {
47                        "ParticipateDoNotInitiate" => {
48                            Ok(BitmexExecInstruction::ParticipateDoNotInitiate)
49                        }
50                        "AllOrNone" => Ok(BitmexExecInstruction::AllOrNone),
51                        "MarkPrice" => Ok(BitmexExecInstruction::MarkPrice),
52                        "IndexPrice" => Ok(BitmexExecInstruction::IndexPrice),
53                        "LastPrice" => Ok(BitmexExecInstruction::LastPrice),
54                        "Close" => Ok(BitmexExecInstruction::Close),
55                        "ReduceOnly" => Ok(BitmexExecInstruction::ReduceOnly),
56                        "Fixed" => Ok(BitmexExecInstruction::Fixed),
57                        "" => Ok(BitmexExecInstruction::Unknown),
58                        _ => Err(serde::de::Error::custom(format!(
59                            "Unknown ExecInstruction: {trimmed}"
60                        ))),
61                    }
62                })
63                .collect();
64            instructions.map(Some)
65        }
66    }
67}
68
69#[derive(Clone, Debug, Serialize, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub struct BitmexInstrument {
72    pub symbol: Ustr,
73    pub root_symbol: Ustr,
74    pub state: BitmexInstrumentState,
75    #[serde(rename = "typ")]
76    pub instrument_type: BitmexInstrumentType,
77    pub listing: Option<DateTime<Utc>>,
78    pub front: Option<DateTime<Utc>>,
79    pub expiry: Option<DateTime<Utc>>,
80    pub settle: Option<DateTime<Utc>>,
81    pub listed_settle: Option<DateTime<Utc>>,
82    pub position_currency: Option<Ustr>,
83    pub underlying: Ustr,
84    pub quote_currency: Ustr,
85    pub underlying_symbol: Option<Ustr>,
86    pub reference: Option<Ustr>,
87    pub reference_symbol: Option<Ustr>,
88    pub calc_interval: Option<DateTime<Utc>>,
89    pub publish_interval: Option<DateTime<Utc>>,
90    pub publish_time: Option<DateTime<Utc>>,
91    pub max_order_qty: Option<f64>,
92    pub max_price: Option<f64>,
93    pub lot_size: Option<f64>,
94    pub tick_size: f64,
95    pub multiplier: f64,
96    pub settl_currency: Option<Ustr>,
97    pub underlying_to_position_multiplier: Option<f64>,
98    pub underlying_to_settle_multiplier: Option<f64>,
99    pub quote_to_settle_multiplier: Option<f64>,
100    pub is_quanto: bool,
101    pub is_inverse: bool,
102    pub init_margin: Option<f64>,
103    pub maint_margin: Option<f64>,
104    pub risk_limit: Option<f64>,
105    pub risk_step: Option<f64>,
106    pub limit: Option<f64>,
107    pub taxed: Option<bool>,
108    pub deleverage: Option<bool>,
109    pub maker_fee: Option<f64>,
110    pub taker_fee: Option<f64>,
111    pub settlement_fee: Option<f64>,
112    pub funding_base_symbol: Option<Ustr>,
113    pub funding_quote_symbol: Option<Ustr>,
114    pub funding_premium_symbol: Option<Ustr>,
115    pub funding_timestamp: Option<DateTime<Utc>>,
116    pub funding_interval: Option<DateTime<Utc>>,
117    pub funding_rate: Option<f64>,
118    pub indicative_funding_rate: Option<f64>,
119    pub rebalance_timestamp: Option<DateTime<Utc>>,
120    pub rebalance_interval: Option<DateTime<Utc>>,
121    pub prev_close_price: Option<f64>,
122    pub limit_down_price: Option<f64>,
123    pub limit_up_price: Option<f64>,
124    pub prev_total_volume: Option<f64>,
125    pub total_volume: Option<f64>,
126    pub volume: Option<f64>,
127    #[serde(rename = "volume24h")]
128    pub volume_24h: Option<f64>,
129    pub prev_total_turnover: Option<f64>,
130    pub total_turnover: Option<f64>,
131    pub turnover: Option<f64>,
132    #[serde(rename = "turnover24h")]
133    pub turnover_24h: Option<f64>,
134    #[serde(rename = "homeNotional24h")]
135    pub home_notional_24h: Option<f64>,
136    #[serde(rename = "foreignNotional24h")]
137    pub foreign_notional_24h: Option<f64>,
138    #[serde(rename = "prevPrice24h")]
139    pub prev_price_24h: Option<f64>,
140    pub vwap: Option<f64>,
141    pub high_price: Option<f64>,
142    pub low_price: Option<f64>,
143    pub last_price: Option<f64>,
144    pub last_price_protected: Option<f64>,
145    pub last_tick_direction: Option<BitmexTickDirection>,
146    pub last_change_pcnt: Option<f64>,
147    pub bid_price: Option<f64>,
148    pub mid_price: Option<f64>,
149    pub ask_price: Option<f64>,
150    pub impact_bid_price: Option<f64>,
151    pub impact_mid_price: Option<f64>,
152    pub impact_ask_price: Option<f64>,
153    pub has_liquidity: Option<bool>,
154    pub open_interest: Option<f64>,
155    pub open_value: Option<f64>,
156    pub fair_method: Option<BitmexFairMethod>,
157    pub fair_basis_rate: Option<f64>,
158    pub fair_basis: Option<f64>,
159    pub fair_price: Option<f64>,
160    pub mark_method: Option<BitmexMarkMethod>,
161    pub mark_price: Option<f64>,
162    pub indicative_settle_price: Option<f64>,
163    pub settled_price_adjustment_rate: Option<f64>,
164    pub settled_price: Option<f64>,
165    pub instant_pnl: bool,
166    pub min_tick: Option<f64>,
167    pub funding_base_rate: Option<f64>,
168    pub funding_quote_rate: Option<f64>,
169    pub capped: Option<bool>,
170    pub opening_timestamp: Option<DateTime<Utc>>,
171    pub closing_timestamp: Option<DateTime<Utc>>,
172    pub timestamp: DateTime<Utc>,
173}
174
175/// Raw Order and Balance Data.
176#[derive(Clone, Debug, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct BitmexExecution {
179    #[serde(rename = "execID")]
180    pub exec_id: Uuid,
181    #[serde(rename = "orderID")]
182    pub order_id: Option<Uuid>,
183    #[serde(rename = "clOrdID")]
184    pub cl_ord_id: Option<Ustr>,
185    #[serde(rename = "clOrdLinkID")]
186    pub cl_ord_link_id: Option<Ustr>,
187    pub account: i64,
188    pub symbol: Option<Ustr>,
189    pub side: Option<BitmexSide>,
190    pub last_qty: i64,
191    pub last_px: f64,
192    pub underlying_last_px: Option<f64>,
193    pub last_mkt: Option<Ustr>,
194    pub last_liquidity_ind: Option<BitmexLiquidityIndicator>,
195    pub order_qty: Option<i64>,
196    pub price: Option<f64>,
197    pub display_qty: Option<i64>,
198    pub stop_px: Option<f64>,
199    pub peg_offset_value: Option<f64>,
200    pub peg_price_type: Option<BitmexPegPriceType>,
201    pub currency: Option<Ustr>,
202    pub settl_currency: Option<Ustr>,
203    pub exec_type: BitmexExecType,
204    pub ord_type: BitmexOrderType,
205    pub time_in_force: BitmexTimeInForce,
206    #[serde(default, deserialize_with = "deserialize_exec_instructions")]
207    pub exec_inst: Option<Vec<BitmexExecInstruction>>,
208    pub contingency_type: Option<BitmexContingencyType>,
209    pub ex_destination: Option<Ustr>,
210    pub ord_status: Option<BitmexOrderStatus>,
211    pub triggered: Option<Ustr>,
212    pub working_indicator: Option<bool>,
213    pub ord_rej_reason: Option<Ustr>,
214    pub leaves_qty: Option<i64>,
215    pub cum_qty: Option<i64>,
216    pub avg_px: Option<f64>,
217    pub commission: Option<f64>,
218    pub trade_publish_indicator: Option<Ustr>,
219    pub multi_leg_reporting_type: Option<Ustr>,
220    pub text: Option<Ustr>,
221    pub trd_match_id: Option<Uuid>,
222    pub exec_cost: Option<i64>,
223    pub exec_comm: Option<i64>,
224    pub home_notional: Option<f64>,
225    pub foreign_notional: Option<f64>,
226    pub transact_time: Option<DateTime<Utc>>,
227    pub timestamp: Option<DateTime<Utc>>,
228}
229
230/// Swap Funding History.
231#[derive(Clone, Debug, Serialize, Deserialize)]
232#[serde(rename_all = "camelCase")]
233pub struct BitmexFunding {
234    pub timestamp: DateTime<Utc>,
235    pub symbol: Ustr,
236    pub funding_interval: Option<DateTime<Utc>>,
237    pub funding_rate: Option<f64>,
238    pub funding_rate_daily: Option<f64>,
239}
240
241#[derive(Clone, Debug, Serialize, Deserialize, Default)]
242#[serde(rename_all = "camelCase")]
243pub struct BitmexInstrumentInterval {
244    pub intervals: Vec<String>,
245    pub symbols: Vec<String>,
246}
247
248#[derive(Clone, Debug, Serialize, Deserialize)]
249#[serde(rename_all = "camelCase")]
250pub struct BitmexIndexComposite {
251    pub timestamp: DateTime<Utc>,
252    pub symbol: Option<String>,
253    pub index_symbol: Option<String>,
254    pub reference: Option<String>,
255    pub last_price: Option<f64>,
256    pub weight: Option<f64>,
257    pub logged: Option<DateTime<Utc>>,
258}
259
260/// Insurance Fund Data.
261#[derive(Clone, Debug, Serialize, Deserialize)]
262#[serde(rename_all = "camelCase")]
263pub struct BitmexInsurance {
264    pub currency: Ustr,
265    pub timestamp: DateTime<Utc>,
266    pub wallet_balance: Option<i64>,
267}
268
269/// Active Liquidations.
270#[derive(Clone, Debug, Serialize, Deserialize)]
271#[serde(rename_all = "camelCase")]
272pub struct BitmexLiquidation {
273    #[serde(rename = "orderID")]
274    pub order_id: Uuid,
275    pub symbol: Option<String>,
276    pub side: Option<BitmexSide>,
277    pub price: Option<f64>,
278    pub leaves_qty: Option<i64>,
279}
280
281/// Placement, Cancellation, Amending, and History.
282#[derive(Clone, Debug, Serialize, Deserialize)]
283#[serde(rename_all = "camelCase")]
284pub struct BitmexOrder {
285    #[serde(rename = "orderID")]
286    pub order_id: Uuid,
287    #[serde(rename = "clOrdID")]
288    pub cl_ord_id: Option<Ustr>,
289    #[serde(rename = "clOrdLinkID")]
290    pub cl_ord_link_id: Option<Ustr>,
291    pub account: i64,
292    pub symbol: Option<Ustr>,
293    pub side: Option<BitmexSide>,
294    pub order_qty: Option<i64>,
295    pub price: Option<f64>,
296    pub display_qty: Option<i64>,
297    pub stop_px: Option<f64>,
298    pub peg_offset_value: Option<f64>,
299    pub peg_price_type: Option<BitmexPegPriceType>,
300    pub currency: Option<Ustr>,
301    pub settl_currency: Option<Ustr>,
302    pub ord_type: Option<BitmexOrderType>,
303    pub time_in_force: Option<BitmexTimeInForce>,
304    #[serde(default, deserialize_with = "deserialize_exec_instructions")]
305    pub exec_inst: Option<Vec<BitmexExecInstruction>>,
306    pub contingency_type: Option<BitmexContingencyType>,
307    pub ex_destination: Option<Ustr>,
308    pub ord_status: Option<BitmexOrderStatus>,
309    pub triggered: Option<Ustr>,
310    pub working_indicator: Option<bool>,
311    pub ord_rej_reason: Option<Ustr>,
312    pub leaves_qty: Option<i64>,
313    pub cum_qty: Option<i64>,
314    pub avg_px: Option<f64>,
315    pub multi_leg_reporting_type: Option<Ustr>,
316    pub text: Option<Ustr>,
317    pub transact_time: Option<DateTime<Utc>>,
318    pub timestamp: Option<DateTime<Utc>>,
319}
320
321#[derive(Clone, Debug, Serialize, Deserialize)]
322#[serde(rename_all = "camelCase")]
323pub struct BitmexOrderBookL2 {
324    pub symbol: Ustr,
325    pub id: i64,
326    pub side: BitmexSide,
327    pub size: Option<i64>,
328    pub price: Option<f64>,
329}
330
331/// Position status.
332#[derive(Clone, Debug, Serialize, Deserialize)]
333#[serde(rename_all = "camelCase")]
334pub struct BitmexPosition {
335    pub account: i64,
336    pub symbol: Ustr,
337    pub currency: Option<Ustr>,
338    pub underlying: Option<Ustr>,
339    pub quote_currency: Option<Ustr>,
340    pub commission: Option<f64>,
341    pub init_margin_req: Option<f64>,
342    pub maint_margin_req: Option<f64>,
343    pub risk_limit: Option<i64>,
344    pub leverage: Option<f64>,
345    pub cross_margin: Option<bool>,
346    pub deleverage_percentile: Option<f64>,
347    pub rebalanced_pnl: Option<i64>,
348    pub prev_realised_pnl: Option<i64>,
349    pub prev_unrealised_pnl: Option<i64>,
350    pub prev_close_price: Option<f64>,
351    pub opening_timestamp: Option<DateTime<Utc>>,
352    pub opening_qty: Option<i64>,
353    pub opening_cost: Option<i64>,
354    pub opening_comm: Option<i64>,
355    pub open_order_buy_qty: Option<i64>,
356    pub open_order_buy_cost: Option<i64>,
357    pub open_order_buy_premium: Option<i64>,
358    pub open_order_sell_qty: Option<i64>,
359    pub open_order_sell_cost: Option<i64>,
360    pub open_order_sell_premium: Option<i64>,
361    pub exec_buy_qty: Option<i64>,
362    pub exec_buy_cost: Option<i64>,
363    pub exec_sell_qty: Option<i64>,
364    pub exec_sell_cost: Option<i64>,
365    pub exec_qty: Option<i64>,
366    pub exec_cost: Option<i64>,
367    pub exec_comm: Option<i64>,
368    pub current_timestamp: Option<DateTime<Utc>>,
369    pub current_qty: Option<i64>,
370    pub current_cost: Option<i64>,
371    pub current_comm: Option<i64>,
372    pub realised_cost: Option<i64>,
373    pub unrealised_cost: Option<i64>,
374    pub gross_open_cost: Option<i64>,
375    pub gross_open_premium: Option<i64>,
376    pub gross_exec_cost: Option<i64>,
377    pub is_open: Option<bool>,
378    pub mark_price: Option<f64>,
379    pub mark_value: Option<i64>,
380    pub risk_value: Option<i64>,
381    pub home_notional: Option<f64>,
382    pub foreign_notional: Option<f64>,
383    pub pos_state: Option<Ustr>,
384    pub pos_cost: Option<i64>,
385    pub pos_cost2: Option<i64>,
386    pub pos_cross: Option<i64>,
387    pub pos_init: Option<i64>,
388    pub pos_comm: Option<i64>,
389    pub pos_loss: Option<i64>,
390    pub pos_margin: Option<i64>,
391    pub pos_maint: Option<i64>,
392    pub pos_allowance: Option<i64>,
393    pub taxable_margin: Option<i64>,
394    pub init_margin: Option<i64>,
395    pub maint_margin: Option<i64>,
396    pub session_margin: Option<i64>,
397    pub target_excess_margin: Option<i64>,
398    pub var_margin: Option<i64>,
399    pub realised_gross_pnl: Option<i64>,
400    pub realised_tax: Option<i64>,
401    pub realised_pnl: Option<i64>,
402    pub unrealised_gross_pnl: Option<i64>,
403    pub long_bankrupt: Option<i64>,
404    pub short_bankrupt: Option<i64>,
405    pub tax_base: Option<i64>,
406    pub indicative_tax_rate: Option<f64>,
407    pub indicative_tax: Option<i64>,
408    pub unrealised_tax: Option<i64>,
409    pub unrealised_pnl: Option<i64>,
410    pub unrealised_pnl_pcnt: Option<f64>,
411    pub unrealised_roe_pcnt: Option<f64>,
412    pub avg_cost_price: Option<f64>,
413    pub avg_entry_price: Option<f64>,
414    pub break_even_price: Option<f64>,
415    pub margin_call_price: Option<f64>,
416    pub liquidation_price: Option<f64>,
417    pub bankrupt_price: Option<f64>,
418    pub timestamp: Option<DateTime<Utc>>,
419    pub last_price: Option<f64>,
420    pub last_value: Option<i64>,
421}
422
423/// Best Bid/Offer Snapshots & Historical Bins.
424#[derive(Clone, Debug, Deserialize)]
425#[serde(rename_all = "camelCase")]
426pub struct BitmexQuote {
427    pub timestamp: DateTime<Utc>,
428    pub symbol: Ustr,
429    pub bid_size: Option<i64>,
430    pub bid_price: Option<f64>,
431    pub ask_price: Option<f64>,
432    pub ask_size: Option<i64>,
433}
434
435/// Historical Settlement Data.
436#[derive(Clone, Debug, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct BitmexSettlement {
439    pub timestamp: DateTime<Utc>,
440    pub symbol: Ustr,
441    pub settlement_type: Option<String>,
442    pub settled_price: Option<f64>,
443    pub option_strike_price: Option<f64>,
444    pub option_underlying_price: Option<f64>,
445    pub bankrupt: Option<i64>,
446    pub tax_base: Option<i64>,
447    pub tax_rate: Option<f64>,
448}
449
450/// Exchange Statistics.
451#[derive(Clone, Debug, Deserialize)]
452#[serde(rename_all = "camelCase")]
453pub struct BitmexStats {
454    pub root_symbol: Ustr,
455    pub currency: Option<String>,
456    pub volume24h: Option<i64>,
457    pub turnover24h: Option<i64>,
458    pub open_interest: Option<i64>,
459    pub open_value: Option<i64>,
460}
461
462#[derive(Clone, Debug, Deserialize)]
463#[serde(rename_all = "camelCase")]
464pub struct BitmexStatsHistory {
465    pub date: DateTime<Utc>,
466    pub root_symbol: Ustr,
467    pub currency: Option<String>,
468    pub volume: Option<i64>,
469    pub turnover: Option<i64>,
470}
471
472#[derive(Clone, Debug, Deserialize)]
473#[serde(rename_all = "camelCase")]
474pub struct BitmexStatsUSD {
475    pub root_symbol: Ustr,
476    pub currency: Option<String>,
477    pub turnover24h: Option<i64>,
478    pub turnover30d: Option<i64>,
479    pub turnover365d: Option<i64>,
480    pub turnover: Option<i64>,
481}
482
483/// Individual & Bucketed Trades.
484#[derive(Clone, Debug, Deserialize)]
485#[serde(rename_all = "camelCase")]
486pub struct BitmexTrade {
487    pub timestamp: DateTime<Utc>,
488    pub symbol: Ustr,
489    pub side: Option<BitmexSide>,
490    pub size: i64,
491    pub price: f64,
492    pub tick_direction: Option<String>,
493    #[serde(rename = "trdMatchID")]
494    pub trd_match_id: Option<Uuid>,
495    pub gross_value: Option<i64>,
496    pub home_notional: Option<f64>,
497    pub foreign_notional: Option<f64>,
498}
499
500#[derive(Clone, Debug, Deserialize)]
501#[serde(rename_all = "camelCase")]
502pub struct BitmexTradeBin {
503    pub timestamp: DateTime<Utc>,
504    pub symbol: Ustr,
505    pub open: Option<f64>,
506    pub high: Option<f64>,
507    pub low: Option<f64>,
508    pub close: Option<f64>,
509    pub trades: Option<i64>,
510    pub volume: Option<i64>,
511    pub vwap: Option<f64>,
512    pub last_size: Option<i64>,
513    pub turnover: Option<i64>,
514    pub home_notional: Option<f64>,
515    pub foreign_notional: Option<f64>,
516}
517
518#[derive(Clone, Debug, Deserialize)]
519#[serde(rename_all = "camelCase")]
520pub struct BitmexWallet {
521    pub account: i64,
522    pub currency: Ustr,
523    pub prev_deposited: Option<i64>,
524    pub prev_withdrawn: Option<i64>,
525    pub prev_transfer_in: Option<i64>,
526    pub prev_transfer_out: Option<i64>,
527    pub prev_amount: Option<i64>,
528    pub prev_timestamp: Option<DateTime<Utc>>,
529    pub delta_deposited: Option<i64>,
530    pub delta_withdrawn: Option<i64>,
531    pub delta_transfer_in: Option<i64>,
532    pub delta_transfer_out: Option<i64>,
533    pub delta_amount: Option<i64>,
534    pub deposited: Option<i64>,
535    pub withdrawn: Option<i64>,
536    pub transfer_in: Option<i64>,
537    pub transfer_out: Option<i64>,
538    pub amount: Option<i64>,
539    pub pending_credit: Option<i64>,
540    pub pending_debit: Option<i64>,
541    pub confirmed_debit: Option<i64>,
542    pub timestamp: Option<DateTime<Utc>>,
543    pub addr: Option<Ustr>,
544    pub script: Option<Ustr>,
545    pub withdrawal_lock: Option<Vec<Ustr>>,
546}
547
548#[derive(Clone, Debug, Deserialize, Default)]
549#[serde(rename_all = "camelCase")]
550pub struct BitmexTransaction {
551    pub transact_id: Option<Uuid>,
552    pub account: Option<i64>,
553    pub currency: Option<Ustr>,
554    pub transact_type: Option<Ustr>,
555    pub amount: Option<i64>,
556    pub fee: Option<i64>,
557    pub transact_status: Option<Ustr>,
558    pub address: Option<Ustr>,
559    pub tx: Option<Ustr>,
560    pub text: Option<Ustr>,
561    pub transact_time: Option<DateTime<Utc>>,
562    pub timestamp: Option<DateTime<Utc>>,
563}
564
565/// Public Announcements.
566#[derive(Clone, Debug, Deserialize)]
567#[serde(rename_all = "camelCase")]
568pub struct BitmexAnnouncement {
569    pub id: i32,
570    pub link: Option<String>,
571    pub title: Option<String>,
572    pub content: Option<String>,
573    pub date: Option<DateTime<Utc>>,
574}
575
576/// Persistent API Keys for Developers.
577#[derive(Clone, Debug, Deserialize)]
578#[serde(rename_all = "camelCase")]
579pub struct BitmexAPIKey {
580    pub id: String,
581    pub secret: Option<String>,
582    pub name: String,
583    pub nonce: i64,
584    pub cidr: Option<String>,
585    pub permissions: Vec<serde_json::Value>,
586    pub enabled: Option<bool>,
587    pub user_id: i32,
588    pub created: Option<DateTime<Utc>>,
589}
590
591/// Account Notifications.
592#[derive(Clone, Debug, Deserialize)]
593#[serde(rename_all = "camelCase")]
594pub struct BitmexGlobalNotification {
595    pub id: Option<i32>,
596    pub date: DateTime<Utc>,
597    pub title: String,
598    pub body: String,
599    pub ttl: i32,
600    pub r#type: Option<String>,
601    pub closable: Option<bool>,
602    pub persist: Option<bool>,
603    pub wait_for_visibility: Option<bool>,
604    pub sound: Option<String>,
605}
606
607#[derive(Clone, Debug, Deserialize)]
608#[serde(rename_all = "camelCase")]
609pub struct BitmexAccessToken {
610    pub id: String,
611    /// The time to live in seconds (2 weeks by default).
612    pub ttl: Option<f64>,
613    pub created: Option<DateTime<Utc>>,
614    pub user_id: Option<f64>,
615}
616
617/// Daily Quote Fill Ratio Statistic.
618#[derive(Clone, Debug, Deserialize)]
619#[serde(rename_all = "camelCase")]
620pub struct BitmexQuoteFillRatio {
621    pub date: DateTime<Utc>,
622    pub account: Option<f64>,
623    pub quote_count: Option<f64>,
624    pub dealt_count: Option<f64>,
625    pub quotes_mavg7: Option<f64>,
626    pub dealt_mavg7: Option<f64>,
627    pub quote_fill_ratio_mavg7: Option<f64>,
628}
629
630/// Account Operations.
631#[derive(Clone, Debug, Deserialize)]
632#[serde(rename_all = "camelCase")]
633pub struct BitmexUser {
634    pub id: Option<i32>,
635    pub owner_id: Option<i32>,
636    pub firstname: Option<String>,
637    pub lastname: Option<String>,
638    pub username: String,
639    pub email: String,
640    pub phone: Option<String>,
641    pub created: Option<DateTime<Utc>>,
642    pub last_updated: Option<DateTime<Utc>>,
643    pub preferences: BitmexUserPreferences,
644    #[serde(rename = "TFAEnabled")]
645    pub tfa_enabled: Option<String>,
646    #[serde(rename = "affiliateID")]
647    pub affiliate_id: Option<String>,
648    pub pgp_pub_key: Option<String>,
649    pub country: Option<String>,
650    pub geoip_country: Option<String>,
651    pub geoip_region: Option<String>,
652    pub typ: Option<String>,
653}
654
655#[derive(Clone, Debug, Deserialize)]
656#[serde(rename_all = "camelCase")]
657pub struct BitmexMargin {
658    pub account: i64,
659    pub currency: Ustr,
660    pub risk_limit: Option<i64>,
661    pub prev_state: Option<String>,
662    pub state: Option<String>,
663    pub action: Option<String>,
664    pub amount: Option<i64>,
665    pub pending_credit: Option<i64>,
666    pub pending_debit: Option<i64>,
667    pub confirmed_debit: Option<i64>,
668    pub prev_realised_pnl: Option<i64>,
669    pub prev_unrealised_pnl: Option<i64>,
670    pub gross_comm: Option<i64>,
671    pub gross_open_cost: Option<i64>,
672    pub gross_open_premium: Option<i64>,
673    pub gross_exec_cost: Option<i64>,
674    pub gross_mark_value: Option<i64>,
675    pub risk_value: Option<i64>,
676    pub taxable_margin: Option<i64>,
677    pub init_margin: Option<i64>,
678    pub maint_margin: Option<i64>,
679    pub session_margin: Option<i64>,
680    pub target_excess_margin: Option<i64>,
681    pub var_margin: Option<i64>,
682    pub realised_pnl: Option<i64>,
683    pub unrealised_pnl: Option<i64>,
684    pub indicative_tax: Option<i64>,
685    pub unrealised_profit: Option<i64>,
686    pub synthetic_margin: Option<i64>,
687    pub wallet_balance: Option<i64>,
688    pub margin_balance: Option<i64>,
689    pub margin_balance_pcnt: Option<f64>,
690    pub margin_leverage: Option<f64>,
691    pub margin_used_pcnt: Option<f64>,
692    pub excess_margin: Option<i64>,
693    pub excess_margin_pcnt: Option<f64>,
694    pub available_margin: Option<i64>,
695    pub withdrawable_margin: Option<i64>,
696    pub timestamp: Option<DateTime<Utc>>,
697    pub gross_last_value: Option<i64>,
698    pub commission: Option<f64>,
699}
700
701/// User communication SNS token.
702#[derive(Clone, Debug, Deserialize)]
703#[serde(rename_all = "camelCase")]
704pub struct BitmexCommunicationToken {
705    pub id: String,
706    #[serde(rename = "userId")]
707    pub user_id: i32,
708    #[serde(rename = "deviceToken")]
709    pub device_token: String,
710    pub channel: String,
711}
712
713/// User Events for auditing.
714#[derive(Clone, Debug, Deserialize)]
715#[serde(rename_all = "camelCase")]
716pub struct BitmexUserEvent {
717    pub id: Option<f64>,
718    #[serde(rename = "type")]
719    pub r#type: String,
720    pub status: String,
721    #[serde(rename = "userId")]
722    pub user_id: f64,
723    #[serde(rename = "createdById")]
724    pub created_by_id: Option<f64>,
725    pub ip: Option<String>,
726    #[serde(rename = "geoipCountry")]
727    pub geoip_country: Option<String>,
728    #[serde(rename = "geoipRegion")]
729    pub geoip_region: Option<String>,
730    #[serde(rename = "geoipSubRegion")]
731    pub geoip_sub_region: Option<String>,
732    #[serde(rename = "eventMeta")]
733    pub event_meta: Option<BitmexEventMetaEventMeta>,
734    pub created: DateTime<Utc>,
735}
736
737#[derive(Clone, Debug, Deserialize, Default)]
738#[allow(dead_code)]
739pub struct BitmexEventMetaEventMeta(serde_json::Value);
740
741#[derive(Clone, Debug, Deserialize, Default)]
742#[serde(rename_all = "camelCase")]
743pub struct BitmexUserPreferences {
744    pub alert_on_liquidations: Option<bool>,
745    pub animations_enabled: Option<bool>,
746    pub announcements_last_seen: Option<DateTime<Utc>>,
747    pub chat_channel_id: Option<f64>,
748    pub color_theme: Option<String>,
749    pub currency: Option<Ustr>,
750    pub debug: Option<bool>,
751    pub disable_emails: Option<Vec<String>>,
752    pub disable_push: Option<Vec<String>>,
753    pub hide_confirm_dialogs: Option<Vec<String>>,
754    pub hide_connection_modal: Option<bool>,
755    pub hide_from_leaderboard: Option<bool>,
756    pub hide_name_from_leaderboard: Option<bool>,
757    pub hide_notifications: Option<Vec<String>>,
758    pub locale: Option<String>,
759    pub msgs_seen: Option<Vec<String>>,
760    pub order_book_binning: Option<BitmexOrderBookBinning>,
761    pub order_book_type: Option<String>,
762    pub order_clear_immediate: Option<bool>,
763    pub order_controls_plus_minus: Option<bool>,
764    pub show_locale_numbers: Option<bool>,
765    pub sounds: Option<Vec<String>>,
766    #[serde(rename = "strictIPCheck")]
767    pub strict_ip_check: Option<bool>,
768    pub strict_timeout: Option<bool>,
769    pub ticker_group: Option<String>,
770    pub ticker_pinned: Option<bool>,
771    pub trade_layout: Option<String>,
772}
773
774#[derive(Clone, Debug, Deserialize, Default)]
775#[allow(dead_code)]
776pub struct BitmexOrderBookBinning(serde_json::Value);
777
778/// Represents the response from `GET /api/v1` (root endpoint).
779#[derive(Clone, Debug, Deserialize)]
780#[serde(rename_all = "camelCase")]
781pub struct BitmexApiInfo {
782    /// API name.
783    pub name: String,
784    /// API version.
785    pub version: String,
786    /// Server timestamp in milliseconds.
787    pub timestamp: u64,
788}
789
790////////////////////////////////////////////////////////////////////////////////
791// Tests
792////////////////////////////////////////////////////////////////////////////////
793
794#[cfg(test)]
795mod tests {
796    use rstest::rstest;
797    use serde_json::json;
798
799    use super::*;
800
801    #[rstest]
802    #[case(json!(null), None)]
803    #[case(json!(""), None)]
804    #[case(json!("ParticipateDoNotInitiate"), Some(vec![BitmexExecInstruction::ParticipateDoNotInitiate]))]
805    #[case(json!("ReduceOnly"), Some(vec![BitmexExecInstruction::ReduceOnly]))]
806    #[case(json!("LastPrice,Close"), Some(vec![BitmexExecInstruction::LastPrice, BitmexExecInstruction::Close]))]
807    #[case(
808        json!("ParticipateDoNotInitiate,ReduceOnly"),
809        Some(vec![BitmexExecInstruction::ParticipateDoNotInitiate, BitmexExecInstruction::ReduceOnly])
810    )]
811    #[case(
812        json!("MarkPrice,IndexPrice,AllOrNone"),
813        Some(vec![BitmexExecInstruction::MarkPrice, BitmexExecInstruction::IndexPrice, BitmexExecInstruction::AllOrNone])
814    )]
815    #[case(json!("Fixed"), Some(vec![BitmexExecInstruction::Fixed]))]
816    fn test_deserialize_exec_instructions(
817        #[case] input: serde_json::Value,
818        #[case] expected: Option<Vec<BitmexExecInstruction>>,
819    ) {
820        #[derive(Deserialize)]
821        struct TestStruct {
822            #[serde(default, deserialize_with = "deserialize_exec_instructions")]
823            exec_inst: Option<Vec<BitmexExecInstruction>>,
824        }
825
826        let test_json = json!({
827            "exec_inst": input
828        });
829
830        let result: TestStruct = serde_json::from_value(test_json).unwrap();
831        assert_eq!(result.exec_inst, expected);
832    }
833
834    #[rstest]
835    fn test_deserialize_exec_instructions_with_spaces() {
836        #[derive(Deserialize)]
837        struct TestStruct {
838            #[serde(default, deserialize_with = "deserialize_exec_instructions")]
839            exec_inst: Option<Vec<BitmexExecInstruction>>,
840        }
841
842        let test_json = json!({
843            "exec_inst": "LastPrice , Close , ReduceOnly"
844        });
845
846        let result: TestStruct = serde_json::from_value(test_json).unwrap();
847        assert_eq!(
848            result.exec_inst,
849            Some(vec![
850                BitmexExecInstruction::LastPrice,
851                BitmexExecInstruction::Close,
852                BitmexExecInstruction::ReduceOnly,
853            ])
854        );
855    }
856
857    #[rstest]
858    fn test_deserialize_order_with_exec_instructions() {
859        let order_json = json!({
860            "account": 123456,
861            "symbol": "XBTUSD",
862            "orderID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
863            "side": "Buy",
864            "ordType": "Limit",
865            "timeInForce": "GoodTillCancel",
866            "ordStatus": "New",
867            "orderQty": 100,
868            "cumQty": 0,
869            "price": 50000.0,
870            "execInst": "ParticipateDoNotInitiate,ReduceOnly",
871            "transactTime": "2024-01-01T00:00:00.000Z",
872            "timestamp": "2024-01-01T00:00:00.000Z"
873        });
874
875        let order: BitmexOrder = serde_json::from_value(order_json).unwrap();
876        assert_eq!(
877            order.exec_inst,
878            Some(vec![
879                BitmexExecInstruction::ParticipateDoNotInitiate,
880                BitmexExecInstruction::ReduceOnly,
881            ])
882        );
883    }
884}