Skip to main content

nautilus_bitmex/http/
models.rs

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