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