1use ahash::AHashMap;
19use nautilus_core::serialization::{deserialize_decimal, deserialize_optional_decimal};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24pub use crate::common::{
25 enums::{DeribitCurrency, DeribitOptionType, DeribitProductType},
26 models::DeribitTradeLeg,
27 rpc::{DeribitJsonRpcError, DeribitJsonRpcRequest, DeribitJsonRpcResponse},
28};
29
30#[derive(Debug, Serialize, Deserialize)]
32#[serde(untagged)]
33pub enum DeribitResponsePayload<T> {
34 Success { result: T },
36 Error { error: DeribitJsonRpcError },
38}
39
40#[derive(Clone, Debug, Serialize, Deserialize)]
42pub struct DeribitInstrument {
43 pub base_currency: Ustr,
45 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
47 pub block_trade_commission: Option<Decimal>,
48 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
50 pub block_trade_min_trade_amount: Option<Decimal>,
51 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
53 pub block_trade_tick_size: Option<Decimal>,
54 #[serde(deserialize_with = "deserialize_decimal")]
56 pub contract_size: Decimal,
57 pub counter_currency: Option<Ustr>,
59 pub creation_timestamp: i64,
61 pub expiration_timestamp: Option<i64>,
63 pub future_type: Option<String>,
65 pub instrument_id: i64,
67 pub instrument_name: Ustr,
69 pub instrument_type: Option<String>,
71 pub is_active: bool,
73 pub kind: DeribitProductType,
75 #[serde(deserialize_with = "deserialize_decimal")]
77 pub maker_commission: Decimal,
78 pub max_leverage: Option<i64>,
80 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
82 pub max_liquidation_commission: Option<Decimal>,
83 #[serde(deserialize_with = "deserialize_decimal")]
85 pub min_trade_amount: Decimal,
86 pub option_type: Option<DeribitOptionType>,
88 pub price_index: Option<String>,
90 pub quote_currency: Ustr,
92 pub settlement_currency: Option<Ustr>,
94 pub settlement_period: Option<String>,
96 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
98 pub strike: Option<Decimal>,
99 #[serde(deserialize_with = "deserialize_decimal")]
101 pub taker_commission: Decimal,
102 #[serde(deserialize_with = "deserialize_decimal")]
104 pub tick_size: Decimal,
105 pub tick_size_steps: Option<Vec<DeribitTickSizeStep>>,
107}
108
109#[derive(Clone, Debug, Serialize, Deserialize)]
111pub struct DeribitTickSizeStep {
112 #[serde(deserialize_with = "deserialize_decimal")]
114 pub above_price: Decimal,
115 #[serde(deserialize_with = "deserialize_decimal")]
117 pub tick_size: Decimal,
118}
119
120#[derive(Clone, Debug, Serialize, Deserialize)]
122pub struct DeribitCombo {
123 pub id: Ustr,
125 pub state: String,
127 pub legs: Vec<DeribitComboLeg>,
129 pub creation_timestamp: i64,
131 pub instrument_id: i64,
133 pub state_timestamp: i64,
135}
136
137#[derive(Clone, Debug, Serialize, Deserialize)]
139pub struct DeribitComboLeg {
140 #[serde(deserialize_with = "deserialize_decimal")]
142 pub amount: Decimal,
143 pub instrument_name: Ustr,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct DeribitAccountSummariesResponse {
153 pub summaries: Vec<DeribitAccountSummary>,
155 #[serde(default)]
157 pub id: Option<i64>,
158 #[serde(default)]
160 pub email: Option<String>,
161 #[serde(default)]
163 pub system_name: Option<String>,
164 #[serde(default)]
166 pub username: Option<String>,
167 #[serde(rename = "type", default)]
169 pub account_type: Option<String>,
170 #[serde(default)]
172 pub creation_timestamp: Option<i64>,
173 #[serde(default)]
175 pub referrer_id: Option<String>,
176 #[serde(default)]
178 pub login_enabled: Option<bool>,
179 #[serde(default)]
181 pub security_keys_enabled: Option<bool>,
182 #[serde(default)]
184 pub mmp_enabled: Option<bool>,
185 #[serde(default)]
187 pub interuser_transfers_enabled: Option<bool>,
188 #[serde(default)]
190 pub self_trading_reject_mode: Option<String>,
191 #[serde(default)]
193 pub self_trading_extended_to_subaccounts: Option<bool>,
194 #[serde(default)]
196 pub block_rfq_self_match_prevention: Option<bool>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct DeribitAccountSummary {
204 pub currency: Ustr,
206 #[serde(deserialize_with = "deserialize_decimal")]
208 pub equity: Decimal,
209 #[serde(deserialize_with = "deserialize_decimal")]
211 pub balance: Decimal,
212 #[serde(deserialize_with = "deserialize_decimal")]
214 pub available_funds: Decimal,
215 #[serde(deserialize_with = "deserialize_decimal")]
217 pub margin_balance: Decimal,
218 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
220 pub initial_margin: Option<Decimal>,
221 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
223 pub maintenance_margin: Option<Decimal>,
224 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
226 pub total_pl: Option<Decimal>,
227 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
229 pub session_upl: Option<Decimal>,
230 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
232 pub session_rpl: Option<Decimal>,
233 #[serde(default)]
235 pub portfolio_margining_enabled: Option<bool>,
236 #[serde(default)]
238 pub margin_model: Option<String>,
239 #[serde(default)]
241 pub cross_collateral_enabled: Option<bool>,
242 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
244 pub available_withdrawal_funds: Option<Decimal>,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct DeribitAccountSummaryExtended {
254 pub currency: Ustr,
256 #[serde(deserialize_with = "deserialize_decimal")]
258 pub equity: Decimal,
259 #[serde(deserialize_with = "deserialize_decimal")]
261 pub balance: Decimal,
262 #[serde(deserialize_with = "deserialize_decimal")]
264 pub available_funds: Decimal,
265 #[serde(deserialize_with = "deserialize_decimal")]
267 pub margin_balance: Decimal,
268 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
270 pub initial_margin: Option<Decimal>,
271 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
273 pub maintenance_margin: Option<Decimal>,
274 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
276 pub total_pl: Option<Decimal>,
277 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
279 pub session_upl: Option<Decimal>,
280 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
282 pub session_rpl: Option<Decimal>,
283 #[serde(default)]
285 pub portfolio_margining_enabled: Option<bool>,
286 #[serde(default)]
289 pub id: Option<i64>,
290 #[serde(default)]
292 pub email: Option<String>,
293 #[serde(default)]
295 pub username: Option<String>,
296 #[serde(default)]
298 pub system_name: Option<String>,
299 #[serde(rename = "type", default)]
301 pub account_type: Option<String>,
302 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
304 pub futures_session_upl: Option<Decimal>,
305 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
307 pub futures_session_rpl: Option<Decimal>,
308 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
310 pub options_session_upl: Option<Decimal>,
311 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
313 pub options_session_rpl: Option<Decimal>,
314 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
316 pub futures_pl: Option<Decimal>,
317 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
319 pub options_pl: Option<Decimal>,
320 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
322 pub options_delta: Option<Decimal>,
323 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
325 pub options_gamma: Option<Decimal>,
326 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
328 pub options_vega: Option<Decimal>,
329 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
331 pub options_theta: Option<Decimal>,
332 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
334 pub options_value: Option<Decimal>,
335 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
337 pub delta_total: Option<Decimal>,
338 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
340 pub projected_delta_total: Option<Decimal>,
341 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
343 pub projected_initial_margin: Option<Decimal>,
344 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
346 pub projected_maintenance_margin: Option<Decimal>,
347 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
349 pub estimated_liquidation_ratio: Option<Decimal>,
350 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
352 pub available_withdrawal_funds: Option<Decimal>,
353 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
355 pub spot_reserve: Option<Decimal>,
356 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
358 pub fee_balance: Option<Decimal>,
359 #[serde(default)]
361 pub margin_model: Option<String>,
362 #[serde(default)]
364 pub cross_collateral_enabled: Option<bool>,
365 #[serde(default)]
367 pub creation_timestamp: Option<i64>,
368 #[serde(default)]
370 pub login_enabled: Option<bool>,
371 #[serde(default)]
373 pub security_keys_enabled: Option<bool>,
374 #[serde(default)]
376 pub mmp_enabled: Option<bool>,
377 #[serde(default)]
379 pub interuser_transfers_enabled: Option<bool>,
380 #[serde(default)]
382 pub self_trading_reject_mode: Option<String>,
383 #[serde(default)]
385 pub self_trading_extended_to_subaccounts: Option<bool>,
386 #[serde(default)]
388 pub referrer_id: Option<String>,
389 #[serde(default)]
391 pub block_rfq_self_match_prevention: Option<bool>,
392}
393
394#[derive(Clone, Debug, Serialize, Deserialize)]
399pub struct DeribitPublicTrade {
400 #[serde(deserialize_with = "deserialize_decimal")]
403 pub amount: Decimal,
404 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
406 pub contracts: Option<Decimal>,
407 pub direction: String,
409 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
411 pub index_price: Option<Decimal>,
412 pub instrument_name: String,
414 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
416 pub iv: Option<Decimal>,
417 #[serde(default)]
419 pub liquidation: Option<String>,
420 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
422 pub mark_price: Option<Decimal>,
423 #[serde(deserialize_with = "deserialize_decimal")]
425 pub price: Decimal,
426 pub tick_direction: i32,
428 pub timestamp: i64,
430 pub trade_id: String,
432 pub trade_seq: i64,
434 #[serde(default)]
436 pub block_trade_id: Option<String>,
437 #[serde(default)]
439 pub block_trade_leg_count: Option<i32>,
440 #[serde(default)]
442 pub block_rfq_id: Option<i64>,
443 #[serde(default)]
445 pub combo_id: Option<String>,
446 #[serde(default)]
448 pub combo_trade_id: Option<String>,
449 #[serde(default)]
451 pub legs: Option<Vec<DeribitTradeLeg>>,
452}
453
454#[derive(Clone, Debug, Serialize, Deserialize)]
458pub struct DeribitTradesResponse {
459 pub has_more: bool,
461 pub trades: Vec<DeribitPublicTrade>,
463}
464
465#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
467pub struct DeribitExpirationsByKind {
468 #[serde(default)]
470 pub future: Vec<String>,
471 #[serde(default)]
473 pub option: Vec<String>,
474 #[serde(default)]
476 pub future_combo: Vec<String>,
477 #[serde(default)]
479 pub option_combo: Vec<String>,
480}
481
482#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
487#[serde(untagged)]
488pub enum DeribitExpirationsResponse {
489 ByCurrency(AHashMap<String, DeribitExpirationsByKind>),
491 Expirations(DeribitExpirationsByKind),
493}
494
495impl DeribitExpirationsResponse {
496 #[must_use]
498 pub fn expirations_for_currency(&self, currency: &str) -> Option<&DeribitExpirationsByKind> {
499 match self {
500 Self::ByCurrency(expirations) => {
501 let key = currency.to_ascii_lowercase();
502 expirations.get(&key)
503 }
504 Self::Expirations(expirations) => Some(expirations),
505 }
506 }
507}
508
509#[derive(Clone, Debug, Serialize, Deserialize)]
514pub struct DeribitTradingViewChartData {
515 pub close: Vec<f64>,
517 pub cost: Vec<f64>,
519 pub high: Vec<f64>,
521 pub low: Vec<f64>,
523 pub open: Vec<f64>,
525 pub status: String,
527 pub ticks: Vec<i64>,
529 pub volume: Vec<f64>,
531}
532
533#[derive(Clone, Debug, Serialize, Deserialize)]
537pub struct DeribitOrderBook {
538 pub timestamp: i64,
540 pub instrument_name: String,
542 pub bids: Vec<[f64; 2]>,
544 pub asks: Vec<[f64; 2]>,
546 pub state: String,
548 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
550 pub best_bid_price: Option<Decimal>,
551 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
553 pub best_ask_price: Option<Decimal>,
554 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
556 pub best_bid_amount: Option<Decimal>,
557 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
559 pub best_ask_amount: Option<Decimal>,
560 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
562 pub mark_price: Option<Decimal>,
563 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
565 pub last_price: Option<Decimal>,
566 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
568 pub index_price: Option<Decimal>,
569 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
571 pub open_interest: Option<Decimal>,
572 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
574 pub max_price: Option<Decimal>,
575 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
577 pub min_price: Option<Decimal>,
578 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
580 pub current_funding: Option<Decimal>,
581 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
583 pub funding_8h: Option<Decimal>,
584 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
586 pub settlement_price: Option<Decimal>,
587 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
589 pub delivery_price: Option<Decimal>,
590 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
592 pub bid_iv: Option<Decimal>,
593 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
595 pub ask_iv: Option<Decimal>,
596 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
598 pub mark_iv: Option<Decimal>,
599 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
601 pub underlying_price: Option<Decimal>,
602 #[serde(default)]
604 pub underlying_index: Option<serde_json::Value>,
605 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
607 pub interest_rate: Option<Decimal>,
608}
609
610#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
615pub struct DeribitBookSummaryRaw {
616 pub instrument_name: String,
618 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
620 pub underlying_price: Option<Decimal>,
621 #[serde(default)]
623 pub underlying_index: Option<String>,
624 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
626 pub mark_price: Option<Decimal>,
627 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
629 pub mid_price: Option<Decimal>,
630 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
632 pub bid_price: Option<Decimal>,
633 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
635 pub ask_price: Option<Decimal>,
636 #[serde(
638 default,
639 rename = "last",
640 deserialize_with = "deserialize_optional_decimal"
641 )]
642 pub last_price: Option<Decimal>,
643 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
645 pub mark_iv: Option<Decimal>,
646 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
648 pub bid_iv: Option<Decimal>,
649 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
651 pub ask_iv: Option<Decimal>,
652 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
654 pub interest_rate: Option<Decimal>,
655 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
657 pub open_interest: Option<Decimal>,
658 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
660 pub open_interest_value: Option<Decimal>,
661 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
663 pub volume: Option<Decimal>,
664 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
666 pub volume_usd: Option<Decimal>,
667 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
669 pub volume_notional: Option<Decimal>,
670 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
672 pub volume_btc: Option<Decimal>,
673 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
675 pub high: Option<Decimal>,
676 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
678 pub low: Option<Decimal>,
679 #[serde(
681 default,
682 rename = "price_change",
683 deserialize_with = "deserialize_optional_decimal"
684 )]
685 pub price_change: Option<Decimal>,
686 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
688 pub estimated_delivery_price: Option<Decimal>,
689 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
691 pub delivery_price: Option<Decimal>,
692 #[serde(default)]
694 pub base_currency: Option<String>,
695 #[serde(default)]
697 pub quote_currency: Option<String>,
698 #[serde(default)]
700 pub creation_timestamp: i64,
701}
702
703#[cfg(test)]
704mod book_summary_tests {
705 use rstest::rstest;
706 use rust_decimal_macros::dec;
707
708 use super::*;
709
710 #[rstest]
711 fn test_book_summary_raw_deserializes_full_option_payload() {
712 let json = r#"{
713 "instrument_name": "BTC-28MAR25-90000-C",
714 "underlying_price": 95000.5,
715 "underlying_index": "SYN.BTC-28MAR25",
716 "mark_price": 0.042,
717 "mid_price": 0.041,
718 "bid_price": 0.040,
719 "ask_price": 0.042,
720 "last": 0.0415,
721 "mark_iv": 55.2,
722 "bid_iv": 54.0,
723 "ask_iv": 56.1,
724 "interest_rate": 0.0,
725 "open_interest": 123.5,
726 "volume": 10.0,
727 "volume_usd": 950000.0,
728 "volume_notional": 10.0,
729 "high": 0.05,
730 "low": 0.03,
731 "price_change": 0.01,
732 "estimated_delivery_price": 94900.0,
733 "base_currency": "BTC",
734 "quote_currency": "USD",
735 "creation_timestamp": 1710000000000
736 }"#;
737
738 let summary: DeribitBookSummaryRaw = serde_json::from_str(json).unwrap();
739 assert_eq!(summary.instrument_name, "BTC-28MAR25-90000-C");
740 assert_eq!(summary.underlying_price, Some(dec!(95000.5)));
741 assert_eq!(summary.underlying_index.as_deref(), Some("SYN.BTC-28MAR25"));
742 assert_eq!(summary.mark_price, Some(dec!(0.042)));
743 assert_eq!(summary.last_price, Some(dec!(0.0415)));
744 assert_eq!(summary.mark_iv, Some(dec!(55.2)));
745 assert_eq!(summary.bid_price, Some(dec!(0.040)));
746 assert_eq!(summary.ask_price, Some(dec!(0.042)));
747 assert_eq!(summary.open_interest, Some(dec!(123.5)));
748 assert_eq!(summary.volume_usd, Some(dec!(950000.0)));
749 assert_eq!(summary.price_change, Some(dec!(0.01)));
750 assert_eq!(summary.creation_timestamp, 1_710_000_000_000);
751 }
752
753 #[rstest]
754 fn test_book_summary_raw_deserializes_minimal_payload() {
755 let json = r#"{
756 "instrument_name": "BTC-28MAR25-90000-C",
757 "creation_timestamp": 1710000000000
758 }"#;
759
760 let summary: DeribitBookSummaryRaw = serde_json::from_str(json).unwrap();
761 assert_eq!(summary.instrument_name, "BTC-28MAR25-90000-C");
762 assert!(summary.mark_iv.is_none());
763 assert!(summary.open_interest.is_none());
764 assert_eq!(summary.creation_timestamp, 1_710_000_000_000);
765 }
766}
767
768#[derive(Clone, Debug, Serialize, Deserialize)]
773pub struct DeribitTicker {
774 pub instrument_name: String,
776 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
778 pub underlying_price: Option<Decimal>,
779 #[serde(default)]
781 pub underlying_index: Option<String>,
782}
783
784#[derive(Debug, Clone, Serialize, Deserialize)]
788pub struct DeribitPosition {
789 pub instrument_name: Ustr,
791 pub direction: String,
793 #[serde(
795 serialize_with = "nautilus_core::serialization::serialize_decimal",
796 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
797 )]
798 pub size: Decimal,
799 #[serde(
801 serialize_with = "nautilus_core::serialization::serialize_decimal",
802 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
803 )]
804 pub average_price: Decimal,
805 #[serde(
807 serialize_with = "nautilus_core::serialization::serialize_decimal",
808 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
809 )]
810 pub mark_price: Decimal,
811 #[serde(
813 default,
814 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
815 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
816 )]
817 pub index_price: Option<Decimal>,
818 #[serde(
820 serialize_with = "nautilus_core::serialization::serialize_decimal",
821 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
822 )]
823 pub maintenance_margin: Decimal,
824 #[serde(
826 serialize_with = "nautilus_core::serialization::serialize_decimal",
827 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
828 )]
829 pub initial_margin: Decimal,
830 #[serde(default)]
832 pub leverage: Option<i64>,
833 #[serde(
835 serialize_with = "nautilus_core::serialization::serialize_decimal",
836 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
837 )]
838 pub floating_profit_loss: Decimal,
839 #[serde(
841 serialize_with = "nautilus_core::serialization::serialize_decimal",
842 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
843 )]
844 pub realized_profit_loss: Decimal,
845 #[serde(
847 serialize_with = "nautilus_core::serialization::serialize_decimal",
848 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
849 )]
850 pub total_profit_loss: Decimal,
851 pub kind: DeribitProductType,
853 #[serde(
855 default,
856 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
857 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
858 )]
859 pub size_currency: Option<Decimal>,
860 #[serde(
862 default,
863 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
864 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
865 )]
866 pub estimated_liquidation_price: Option<Decimal>,
867 #[serde(
869 default,
870 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
871 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
872 )]
873 pub delta: Option<Decimal>,
874 #[serde(
876 default,
877 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
878 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
879 )]
880 pub gamma: Option<Decimal>,
881 #[serde(
883 default,
884 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
885 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
886 )]
887 pub vega: Option<Decimal>,
888 #[serde(
890 default,
891 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
892 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
893 )]
894 pub theta: Option<Decimal>,
895 #[serde(
897 default,
898 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
899 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
900 )]
901 pub settlement_price: Option<Decimal>,
902 #[serde(
904 default,
905 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
906 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
907 )]
908 pub open_orders_margin: Option<Decimal>,
909 #[serde(
911 default,
912 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
913 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
914 )]
915 pub average_price_usd: Option<Decimal>,
916 #[serde(
918 default,
919 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
920 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
921 )]
922 pub realized_profit_loss_session: Option<Decimal>,
923 #[serde(
925 default,
926 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
927 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
928 )]
929 pub floating_profit_loss_usd: Option<Decimal>,
930}
931
932#[derive(Clone, Debug, Serialize, Deserialize)]
937pub struct DeribitUserTradesResponse {
938 pub has_more: bool,
940 pub trades: Vec<crate::websocket::messages::DeribitUserTradeMsg>,
942}
943
944#[cfg(test)]
945mod tests {
946 use rstest::rstest;
947 use rust_decimal_macros::dec;
948
949 use super::*;
950
951 #[rstest]
952 fn test_deserialize_public_trade_with_empty_mark_and_index_price() {
953 let json = r#"{
954 "amount": 1.0,
955 "direction": "sell",
956 "index_price": "",
957 "instrument_name": "ETH-PERPETUAL",
958 "mark_price": "",
959 "price": 2968.3,
960 "tick_direction": 0,
961 "timestamp": 1766332040636,
962 "trade_id": "ETH-123",
963 "trade_seq": 1
964 }"#;
965
966 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
967 assert_eq!(trade.index_price, None);
968 assert_eq!(trade.mark_price, None);
969 assert_eq!(trade.price, dec!(2968.3));
970 }
971
972 #[rstest]
973 fn test_deserialize_public_trade_with_missing_mark_and_index_price() {
974 let json = r#"{
975 "amount": 1.0,
976 "direction": "sell",
977 "instrument_name": "ETH-PERPETUAL",
978 "price": 2968.3,
979 "tick_direction": 0,
980 "timestamp": 1766332040636,
981 "trade_id": "ETH-123",
982 "trade_seq": 1
983 }"#;
984
985 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
986 assert_eq!(trade.index_price, None);
987 assert_eq!(trade.mark_price, None);
988 }
989
990 #[rstest]
991 fn test_deserialize_public_trade_with_present_mark_and_index_price() {
992 let json = r#"{
993 "amount": 1.0,
994 "direction": "sell",
995 "index_price": 2967.73,
996 "instrument_name": "ETH-PERPETUAL",
997 "mark_price": 2968.01,
998 "price": 2968.3,
999 "tick_direction": 0,
1000 "timestamp": 1766332040636,
1001 "trade_id": "ETH-123",
1002 "trade_seq": 1
1003 }"#;
1004
1005 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
1006 assert_eq!(trade.index_price, Some(dec!(2967.73)));
1007 assert_eq!(trade.mark_price, Some(dec!(2968.01)));
1008 }
1009
1010 #[rstest]
1011 fn test_deserialize_expirations_currency_keyed_response() {
1012 let json = r#"{
1013 "btc": {
1014 "option": ["20MAY26", "26JUN26"],
1015 "future": ["20MAY26", "PERPETUAL"]
1016 }
1017 }"#;
1018
1019 let response: DeribitExpirationsResponse = serde_json::from_str(json).unwrap();
1020 let expirations = response.expirations_for_currency("BTC").unwrap();
1021 assert_eq!(expirations.option, vec!["20MAY26", "26JUN26"]);
1022 assert_eq!(expirations.future, vec!["20MAY26", "PERPETUAL"]);
1023 }
1024
1025 #[rstest]
1026 fn test_deserialize_expirations_direct_response() {
1027 let json = r#"{
1028 "option": ["20MAY26", "26JUN26"],
1029 "future": ["20MAY26", "PERPETUAL"]
1030 }"#;
1031
1032 let response: DeribitExpirationsResponse = serde_json::from_str(json).unwrap();
1033 let expirations = response.expirations_for_currency("any").unwrap();
1034 assert_eq!(expirations.option, vec!["20MAY26", "26JUN26"]);
1035 assert_eq!(expirations.future, vec!["20MAY26", "PERPETUAL"]);
1036 }
1037}