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, Serialize, Deserialize)]
615pub struct DeribitBookSummary {
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 pub creation_timestamp: i64,
629}
630
631#[derive(Clone, Debug, Serialize, Deserialize)]
636pub struct DeribitTicker {
637 pub instrument_name: String,
639 #[serde(default, deserialize_with = "deserialize_optional_decimal")]
641 pub underlying_price: Option<Decimal>,
642 #[serde(default)]
644 pub underlying_index: Option<String>,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
651pub struct DeribitPosition {
652 pub instrument_name: Ustr,
654 pub direction: String,
656 #[serde(
658 serialize_with = "nautilus_core::serialization::serialize_decimal",
659 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
660 )]
661 pub size: Decimal,
662 #[serde(
664 serialize_with = "nautilus_core::serialization::serialize_decimal",
665 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
666 )]
667 pub average_price: Decimal,
668 #[serde(
670 serialize_with = "nautilus_core::serialization::serialize_decimal",
671 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
672 )]
673 pub mark_price: Decimal,
674 #[serde(
676 default,
677 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
678 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
679 )]
680 pub index_price: Option<Decimal>,
681 #[serde(
683 serialize_with = "nautilus_core::serialization::serialize_decimal",
684 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
685 )]
686 pub maintenance_margin: Decimal,
687 #[serde(
689 serialize_with = "nautilus_core::serialization::serialize_decimal",
690 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
691 )]
692 pub initial_margin: Decimal,
693 #[serde(default)]
695 pub leverage: Option<i64>,
696 #[serde(
698 serialize_with = "nautilus_core::serialization::serialize_decimal",
699 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
700 )]
701 pub floating_profit_loss: Decimal,
702 #[serde(
704 serialize_with = "nautilus_core::serialization::serialize_decimal",
705 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
706 )]
707 pub realized_profit_loss: Decimal,
708 #[serde(
710 serialize_with = "nautilus_core::serialization::serialize_decimal",
711 deserialize_with = "nautilus_core::serialization::deserialize_decimal"
712 )]
713 pub total_profit_loss: Decimal,
714 pub kind: DeribitProductType,
716 #[serde(
718 default,
719 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
720 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
721 )]
722 pub size_currency: Option<Decimal>,
723 #[serde(
725 default,
726 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
727 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
728 )]
729 pub estimated_liquidation_price: Option<Decimal>,
730 #[serde(
732 default,
733 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
734 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
735 )]
736 pub delta: Option<Decimal>,
737 #[serde(
739 default,
740 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
741 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
742 )]
743 pub gamma: Option<Decimal>,
744 #[serde(
746 default,
747 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
748 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
749 )]
750 pub vega: Option<Decimal>,
751 #[serde(
753 default,
754 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
755 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
756 )]
757 pub theta: Option<Decimal>,
758 #[serde(
760 default,
761 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
762 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
763 )]
764 pub settlement_price: Option<Decimal>,
765 #[serde(
767 default,
768 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
769 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
770 )]
771 pub open_orders_margin: Option<Decimal>,
772 #[serde(
774 default,
775 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
776 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
777 )]
778 pub average_price_usd: Option<Decimal>,
779 #[serde(
781 default,
782 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
783 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
784 )]
785 pub realized_profit_loss_session: Option<Decimal>,
786 #[serde(
788 default,
789 serialize_with = "nautilus_core::serialization::serialize_optional_decimal",
790 deserialize_with = "nautilus_core::serialization::deserialize_optional_decimal"
791 )]
792 pub floating_profit_loss_usd: Option<Decimal>,
793}
794
795#[derive(Clone, Debug, Serialize, Deserialize)]
800pub struct DeribitUserTradesResponse {
801 pub has_more: bool,
803 pub trades: Vec<crate::websocket::messages::DeribitUserTradeMsg>,
805}
806
807#[cfg(test)]
808mod tests {
809 use rstest::rstest;
810 use rust_decimal_macros::dec;
811
812 use super::*;
813
814 #[rstest]
815 fn test_deserialize_public_trade_with_empty_mark_and_index_price() {
816 let json = r#"{
817 "amount": 1.0,
818 "direction": "sell",
819 "index_price": "",
820 "instrument_name": "ETH-PERPETUAL",
821 "mark_price": "",
822 "price": 2968.3,
823 "tick_direction": 0,
824 "timestamp": 1766332040636,
825 "trade_id": "ETH-123",
826 "trade_seq": 1
827 }"#;
828
829 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
830 assert_eq!(trade.index_price, None);
831 assert_eq!(trade.mark_price, None);
832 assert_eq!(trade.price, dec!(2968.3));
833 }
834
835 #[rstest]
836 fn test_deserialize_public_trade_with_missing_mark_and_index_price() {
837 let json = r#"{
838 "amount": 1.0,
839 "direction": "sell",
840 "instrument_name": "ETH-PERPETUAL",
841 "price": 2968.3,
842 "tick_direction": 0,
843 "timestamp": 1766332040636,
844 "trade_id": "ETH-123",
845 "trade_seq": 1
846 }"#;
847
848 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
849 assert_eq!(trade.index_price, None);
850 assert_eq!(trade.mark_price, None);
851 }
852
853 #[rstest]
854 fn test_deserialize_public_trade_with_present_mark_and_index_price() {
855 let json = r#"{
856 "amount": 1.0,
857 "direction": "sell",
858 "index_price": 2967.73,
859 "instrument_name": "ETH-PERPETUAL",
860 "mark_price": 2968.01,
861 "price": 2968.3,
862 "tick_direction": 0,
863 "timestamp": 1766332040636,
864 "trade_id": "ETH-123",
865 "trade_seq": 1
866 }"#;
867
868 let trade: DeribitPublicTrade = serde_json::from_str(json).unwrap();
869 assert_eq!(trade.index_price, Some(dec!(2967.73)));
870 assert_eq!(trade.mark_price, Some(dec!(2968.01)));
871 }
872
873 #[rstest]
874 fn test_deserialize_expirations_currency_keyed_response() {
875 let json = r#"{
876 "btc": {
877 "option": ["20MAY26", "26JUN26"],
878 "future": ["20MAY26", "PERPETUAL"]
879 }
880 }"#;
881
882 let response: DeribitExpirationsResponse = serde_json::from_str(json).unwrap();
883 let expirations = response.expirations_for_currency("BTC").unwrap();
884 assert_eq!(expirations.option, vec!["20MAY26", "26JUN26"]);
885 assert_eq!(expirations.future, vec!["20MAY26", "PERPETUAL"]);
886 }
887
888 #[rstest]
889 fn test_deserialize_expirations_direct_response() {
890 let json = r#"{
891 "option": ["20MAY26", "26JUN26"],
892 "future": ["20MAY26", "PERPETUAL"]
893 }"#;
894
895 let response: DeribitExpirationsResponse = serde_json::from_str(json).unwrap();
896 let expirations = response.expirations_for_currency("any").unwrap();
897 assert_eq!(expirations.option, vec!["20MAY26", "26JUN26"]);
898 assert_eq!(expirations.future, vec!["20MAY26", "PERPETUAL"]);
899 }
900}