deribit_base/model/
instrument.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 21/7/25
5******************************************************************************/
6
7use serde::{Deserialize, Serialize};
8
9use crate::{impl_json_debug_pretty, impl_json_display};
10/// Instrument kind enumeration
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13pub enum InstrumentKind {
14    /// Future contract
15    Future,
16    /// Option contract
17    Option,
18    /// Spot trading
19    Spot,
20    /// Future combo
21    #[serde(rename = "future_combo")]
22    FutureCombo,
23    /// Option combo
24    #[serde(rename = "option_combo")]
25    OptionCombo,
26}
27
28/// Option type enumeration
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "lowercase")]
31pub enum OptionType {
32    /// Call option
33    Call,
34    /// Put option
35    Put,
36}
37
38/// Instrument type enumeration
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40#[serde(rename_all = "lowercase")]
41pub enum InstrumentType {
42    /// Linear instrument
43    Linear,
44    /// Reversed instrument
45    Reversed,
46}
47
48/// Instrument information
49#[derive(Clone, Serialize, Deserialize)]
50pub struct Instrument {
51    /// Instrument name (e.g., "BTC-PERPETUAL", "ETH-25JUL25-3000-C")
52    pub instrument_name: String,
53    /// Price index used for mark price calculation
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub price_index: Option<String>,
56    /// Instrument kind
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub kind: Option<InstrumentKind>,
59    /// Base currency
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub currency: Option<String>,
62    /// Whether the instrument is active for trading
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub is_active: Option<bool>,
65    /// Expiration timestamp (None for perpetuals)
66    pub expiration_timestamp: Option<i64>,
67    /// Strike price (for options)
68    pub strike: Option<f64>,
69    /// Option type (call/put, for options only)
70    pub option_type: Option<OptionType>,
71    /// Minimum price movement
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub tick_size: Option<f64>,
74    /// Minimum trade amount
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub min_trade_amount: Option<f64>,
77    /// Contract size
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub contract_size: Option<f64>,
80    /// Settlement period
81    pub settlement_period: Option<String>,
82    /// Instrument type (linear/reversed)
83    pub instrument_type: Option<InstrumentType>,
84    /// Quote currency
85    pub quote_currency: Option<String>,
86    /// Settlement currency
87    pub settlement_currency: Option<String>,
88    /// Creation timestamp
89    pub creation_timestamp: Option<i64>,
90    /// Maximum leverage
91    pub max_leverage: Option<f64>,
92    /// Maker commission rate
93    pub maker_commission: Option<f64>,
94    /// Taker commission rate
95    pub taker_commission: Option<f64>,
96    /// Unique instrument identifier
97    pub instrument_id: Option<u32>,
98    /// Base currency for the instrument
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub base_currency: Option<String>,
101    /// Counter currency for the instrument
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub counter_currency: Option<String>,
104}
105
106impl Instrument {
107    /// Check if the instrument is a perpetual contract
108    pub fn is_perpetual(&self) -> bool {
109        self.expiration_timestamp.is_none()
110            && self
111                .kind
112                .as_ref()
113                .is_some_and(|k| matches!(k, InstrumentKind::Future))
114    }
115
116    /// Check if the instrument is an option
117    pub fn is_option(&self) -> bool {
118        self.kind
119            .as_ref()
120            .is_some_and(|k| matches!(k, InstrumentKind::Option | InstrumentKind::OptionCombo))
121    }
122
123    /// Check if the instrument is a future
124    pub fn is_future(&self) -> bool {
125        self.kind
126            .as_ref()
127            .is_some_and(|k| matches!(k, InstrumentKind::Future | InstrumentKind::FutureCombo))
128    }
129
130    /// Check if the instrument is a spot
131    pub fn is_spot(&self) -> bool {
132        self.kind
133            .as_ref()
134            .is_some_and(|k| matches!(k, InstrumentKind::Spot))
135    }
136}
137
138/// Index data
139#[derive(Clone, Serialize, Deserialize)]
140pub struct IndexData {
141    /// BTC component (optional)
142    pub btc: Option<f64>,
143    /// ETH component (optional)
144    pub eth: Option<f64>,
145    /// USDC component (optional)
146    pub usdc: Option<f64>,
147    /// USDT component (optional)
148    pub usdt: Option<f64>,
149    /// EURR component (optional)
150    pub eurr: Option<f64>,
151    /// EDP (Estimated Delivery Price)
152    pub edp: f64,
153}
154
155/// Index price data
156#[derive(Clone, Serialize, Deserialize)]
157pub struct IndexPriceData {
158    /// Current index price
159    pub index_price: f64,
160    /// Estimated delivery price
161    pub estimated_delivery_price: f64,
162}
163
164// Debug implementations using pretty JSON formatting
165impl_json_debug_pretty!(Instrument, IndexData, IndexPriceData);
166
167// Display implementations using compact JSON formatting
168impl_json_display!(Instrument, IndexData, IndexPriceData);