1use ahash::AHashMap;
22use nautilus_core::serialization::{decimal, deserialize_optional_decimal_token_borrowed};
23use rust_decimal::Decimal;
24use serde::{Deserialize, Serialize};
25use ustr::Ustr;
26
27use crate::common::serialization::{
28 deserialize_decimal_token_or_zero_borrowed, deserialize_decimal_token_pairs_borrowed,
29 deserialize_decimal_token_vec_borrowed,
30};
31pub use crate::common::{
32 enums::{DeribitCurrency, DeribitOptionType, DeribitProductType},
33 models::DeribitTradeLeg,
34 rpc::{DeribitJsonRpcError, DeribitJsonRpcRequest, DeribitJsonRpcResponse},
35};
36
37#[derive(Debug, Serialize, Deserialize)]
39#[serde(untagged)]
40pub enum DeribitResponsePayload<T> {
41 Success { result: T },
43 Error { error: DeribitJsonRpcError },
45}
46
47#[derive(Clone, Debug, Serialize, Deserialize)]
49pub struct DeribitInstrument {
50 pub base_currency: Ustr,
52 #[serde(
54 default,
55 deserialize_with = "deserialize_optional_decimal_token_borrowed"
56 )]
57 pub block_trade_commission: Option<Decimal>,
58 #[serde(
60 default,
61 deserialize_with = "deserialize_optional_decimal_token_borrowed"
62 )]
63 pub block_trade_min_trade_amount: Option<Decimal>,
64 #[serde(
66 default,
67 deserialize_with = "deserialize_optional_decimal_token_borrowed"
68 )]
69 pub block_trade_tick_size: Option<Decimal>,
70 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
72 pub contract_size: Decimal,
73 pub counter_currency: Option<Ustr>,
75 pub creation_timestamp: i64,
77 pub expiration_timestamp: Option<i64>,
79 pub future_type: Option<String>,
81 pub instrument_id: i64,
83 pub instrument_name: Ustr,
85 pub instrument_type: Option<String>,
87 pub is_active: bool,
89 pub kind: DeribitProductType,
91 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
93 pub maker_commission: Decimal,
94 pub max_leverage: Option<i64>,
96 #[serde(
98 default,
99 deserialize_with = "deserialize_optional_decimal_token_borrowed"
100 )]
101 pub max_liquidation_commission: Option<Decimal>,
102 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
104 pub min_trade_amount: Decimal,
105 pub option_type: Option<DeribitOptionType>,
107 pub price_index: Option<String>,
109 pub quote_currency: Ustr,
111 pub settlement_currency: Option<Ustr>,
113 pub settlement_period: Option<String>,
115 #[serde(
117 default,
118 deserialize_with = "deserialize_optional_decimal_token_borrowed"
119 )]
120 pub strike: Option<Decimal>,
121 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
123 pub taker_commission: Decimal,
124 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
126 pub tick_size: Decimal,
127 pub tick_size_steps: Option<Vec<DeribitTickSizeStep>>,
129}
130
131#[derive(Clone, Debug, Serialize, Deserialize)]
133pub struct DeribitTickSizeStep {
134 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
136 pub above_price: Decimal,
137 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
139 pub tick_size: Decimal,
140}
141
142#[derive(Clone, Debug, Serialize, Deserialize)]
144pub struct DeribitCombo {
145 pub id: Ustr,
147 pub state: String,
149 pub legs: Vec<DeribitComboLeg>,
151 pub creation_timestamp: i64,
153 pub instrument_id: i64,
155 pub state_timestamp: i64,
157}
158
159#[derive(Clone, Debug, Serialize, Deserialize)]
161pub struct DeribitComboLeg {
162 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
164 pub amount: Decimal,
165 pub instrument_name: Ustr,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct DeribitAccountSummariesResponse {
175 pub summaries: Vec<DeribitAccountSummary>,
177 #[serde(default)]
179 pub id: Option<i64>,
180 #[serde(default)]
182 pub email: Option<String>,
183 #[serde(default)]
185 pub system_name: Option<String>,
186 #[serde(default)]
188 pub username: Option<String>,
189 #[serde(rename = "type", default)]
191 pub account_type: Option<String>,
192 #[serde(default)]
194 pub creation_timestamp: Option<i64>,
195 #[serde(default)]
197 pub referrer_id: Option<String>,
198 #[serde(default)]
200 pub login_enabled: Option<bool>,
201 #[serde(default)]
203 pub security_keys_enabled: Option<bool>,
204 #[serde(default)]
206 pub mmp_enabled: Option<bool>,
207 #[serde(default)]
209 pub interuser_transfers_enabled: Option<bool>,
210 #[serde(default)]
212 pub self_trading_reject_mode: Option<String>,
213 #[serde(default)]
215 pub self_trading_extended_to_subaccounts: Option<bool>,
216 #[serde(default)]
218 pub block_rfq_self_match_prevention: Option<bool>,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct DeribitAccountSummary {
226 pub currency: Ustr,
228 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
230 pub equity: Decimal,
231 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
233 pub balance: Decimal,
234 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
236 pub available_funds: Decimal,
237 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
239 pub margin_balance: Decimal,
240 #[serde(
242 default,
243 deserialize_with = "deserialize_optional_decimal_token_borrowed"
244 )]
245 pub initial_margin: Option<Decimal>,
246 #[serde(
248 default,
249 deserialize_with = "deserialize_optional_decimal_token_borrowed"
250 )]
251 pub maintenance_margin: Option<Decimal>,
252 #[serde(
254 default,
255 deserialize_with = "deserialize_optional_decimal_token_borrowed"
256 )]
257 pub total_pl: Option<Decimal>,
258 #[serde(
260 default,
261 deserialize_with = "deserialize_optional_decimal_token_borrowed"
262 )]
263 pub session_upl: Option<Decimal>,
264 #[serde(
266 default,
267 deserialize_with = "deserialize_optional_decimal_token_borrowed"
268 )]
269 pub session_rpl: Option<Decimal>,
270 #[serde(default)]
272 pub portfolio_margining_enabled: Option<bool>,
273 #[serde(default)]
275 pub margin_model: Option<String>,
276 #[serde(default)]
278 pub cross_collateral_enabled: Option<bool>,
279 #[serde(
281 default,
282 deserialize_with = "deserialize_optional_decimal_token_borrowed"
283 )]
284 pub available_withdrawal_funds: Option<Decimal>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct DeribitAccountSummaryExtended {
294 pub currency: Ustr,
296 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
298 pub equity: Decimal,
299 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
301 pub balance: Decimal,
302 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
304 pub available_funds: Decimal,
305 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
307 pub margin_balance: Decimal,
308 #[serde(
310 default,
311 deserialize_with = "deserialize_optional_decimal_token_borrowed"
312 )]
313 pub initial_margin: Option<Decimal>,
314 #[serde(
316 default,
317 deserialize_with = "deserialize_optional_decimal_token_borrowed"
318 )]
319 pub maintenance_margin: Option<Decimal>,
320 #[serde(
322 default,
323 deserialize_with = "deserialize_optional_decimal_token_borrowed"
324 )]
325 pub total_pl: Option<Decimal>,
326 #[serde(
328 default,
329 deserialize_with = "deserialize_optional_decimal_token_borrowed"
330 )]
331 pub session_upl: Option<Decimal>,
332 #[serde(
334 default,
335 deserialize_with = "deserialize_optional_decimal_token_borrowed"
336 )]
337 pub session_rpl: Option<Decimal>,
338 #[serde(default)]
340 pub portfolio_margining_enabled: Option<bool>,
341 #[serde(default)]
344 pub id: Option<i64>,
345 #[serde(default)]
347 pub email: Option<String>,
348 #[serde(default)]
350 pub username: Option<String>,
351 #[serde(default)]
353 pub system_name: Option<String>,
354 #[serde(rename = "type", default)]
356 pub account_type: Option<String>,
357 #[serde(
359 default,
360 deserialize_with = "deserialize_optional_decimal_token_borrowed"
361 )]
362 pub futures_session_upl: Option<Decimal>,
363 #[serde(
365 default,
366 deserialize_with = "deserialize_optional_decimal_token_borrowed"
367 )]
368 pub futures_session_rpl: Option<Decimal>,
369 #[serde(
371 default,
372 deserialize_with = "deserialize_optional_decimal_token_borrowed"
373 )]
374 pub options_session_upl: Option<Decimal>,
375 #[serde(
377 default,
378 deserialize_with = "deserialize_optional_decimal_token_borrowed"
379 )]
380 pub options_session_rpl: Option<Decimal>,
381 #[serde(
383 default,
384 deserialize_with = "deserialize_optional_decimal_token_borrowed"
385 )]
386 pub futures_pl: Option<Decimal>,
387 #[serde(
389 default,
390 deserialize_with = "deserialize_optional_decimal_token_borrowed"
391 )]
392 pub options_pl: Option<Decimal>,
393 #[serde(
395 default,
396 deserialize_with = "deserialize_optional_decimal_token_borrowed"
397 )]
398 pub options_delta: Option<Decimal>,
399 #[serde(
401 default,
402 deserialize_with = "deserialize_optional_decimal_token_borrowed"
403 )]
404 pub options_gamma: Option<Decimal>,
405 #[serde(
407 default,
408 deserialize_with = "deserialize_optional_decimal_token_borrowed"
409 )]
410 pub options_vega: Option<Decimal>,
411 #[serde(
413 default,
414 deserialize_with = "deserialize_optional_decimal_token_borrowed"
415 )]
416 pub options_theta: Option<Decimal>,
417 #[serde(
419 default,
420 deserialize_with = "deserialize_optional_decimal_token_borrowed"
421 )]
422 pub options_value: Option<Decimal>,
423 #[serde(
425 default,
426 deserialize_with = "deserialize_optional_decimal_token_borrowed"
427 )]
428 pub delta_total: Option<Decimal>,
429 #[serde(
431 default,
432 deserialize_with = "deserialize_optional_decimal_token_borrowed"
433 )]
434 pub projected_delta_total: Option<Decimal>,
435 #[serde(
437 default,
438 deserialize_with = "deserialize_optional_decimal_token_borrowed"
439 )]
440 pub projected_initial_margin: Option<Decimal>,
441 #[serde(
443 default,
444 deserialize_with = "deserialize_optional_decimal_token_borrowed"
445 )]
446 pub projected_maintenance_margin: Option<Decimal>,
447 #[serde(
449 default,
450 deserialize_with = "deserialize_optional_decimal_token_borrowed"
451 )]
452 pub estimated_liquidation_ratio: Option<Decimal>,
453 #[serde(
455 default,
456 deserialize_with = "deserialize_optional_decimal_token_borrowed"
457 )]
458 pub available_withdrawal_funds: Option<Decimal>,
459 #[serde(
461 default,
462 deserialize_with = "deserialize_optional_decimal_token_borrowed"
463 )]
464 pub spot_reserve: Option<Decimal>,
465 #[serde(
467 default,
468 deserialize_with = "deserialize_optional_decimal_token_borrowed"
469 )]
470 pub fee_balance: Option<Decimal>,
471 #[serde(default)]
473 pub margin_model: Option<String>,
474 #[serde(default)]
476 pub cross_collateral_enabled: Option<bool>,
477 #[serde(default)]
479 pub creation_timestamp: Option<i64>,
480 #[serde(default)]
482 pub login_enabled: Option<bool>,
483 #[serde(default)]
485 pub security_keys_enabled: Option<bool>,
486 #[serde(default)]
488 pub mmp_enabled: Option<bool>,
489 #[serde(default)]
491 pub interuser_transfers_enabled: Option<bool>,
492 #[serde(default)]
494 pub self_trading_reject_mode: Option<String>,
495 #[serde(default)]
497 pub self_trading_extended_to_subaccounts: Option<bool>,
498 #[serde(default)]
500 pub referrer_id: Option<String>,
501 #[serde(default)]
503 pub block_rfq_self_match_prevention: Option<bool>,
504}
505
506#[derive(Clone, Debug, Serialize, Deserialize)]
511pub struct DeribitPublicTrade {
512 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
515 pub amount: Decimal,
516 #[serde(
518 default,
519 deserialize_with = "deserialize_optional_decimal_token_borrowed"
520 )]
521 pub contracts: Option<Decimal>,
522 pub direction: String,
524 #[serde(
526 default,
527 deserialize_with = "deserialize_optional_decimal_token_borrowed"
528 )]
529 pub index_price: Option<Decimal>,
530 pub instrument_name: String,
532 #[serde(
534 default,
535 deserialize_with = "deserialize_optional_decimal_token_borrowed"
536 )]
537 pub iv: Option<Decimal>,
538 #[serde(default)]
540 pub liquidation: Option<String>,
541 #[serde(
543 default,
544 deserialize_with = "deserialize_optional_decimal_token_borrowed"
545 )]
546 pub mark_price: Option<Decimal>,
547 #[serde(deserialize_with = "deserialize_decimal_token_or_zero_borrowed")]
549 pub price: Decimal,
550 pub tick_direction: i32,
552 pub timestamp: i64,
554 pub trade_id: String,
556 pub trade_seq: i64,
558 #[serde(default)]
560 pub block_trade_id: Option<String>,
561 #[serde(default)]
563 pub block_trade_leg_count: Option<i32>,
564 #[serde(default)]
566 pub block_rfq_id: Option<i64>,
567 #[serde(default)]
569 pub combo_id: Option<String>,
570 #[serde(default)]
572 pub combo_trade_id: Option<String>,
573 #[serde(default)]
575 pub legs: Option<Vec<DeribitTradeLeg>>,
576}
577
578#[derive(Clone, Debug, Serialize, Deserialize)]
582pub struct DeribitTradesResponse {
583 pub has_more: bool,
585 pub trades: Vec<DeribitPublicTrade>,
587}
588
589#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
591pub struct DeribitExpirationsByKind {
592 #[serde(default)]
594 pub future: Vec<String>,
595 #[serde(default)]
597 pub option: Vec<String>,
598 #[serde(default)]
600 pub future_combo: Vec<String>,
601 #[serde(default)]
603 pub option_combo: Vec<String>,
604}
605
606#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
611#[serde(untagged)]
612pub enum DeribitExpirationsResponse {
613 ByCurrency(AHashMap<String, DeribitExpirationsByKind>),
615 Expirations(DeribitExpirationsByKind),
617}
618
619impl DeribitExpirationsResponse {
620 #[must_use]
622 pub fn expirations_for_currency(&self, currency: &str) -> Option<&DeribitExpirationsByKind> {
623 match self {
624 Self::ByCurrency(expirations) => {
625 let key = currency.to_ascii_lowercase();
626 expirations.get(&key)
627 }
628 Self::Expirations(expirations) => Some(expirations),
629 }
630 }
631}
632
633#[derive(Clone, Debug, Serialize, Deserialize)]
638pub struct DeribitTradingViewChartData {
639 #[serde(deserialize_with = "deserialize_decimal_token_vec_borrowed")]
641 pub close: Vec<Decimal>,
642 #[serde(deserialize_with = "deserialize_decimal_token_vec_borrowed")]
644 pub cost: Vec<Decimal>,
645 #[serde(deserialize_with = "deserialize_decimal_token_vec_borrowed")]
647 pub high: Vec<Decimal>,
648 #[serde(deserialize_with = "deserialize_decimal_token_vec_borrowed")]
650 pub low: Vec<Decimal>,
651 #[serde(deserialize_with = "deserialize_decimal_token_vec_borrowed")]
653 pub open: Vec<Decimal>,
654 pub status: String,
656 pub ticks: Vec<i64>,
658 #[serde(deserialize_with = "deserialize_decimal_token_vec_borrowed")]
660 pub volume: Vec<Decimal>,
661}
662
663#[derive(Clone, Debug, Serialize, Deserialize)]
667pub struct DeribitOrderBook {
668 pub timestamp: i64,
670 pub instrument_name: String,
672 #[serde(deserialize_with = "deserialize_decimal_token_pairs_borrowed")]
674 pub bids: Vec<[Decimal; 2]>,
675 #[serde(deserialize_with = "deserialize_decimal_token_pairs_borrowed")]
677 pub asks: Vec<[Decimal; 2]>,
678 pub state: String,
680 #[serde(
682 default,
683 deserialize_with = "deserialize_optional_decimal_token_borrowed"
684 )]
685 pub best_bid_price: Option<Decimal>,
686 #[serde(
688 default,
689 deserialize_with = "deserialize_optional_decimal_token_borrowed"
690 )]
691 pub best_ask_price: Option<Decimal>,
692 #[serde(
694 default,
695 deserialize_with = "deserialize_optional_decimal_token_borrowed"
696 )]
697 pub best_bid_amount: Option<Decimal>,
698 #[serde(
700 default,
701 deserialize_with = "deserialize_optional_decimal_token_borrowed"
702 )]
703 pub best_ask_amount: Option<Decimal>,
704 #[serde(
706 default,
707 deserialize_with = "deserialize_optional_decimal_token_borrowed"
708 )]
709 pub mark_price: Option<Decimal>,
710 #[serde(
712 default,
713 deserialize_with = "deserialize_optional_decimal_token_borrowed"
714 )]
715 pub last_price: Option<Decimal>,
716 #[serde(
718 default,
719 deserialize_with = "deserialize_optional_decimal_token_borrowed"
720 )]
721 pub index_price: Option<Decimal>,
722 #[serde(
724 default,
725 deserialize_with = "deserialize_optional_decimal_token_borrowed"
726 )]
727 pub open_interest: Option<Decimal>,
728 #[serde(
730 default,
731 deserialize_with = "deserialize_optional_decimal_token_borrowed"
732 )]
733 pub max_price: Option<Decimal>,
734 #[serde(
736 default,
737 deserialize_with = "deserialize_optional_decimal_token_borrowed"
738 )]
739 pub min_price: Option<Decimal>,
740 #[serde(
742 default,
743 deserialize_with = "deserialize_optional_decimal_token_borrowed"
744 )]
745 pub current_funding: Option<Decimal>,
746 #[serde(
748 default,
749 deserialize_with = "deserialize_optional_decimal_token_borrowed"
750 )]
751 pub funding_8h: Option<Decimal>,
752 #[serde(
754 default,
755 deserialize_with = "deserialize_optional_decimal_token_borrowed"
756 )]
757 pub settlement_price: Option<Decimal>,
758 #[serde(
760 default,
761 deserialize_with = "deserialize_optional_decimal_token_borrowed"
762 )]
763 pub delivery_price: Option<Decimal>,
764 #[serde(
766 default,
767 deserialize_with = "deserialize_optional_decimal_token_borrowed"
768 )]
769 pub bid_iv: Option<Decimal>,
770 #[serde(
772 default,
773 deserialize_with = "deserialize_optional_decimal_token_borrowed"
774 )]
775 pub ask_iv: Option<Decimal>,
776 #[serde(
778 default,
779 deserialize_with = "deserialize_optional_decimal_token_borrowed"
780 )]
781 pub mark_iv: Option<Decimal>,
782 #[serde(
784 default,
785 deserialize_with = "deserialize_optional_decimal_token_borrowed"
786 )]
787 pub underlying_price: Option<Decimal>,
788 #[serde(default)]
790 pub underlying_index: Option<serde_json::Value>,
791 #[serde(
793 default,
794 deserialize_with = "deserialize_optional_decimal_token_borrowed"
795 )]
796 pub interest_rate: Option<Decimal>,
797}
798
799#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
804pub struct DeribitBookSummaryRaw {
805 pub instrument_name: String,
807 #[serde(
809 default,
810 deserialize_with = "deserialize_optional_decimal_token_borrowed"
811 )]
812 pub underlying_price: Option<Decimal>,
813 #[serde(default)]
815 pub underlying_index: Option<String>,
816 #[serde(
818 default,
819 deserialize_with = "deserialize_optional_decimal_token_borrowed"
820 )]
821 pub mark_price: Option<Decimal>,
822 #[serde(
824 default,
825 deserialize_with = "deserialize_optional_decimal_token_borrowed"
826 )]
827 pub mid_price: Option<Decimal>,
828 #[serde(
830 default,
831 deserialize_with = "deserialize_optional_decimal_token_borrowed"
832 )]
833 pub bid_price: Option<Decimal>,
834 #[serde(
836 default,
837 deserialize_with = "deserialize_optional_decimal_token_borrowed"
838 )]
839 pub ask_price: Option<Decimal>,
840 #[serde(
842 default,
843 rename = "last",
844 deserialize_with = "deserialize_optional_decimal_token_borrowed"
845 )]
846 pub last_price: Option<Decimal>,
847 #[serde(
849 default,
850 deserialize_with = "deserialize_optional_decimal_token_borrowed"
851 )]
852 pub mark_iv: Option<Decimal>,
853 #[serde(
855 default,
856 deserialize_with = "deserialize_optional_decimal_token_borrowed"
857 )]
858 pub bid_iv: Option<Decimal>,
859 #[serde(
861 default,
862 deserialize_with = "deserialize_optional_decimal_token_borrowed"
863 )]
864 pub ask_iv: Option<Decimal>,
865 #[serde(
867 default,
868 deserialize_with = "deserialize_optional_decimal_token_borrowed"
869 )]
870 pub interest_rate: Option<Decimal>,
871 #[serde(
873 default,
874 deserialize_with = "deserialize_optional_decimal_token_borrowed"
875 )]
876 pub open_interest: Option<Decimal>,
877 #[serde(
879 default,
880 deserialize_with = "deserialize_optional_decimal_token_borrowed"
881 )]
882 pub open_interest_value: Option<Decimal>,
883 #[serde(
885 default,
886 deserialize_with = "deserialize_optional_decimal_token_borrowed"
887 )]
888 pub volume: Option<Decimal>,
889 #[serde(
891 default,
892 deserialize_with = "deserialize_optional_decimal_token_borrowed"
893 )]
894 pub volume_usd: Option<Decimal>,
895 #[serde(
897 default,
898 deserialize_with = "deserialize_optional_decimal_token_borrowed"
899 )]
900 pub volume_notional: Option<Decimal>,
901 #[serde(
903 default,
904 deserialize_with = "deserialize_optional_decimal_token_borrowed"
905 )]
906 pub volume_btc: Option<Decimal>,
907 #[serde(
909 default,
910 deserialize_with = "deserialize_optional_decimal_token_borrowed"
911 )]
912 pub high: Option<Decimal>,
913 #[serde(
915 default,
916 deserialize_with = "deserialize_optional_decimal_token_borrowed"
917 )]
918 pub low: Option<Decimal>,
919 #[serde(
921 default,
922 rename = "price_change",
923 deserialize_with = "deserialize_optional_decimal_token_borrowed"
924 )]
925 pub price_change: Option<Decimal>,
926 #[serde(
928 default,
929 deserialize_with = "deserialize_optional_decimal_token_borrowed"
930 )]
931 pub estimated_delivery_price: Option<Decimal>,
932 #[serde(
934 default,
935 deserialize_with = "deserialize_optional_decimal_token_borrowed"
936 )]
937 pub delivery_price: Option<Decimal>,
938 #[serde(default)]
940 pub base_currency: Option<String>,
941 #[serde(default)]
943 pub quote_currency: Option<String>,
944 #[serde(default)]
946 pub creation_timestamp: i64,
947}
948
949#[cfg(test)]
950mod book_summary_tests {
951 use rstest::rstest;
952 use rust_decimal_macros::dec;
953
954 use super::*;
955
956 #[rstest]
957 fn test_book_summary_raw_deserializes_full_option_payload() {
958 let json = r#"{
959 "instrument_name": "BTC-28MAR25-90000-C",
960 "underlying_price": 95000.5,
961 "underlying_index": "SYN.BTC-28MAR25",
962 "mark_price": 0.042,
963 "mid_price": 0.041,
964 "bid_price": 0.040,
965 "ask_price": 0.042,
966 "last": 0.0415,
967 "mark_iv": 55.2,
968 "bid_iv": 54.0,
969 "ask_iv": 56.1,
970 "interest_rate": 0.0,
971 "open_interest": 123.5,
972 "volume": 10.0,
973 "volume_usd": 950000.0,
974 "volume_notional": 10.0,
975 "high": 0.05,
976 "low": 0.03,
977 "price_change": 0.01,
978 "estimated_delivery_price": 94900.0,
979 "base_currency": "BTC",
980 "quote_currency": "USD",
981 "creation_timestamp": 1710000000000
982 }"#;
983
984 let summary: DeribitBookSummaryRaw = serde_json::from_str(json).unwrap();
985 assert_eq!(summary.instrument_name, "BTC-28MAR25-90000-C");
986 assert_eq!(summary.underlying_price, Some(dec!(95000.5)));
987 assert_eq!(summary.underlying_index.as_deref(), Some("SYN.BTC-28MAR25"));
988 assert_eq!(summary.mark_price, Some(dec!(0.042)));
989 assert_eq!(summary.last_price, Some(dec!(0.0415)));
990 assert_eq!(summary.mark_iv, Some(dec!(55.2)));
991 assert_eq!(summary.bid_price, Some(dec!(0.040)));
992 assert_eq!(summary.ask_price, Some(dec!(0.042)));
993 assert_eq!(summary.open_interest, Some(dec!(123.5)));
994 assert_eq!(summary.volume_usd, Some(dec!(950000.0)));
995 assert_eq!(summary.price_change, Some(dec!(0.01)));
996 assert_eq!(summary.creation_timestamp, 1_710_000_000_000);
997 }
998
999 #[rstest]
1000 fn test_book_summary_raw_deserializes_minimal_payload() {
1001 let json = r#"{
1002 "instrument_name": "BTC-28MAR25-90000-C",
1003 "creation_timestamp": 1710000000000
1004 }"#;
1005
1006 let summary: DeribitBookSummaryRaw = serde_json::from_str(json).unwrap();
1007 assert_eq!(summary.instrument_name, "BTC-28MAR25-90000-C");
1008 assert!(summary.mark_iv.is_none());
1009 assert!(summary.open_interest.is_none());
1010 assert_eq!(summary.creation_timestamp, 1_710_000_000_000);
1011 }
1012}
1013
1014#[derive(Clone, Debug, Serialize, Deserialize)]
1019pub struct DeribitTicker {
1020 pub instrument_name: String,
1022 #[serde(
1024 default,
1025 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1026 )]
1027 pub underlying_price: Option<Decimal>,
1028 #[serde(default)]
1030 pub underlying_index: Option<String>,
1031}
1032
1033#[derive(Debug, Clone, Serialize, Deserialize)]
1037pub struct DeribitPosition {
1038 pub instrument_name: Ustr,
1040 pub direction: String,
1042 #[serde(
1044 serialize_with = "decimal::serialize",
1045 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1046 )]
1047 pub size: Decimal,
1048 #[serde(
1050 serialize_with = "decimal::serialize",
1051 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1052 )]
1053 pub average_price: Decimal,
1054 #[serde(
1056 serialize_with = "decimal::serialize",
1057 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1058 )]
1059 pub mark_price: Decimal,
1060 #[serde(
1062 default,
1063 serialize_with = "decimal::serialize_optional",
1064 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1065 )]
1066 pub index_price: Option<Decimal>,
1067 #[serde(
1069 serialize_with = "decimal::serialize",
1070 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1071 )]
1072 pub maintenance_margin: Decimal,
1073 #[serde(
1075 serialize_with = "decimal::serialize",
1076 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1077 )]
1078 pub initial_margin: Decimal,
1079 #[serde(
1081 default,
1082 serialize_with = "decimal::serialize_optional",
1083 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1084 )]
1085 pub leverage: Option<Decimal>,
1086 #[serde(
1088 serialize_with = "decimal::serialize",
1089 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1090 )]
1091 pub floating_profit_loss: Decimal,
1092 #[serde(
1094 serialize_with = "decimal::serialize",
1095 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1096 )]
1097 pub realized_profit_loss: Decimal,
1098 #[serde(
1100 serialize_with = "decimal::serialize",
1101 deserialize_with = "deserialize_decimal_token_or_zero_borrowed"
1102 )]
1103 pub total_profit_loss: Decimal,
1104 pub kind: DeribitProductType,
1106 #[serde(
1108 default,
1109 serialize_with = "decimal::serialize_optional",
1110 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1111 )]
1112 pub size_currency: Option<Decimal>,
1113 #[serde(
1115 default,
1116 serialize_with = "decimal::serialize_optional",
1117 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1118 )]
1119 pub estimated_liquidation_price: Option<Decimal>,
1120 #[serde(
1122 default,
1123 serialize_with = "decimal::serialize_optional",
1124 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1125 )]
1126 pub delta: Option<Decimal>,
1127 #[serde(
1129 default,
1130 serialize_with = "decimal::serialize_optional",
1131 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1132 )]
1133 pub gamma: Option<Decimal>,
1134 #[serde(
1136 default,
1137 serialize_with = "decimal::serialize_optional",
1138 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1139 )]
1140 pub vega: Option<Decimal>,
1141 #[serde(
1143 default,
1144 serialize_with = "decimal::serialize_optional",
1145 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1146 )]
1147 pub theta: Option<Decimal>,
1148 #[serde(
1150 default,
1151 serialize_with = "decimal::serialize_optional",
1152 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1153 )]
1154 pub settlement_price: Option<Decimal>,
1155 #[serde(
1157 default,
1158 serialize_with = "decimal::serialize_optional",
1159 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1160 )]
1161 pub open_orders_margin: Option<Decimal>,
1162 #[serde(
1164 default,
1165 serialize_with = "decimal::serialize_optional",
1166 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1167 )]
1168 pub average_price_usd: Option<Decimal>,
1169 #[serde(
1171 default,
1172 serialize_with = "decimal::serialize_optional",
1173 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1174 )]
1175 pub realized_profit_loss_session: Option<Decimal>,
1176 #[serde(
1178 default,
1179 serialize_with = "decimal::serialize_optional",
1180 deserialize_with = "deserialize_optional_decimal_token_borrowed"
1181 )]
1182 pub floating_profit_loss_usd: Option<Decimal>,
1183}
1184
1185#[derive(Clone, Debug, Serialize, Deserialize)]
1190pub struct DeribitUserTradesResponse {
1191 pub has_more: bool,
1193 pub trades: Vec<crate::websocket::messages::DeribitUserTradeMsg>,
1195}
1196
1197#[cfg(test)]
1198mod tests {
1199 use rstest::rstest;
1200 use rust_decimal_macros::dec;
1201
1202 use super::*;
1203
1204 #[rstest]
1206 fn test_decimal_fields_reject_value_input() {
1207 let value = serde_json::json!({ "amount": -1, "instrument_name": "BTC-PERPETUAL" });
1208
1209 let error = serde_json::from_value::<DeribitComboLeg>(value).unwrap_err();
1210
1211 assert_eq!(
1212 error.to_string(),
1213 r#"invalid type: string "-1", expected raw value"#
1214 );
1215 }
1216
1217 #[rstest]
1218 #[case::integer(r#""leverage": 50,"#, Some(dec!(50)))]
1219 #[case::whole_float(r#""leverage": 50.0,"#, Some(dec!(50)))]
1220 #[case::fractional(r#""leverage": 35.639422,"#, Some(dec!(35.639422)))]
1221 #[case::null(r#""leverage": null,"#, None)]
1222 #[case::missing("", None)]
1223 fn test_deserialize_position_leverage(#[case] field: &str, #[case] expected: Option<Decimal>) {
1224 let json = format!(
1225 r#"{{
1226 "average_price": 7440.18,
1227 "direction": "buy",
1228 "floating_profit_loss": 0,
1229 "initial_margin": 0.000197283,
1230 "instrument_name": "BTC-PERPETUAL",
1231 "kind": "future",
1232 {field}
1233 "maintenance_margin": 0.000143783,
1234 "mark_price": 7476.65,
1235 "realized_profit_loss": -9e-9,
1236 "size": 50,
1237 "total_profit_loss": 0.000032781
1238 }}"#
1239 );
1240
1241 let position: DeribitPosition = serde_json::from_str(&json).unwrap();
1242
1243 assert_eq!(position.leverage, expected);
1244 }
1245
1246 #[rstest]
1247 fn test_deserialize_public_trade_with_empty_mark_and_index_price() {
1248 let json = r#"{
1249 "amount": 1.0,
1250 "direction": "sell",
1251 "index_price": "",
1252 "instrument_name": "ETH-PERPETUAL",
1253 "mark_price": "",
1254 "price": 2968.3,
1255 "tick_direction": 0,
1256 "timestamp": 1766332040636,
1257 "trade_id": "ETH-123",
1258 "trade_seq": 1
1259 }"#;
1260
1261 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
1262 assert_eq!(trade.index_price, None);
1263 assert_eq!(trade.mark_price, None);
1264 assert_eq!(trade.price, dec!(2968.3));
1265 }
1266
1267 #[rstest]
1268 fn test_deserialize_public_trade_with_missing_mark_and_index_price() {
1269 let json = r#"{
1270 "amount": 1.0,
1271 "direction": "sell",
1272 "instrument_name": "ETH-PERPETUAL",
1273 "price": 2968.3,
1274 "tick_direction": 0,
1275 "timestamp": 1766332040636,
1276 "trade_id": "ETH-123",
1277 "trade_seq": 1
1278 }"#;
1279
1280 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
1281 assert_eq!(trade.index_price, None);
1282 assert_eq!(trade.mark_price, None);
1283 }
1284
1285 #[rstest]
1286 fn test_deserialize_public_trade_with_present_mark_and_index_price() {
1287 let json = r#"{
1288 "amount": 1.0,
1289 "direction": "sell",
1290 "index_price": 2967.73,
1291 "instrument_name": "ETH-PERPETUAL",
1292 "mark_price": 2968.01,
1293 "price": 2968.3,
1294 "tick_direction": 0,
1295 "timestamp": 1766332040636,
1296 "trade_id": "ETH-123",
1297 "trade_seq": 1
1298 }"#;
1299
1300 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
1301 assert_eq!(trade.index_price, Some(dec!(2967.73)));
1302 assert_eq!(trade.mark_price, Some(dec!(2968.01)));
1303 }
1304
1305 #[rstest]
1306 fn test_deserialize_expirations_currency_keyed_response() {
1307 let json = r#"{
1308 "btc": {
1309 "option": ["20MAY26", "26JUN26"],
1310 "future": ["20MAY26", "PERPETUAL"]
1311 }
1312 }"#;
1313
1314 let response: DeribitExpirationsResponse = serde_json::from_str(json).unwrap();
1315 let expirations = response.expirations_for_currency("BTC").unwrap();
1316 assert_eq!(expirations.option, vec!["20MAY26", "26JUN26"]);
1317 assert_eq!(expirations.future, vec!["20MAY26", "PERPETUAL"]);
1318 }
1319
1320 #[rstest]
1321 fn test_deserialize_expirations_direct_response() {
1322 let json = r#"{
1323 "option": ["20MAY26", "26JUN26"],
1324 "future": ["20MAY26", "PERPETUAL"]
1325 }"#;
1326
1327 let response: DeribitExpirationsResponse = serde_json::from_str(json).unwrap();
1328 let expirations = response.expirations_for_currency("any").unwrap();
1329 assert_eq!(expirations.option, vec!["20MAY26", "26JUN26"]);
1330 assert_eq!(expirations.future, vec!["20MAY26", "PERPETUAL"]);
1331 }
1332}