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