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    /// Instrument kind
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub kind: Option<InstrumentKind>,
56    /// Base currency
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub currency: Option<String>,
59    /// Whether the instrument is active for trading
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub is_active: Option<bool>,
62    /// Expiration timestamp (None for perpetuals)
63    pub expiration_timestamp: Option<i64>,
64    /// Strike price (for options)
65    pub strike: Option<f64>,
66    /// Option type (call/put, for options only)
67    pub option_type: Option<OptionType>,
68    /// Minimum price movement
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub tick_size: Option<f64>,
71    /// Minimum trade amount
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub min_trade_amount: Option<f64>,
74    /// Contract size
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub contract_size: Option<f64>,
77    /// Settlement period
78    pub settlement_period: Option<String>,
79    /// Instrument type (linear/reversed)
80    pub instrument_type: Option<InstrumentType>,
81    /// Quote currency
82    pub quote_currency: Option<String>,
83    /// Settlement currency
84    pub settlement_currency: Option<String>,
85    /// Creation timestamp
86    pub creation_timestamp: Option<i64>,
87    /// Maximum leverage
88    pub max_leverage: Option<f64>,
89    /// Maker commission rate
90    pub maker_commission: Option<f64>,
91    /// Taker commission rate
92    pub taker_commission: Option<f64>,
93    /// Unique instrument identifier
94    pub instrument_id: Option<u32>,
95    /// Base currency for the instrument
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub base_currency: Option<String>,
98    /// Counter currency for the instrument
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub counter_currency: Option<String>,
101}
102
103impl Instrument {
104    /// Check if the instrument is a perpetual contract
105    pub fn is_perpetual(&self) -> bool {
106        self.expiration_timestamp.is_none()
107            && self
108                .kind
109                .as_ref()
110                .is_some_and(|k| matches!(k, InstrumentKind::Future))
111    }
112
113    /// Check if the instrument is an option
114    pub fn is_option(&self) -> bool {
115        self.kind
116            .as_ref()
117            .is_some_and(|k| matches!(k, InstrumentKind::Option | InstrumentKind::OptionCombo))
118    }
119
120    /// Check if the instrument is a future
121    pub fn is_future(&self) -> bool {
122        self.kind
123            .as_ref()
124            .is_some_and(|k| matches!(k, InstrumentKind::Future | InstrumentKind::FutureCombo))
125    }
126
127    /// Check if the instrument is a spot
128    pub fn is_spot(&self) -> bool {
129        self.kind
130            .as_ref()
131            .is_some_and(|k| matches!(k, InstrumentKind::Spot))
132    }
133}
134
135/// Index data
136#[derive(Clone, Serialize, Deserialize)]
137pub struct IndexData {
138    /// BTC component (optional)
139    pub btc: Option<f64>,
140    /// ETH component (optional)
141    pub eth: Option<f64>,
142    /// USDC component (optional)
143    pub usdc: Option<f64>,
144    /// USDT component (optional)
145    pub usdt: Option<f64>,
146    /// EURR component (optional)
147    pub eurr: Option<f64>,
148    /// EDP (Estimated Delivery Price)
149    pub edp: f64,
150}
151
152/// Index price data
153#[derive(Clone, Serialize, Deserialize)]
154pub struct IndexPriceData {
155    /// Current index price
156    pub index_price: f64,
157    /// Estimated delivery price
158    pub estimated_delivery_price: f64,
159}
160
161// Debug implementations using pretty JSON formatting
162impl_json_debug_pretty!(Instrument, IndexData, IndexPriceData);
163
164// Display implementations using compact JSON formatting
165impl_json_display!(Instrument, IndexData, IndexPriceData);