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