deribit_base/model/
options.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 9/9/25
5******************************************************************************/
6use crate::prelude::{Instrument, TickerData};
7
8use chrono::{DateTime, TimeZone, Utc};
9use pretty_simple_display::{DebugPretty, DisplaySimple};
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13/// Combined option instrument data with ticker information
14#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
15pub struct OptionInstrument {
16    /// The instrument details
17    pub instrument: Instrument,
18    /// Real-time ticker data for the option
19    pub ticker: TickerData,
20}
21
22/// A pair of option instruments representing both call and put options for the same underlying asset
23///
24/// This structure groups together the call and put options for a specific underlying asset,
25/// allowing for easy access to both sides of an option strategy. Both options are optional,
26/// meaning you can have just a call, just a put, or both.
27///
28#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
29pub struct OptionInstrumentPair {
30    /// Call option instrument data, if available
31    pub call: Option<OptionInstrument>,
32    /// Put option instrument data, if available  
33    pub put: Option<OptionInstrument>,
34}
35
36/// Spread information for bid/ask prices
37#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
38pub struct Spread {
39    /// Best bid price
40    bid: Option<f64>,
41    /// Best ask price
42    ask: Option<f64>,
43    /// Mid price (average of bid and ask)
44    mid: Option<f64>,
45}
46
47/// Basic Greeks values for option pricing
48#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
49pub struct BasicGreeks {
50    /// Delta value for call option
51    delta_call: Option<f64>,
52    /// Delta value for put option
53    delta_put: Option<f64>,
54    /// Gamma value (rate of change of delta)
55    gamma: Option<f64>,
56}
57
58/// Comprehensive option data structure containing all relevant pricing and risk information
59#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
60pub struct BasicOptionData {
61    /// Strike price of the option
62    pub strike_price: f64,
63    /// Best bid price for call option
64    pub call_bid: Option<f64>,
65    /// Best ask price for call option
66    pub call_ask: Option<f64>,
67    /// Best bid price for put option
68    pub put_bid: Option<f64>,
69    /// Best ask price for put option
70    pub put_ask: Option<f64>,
71    /// Implied volatility for call and put options (call_iv, put_iv)
72    pub implied_volatility: (Option<f64>, Option<f64>),
73    /// Delta value for call option
74    pub delta_call: Option<f64>,
75    /// Delta value for put option
76    pub delta_put: Option<f64>,
77    /// Gamma value (rate of change of delta)
78    pub gamma: Option<f64>,
79    /// Total trading volume
80    pub volume: f64,
81    /// Total open interest
82    pub open_interest: f64,
83    /// Option expiration date
84    pub expiration_date: Option<DateTime<Utc>>,
85    /// Current price of the underlying asset
86    pub underlying_price: Option<f64>,
87    /// Risk-free interest rate
88    pub risk_free_rate: f64,
89    /// Additional fields as JSON value
90    pub extra_fields: Option<Value>,
91}
92
93#[allow(dead_code)]
94impl OptionInstrumentPair {
95    pub fn expiration(&self) -> Option<DateTime<Utc>> {
96        let expiration_timestamp = match self.instrument() {
97            Some(i) => i.expiration_timestamp,
98            None => return None,
99        };
100
101        if let Some(expiration_timestamp) = expiration_timestamp {
102            Utc.timestamp_millis_opt(expiration_timestamp).single()
103        } else {
104            None
105        }
106    }
107    pub fn instrument(&self) -> Option<Instrument> {
108        self.call
109            .as_ref()
110            .map(|i| i.instrument.clone())
111            .or_else(|| self.put.as_ref().map(|i| i.instrument.clone()))
112    }
113    pub fn ticker(&self) -> Option<TickerData> {
114        self.call
115            .as_ref()
116            .map(|i| i.ticker.clone())
117            .or_else(|| self.put.as_ref().map(|i| i.ticker.clone()))
118    }
119    pub fn volume(&self) -> f64 {
120        let mut volume: f64 = 0.0;
121        if let Some(call) = &self.call {
122            volume += call.ticker.stats.volume
123        }
124        if let Some(put) = &self.put {
125            volume += put.ticker.stats.volume
126        }
127        volume
128    }
129    pub fn open_interest(&self) -> f64 {
130        let mut open_interest: f64 = 0.0;
131        if let Some(call) = &self.call {
132            open_interest += call.ticker.open_interest.unwrap_or(0.0)
133        }
134        if let Some(put) = &self.put {
135            open_interest += put.ticker.open_interest.unwrap_or(0.0)
136        }
137        open_interest
138    }
139
140    pub fn interest_rate(&self) -> f64 {
141        let mut interest_rate: f64 = 0.0;
142        if let Some(call) = &self.call {
143            interest_rate += call.ticker.interest_rate.unwrap_or(0.0)
144        }
145        if let Some(put) = &self.put {
146            interest_rate += put.ticker.interest_rate.unwrap_or(0.0)
147        }
148        interest_rate
149    }
150
151    pub fn value(&self) -> Option<Value> {
152        serde_json::to_value(self).ok()
153    }
154
155    pub fn call_spread(&self) -> Spread {
156        if let Some(call) = &self.call {
157            let bid = call.ticker.best_bid_price;
158            let ask = call.ticker.best_ask_price;
159            let mid = match (bid, ask) {
160                (Some(b), Some(a)) => Some((b + a) / 2.0),
161                (Some(b), None) => Some(b),
162                (None, Some(a)) => Some(a),
163                (None, None) => None,
164            };
165            Spread { bid, ask, mid }
166        } else {
167            Spread {
168                bid: None,
169                ask: None,
170                mid: None,
171            }
172        }
173    }
174
175    pub fn put_spread(&self) -> Spread {
176        if let Some(put) = &self.put {
177            let bid = put.ticker.best_bid_price;
178            let ask = put.ticker.best_ask_price;
179            let mid = match (bid, ask) {
180                (Some(b), Some(a)) => Some((b + a) / 2.0),
181                (Some(b), None) => Some(b),
182                (None, Some(a)) => Some(a),
183                (None, None) => None,
184            };
185            Spread { bid, ask, mid }
186        } else {
187            Spread {
188                bid: None,
189                ask: None,
190                mid: None,
191            }
192        }
193    }
194
195    pub fn iv(&self) -> (Option<f64>, Option<f64>) {
196        let call_iv = self.call.as_ref().and_then(|c| c.ticker.mark_iv);
197        let put_iv = self.put.as_ref().and_then(|p| p.ticker.mark_iv);
198        (call_iv, put_iv)
199    }
200
201    pub fn greeks(&self) -> BasicGreeks {
202        let delta_call = self
203            .call
204            .as_ref()
205            .and_then(|c| c.ticker.greeks.as_ref().and_then(|g| g.delta));
206        let delta_put = self
207            .put
208            .as_ref()
209            .and_then(|p| p.ticker.greeks.as_ref().and_then(|g| g.delta));
210        let gamma = self
211            .call
212            .as_ref()
213            .and_then(|c| c.ticker.greeks.as_ref().and_then(|g| g.gamma))
214            .or_else(|| {
215                self.put
216                    .as_ref()
217                    .and_then(|p| p.ticker.greeks.as_ref().and_then(|g| g.gamma))
218            });
219        BasicGreeks {
220            delta_call,
221            delta_put,
222            gamma,
223        }
224    }
225
226    pub fn data(&self) -> BasicOptionData {
227        let strike_price: f64 = match self.instrument() {
228            Some(i) => i.strike.unwrap_or(0.0),
229            None => 0.0,
230        };
231        let call_spread = self.call_spread();
232        let call_bid: Option<f64> = call_spread.bid;
233        let call_ask: Option<f64> = call_spread.ask;
234        let put_spread = self.put_spread();
235        let put_bid: Option<f64> = put_spread.bid;
236        let put_ask: Option<f64> = put_spread.ask;
237        let implied_volatility = self.iv();
238        let greeks = self.greeks();
239        let delta_call: Option<f64> = greeks.delta_call;
240        let delta_put: Option<f64> = greeks.delta_put;
241        let gamma: Option<f64> = greeks.gamma;
242        let volume = self.volume();
243        let open_interest: f64 = self.open_interest();
244        let expiration_date: Option<DateTime<Utc>> = self.expiration();
245        let underlying_price: Option<f64> = self.ticker().and_then(|t| t.underlying_price);
246        let risk_free_rate: f64 = self.interest_rate();
247        let extra_fields: Option<Value> = self.value();
248        BasicOptionData {
249            strike_price,
250            call_bid,
251            call_ask,
252            put_bid,
253            put_ask,
254            implied_volatility,
255            delta_call,
256            delta_put,
257            gamma,
258            volume,
259            open_interest,
260            expiration_date,
261            underlying_price,
262            risk_free_rate,
263            extra_fields,
264        }
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::*;
271    use crate::model::ticker::{Greeks, TickerStats};
272    use serde_json;
273
274    fn create_test_instrument(name: &str, strike: f64, option_type: &str) -> Instrument {
275        use crate::model::instrument::{InstrumentKind, InstrumentType, OptionType};
276
277        Instrument {
278            instrument_name: name.to_string(),
279            strike: Some(strike),
280            option_type: Some(match option_type {
281                "call" => OptionType::Call,
282                "put" => OptionType::Put,
283                _ => OptionType::Call,
284            }),
285            expiration_timestamp: Some(1757491200000),
286            kind: Some(InstrumentKind::Option),
287            instrument_type: Some(InstrumentType::Reversed),
288            currency: Some("BTC".to_string()),
289            is_active: Some(true),
290            contract_size: Some(1.0),
291            tick_size: Some(0.0001),
292            min_trade_amount: Some(0.1),
293            settlement_currency: Some("BTC".to_string()),
294            base_currency: Some("BTC".to_string()),
295            counter_currency: Some("USD".to_string()),
296            quote_currency: Some("BTC".to_string()),
297            price_index: None,
298            maker_commission: None,
299            taker_commission: None,
300            instrument_id: None,
301            creation_timestamp: None,
302            settlement_period: None,
303            max_leverage: None,
304        }
305    }
306
307    #[allow(clippy::too_many_arguments)]
308    fn create_test_ticker(
309        instrument_name: &str,
310        last_price: f64,
311        mark_price: f64,
312        bid_price: Option<f64>,
313        ask_price: Option<f64>,
314        bid_amount: f64,
315        ask_amount: f64,
316        volume: f64,
317        open_interest: f64,
318        delta: Option<f64>,
319        gamma: Option<f64>,
320        mark_iv: Option<f64>,
321    ) -> TickerData {
322        TickerData {
323            instrument_name: instrument_name.to_string(),
324            last_price: Some(last_price),
325            mark_price,
326            best_bid_price: bid_price,
327            best_ask_price: ask_price,
328            best_bid_amount: bid_amount,
329            best_ask_amount: ask_amount,
330            timestamp: 1757476246684,
331            state: "open".to_string(),
332            stats: TickerStats {
333                volume,
334                volume_usd: Some(volume * 1000.0),
335                high: Some(0.1),
336                low: Some(0.01),
337                price_change: Some(5.0),
338            },
339            greeks: Some(Greeks {
340                delta,
341                gamma,
342                vega: Some(0.02544),
343                theta: Some(-0.84746),
344                rho: Some(0.50202),
345            }),
346            open_interest: Some(open_interest),
347            mark_iv,
348            underlying_price: Some(111421.0915),
349            interest_rate: Some(0.0),
350            volume: None,
351            volume_usd: None,
352            high: None,
353            low: None,
354            price_change: None,
355            price_change_percentage: None,
356            bid_iv: None,
357            ask_iv: None,
358            settlement_price: None,
359            index_price: None,
360            min_price: None,
361            max_price: None,
362            underlying_index: None,
363            estimated_delivery_price: None,
364        }
365    }
366
367    #[test]
368    fn test_option_instrument_creation() {
369        let instrument = create_test_instrument("BTC-10SEP25-106000-C", 106000.0, "call");
370        let ticker = create_test_ticker(
371            "BTC-10SEP25-106000-C",
372            0.047,
373            0.0487,
374            Some(0.0001),
375            Some(0.05),
376            10.0,
377            30.8,
378            104.2,
379            49.6,
380            Some(0.99972),
381            Some(0.0),
382            Some(66.62),
383        );
384
385        let option_instrument = OptionInstrument { instrument, ticker };
386
387        assert_eq!(
388            option_instrument.instrument.instrument_name,
389            "BTC-10SEP25-106000-C"
390        );
391        assert_eq!(option_instrument.instrument.strike, Some(106000.0));
392        assert_eq!(option_instrument.ticker.last_price, Some(0.047));
393        assert_eq!(option_instrument.ticker.mark_price, 0.0487);
394    }
395
396    #[test]
397    fn test_option_instrument_pair_serialization() {
398        let json_str = r#"{
399            "call": {
400                "instrument": {
401                    "instrument_name": "BTC-10SEP25-106000-C",
402                    "strike": 106000.0,
403                    "option_type": "call",
404                    "expiration_timestamp": 1757491200000
405                },
406                "ticker": {
407                    "instrument_name": "BTC-10SEP25-106000-C",
408                    "timestamp": 1757476246684,
409                    "state": "open",
410                    "last_price": 0.047,
411                    "mark_price": 0.0487,
412                    "best_bid_price": 0.0001,
413                    "best_ask_price": 0.05,
414                    "best_bid_amount": 10.0,
415                    "best_ask_amount": 30.8,
416                    "stats": {
417                        "volume": 104.2,
418                        "volume_usd": 666962.69,
419                        "high": 0.0635,
420                        "low": 0.0245,
421                        "price_change": -21.0084
422                    }
423                }
424            },
425            "put": null
426        }"#;
427
428        let pair: OptionInstrumentPair =
429            serde_json::from_str(json_str).expect("Failed to deserialize OptionInstrumentPair");
430
431        assert!(pair.call.is_some());
432        assert!(pair.put.is_none());
433
434        let call = pair.call.as_ref().unwrap();
435        assert_eq!(call.instrument.instrument_name, "BTC-10SEP25-106000-C");
436        assert_eq!(call.instrument.strike, Some(106000.0));
437    }
438
439    #[test]
440    fn test_spread_creation() {
441        let spread = Spread {
442            bid: Some(0.045),
443            ask: Some(0.055),
444            mid: Some(0.05),
445        };
446
447        assert_eq!(spread.bid, Some(0.045));
448        assert_eq!(spread.ask, Some(0.055));
449        assert_eq!(spread.mid, Some(0.05));
450    }
451
452    #[test]
453    fn test_basic_greeks_creation() {
454        let greeks = BasicGreeks {
455            delta_call: Some(0.99972),
456            delta_put: Some(-0.00077),
457            gamma: Some(0.0),
458        };
459
460        assert_eq!(greeks.delta_call, Some(0.99972));
461        assert_eq!(greeks.delta_put, Some(-0.00077));
462        assert_eq!(greeks.gamma, Some(0.0));
463    }
464
465    #[test]
466    fn test_basic_option_data_creation() {
467        let expiration = Utc.timestamp_millis_opt(1757491200000).single();
468        let option_data = BasicOptionData {
469            strike_price: 106000.0,
470            call_bid: Some(0.0001),
471            call_ask: Some(0.05),
472            put_bid: Some(0.0),
473            put_ask: Some(0.019),
474            implied_volatility: (Some(66.62), Some(107.51)),
475            delta_call: Some(0.99972),
476            delta_put: Some(-0.00077),
477            gamma: Some(0.0),
478            volume: 196.1,
479            open_interest: 75.2,
480            expiration_date: expiration,
481            underlying_price: Some(111421.0915),
482            risk_free_rate: 0.0,
483            extra_fields: None,
484        };
485
486        assert_eq!(option_data.strike_price, 106000.0);
487        assert_eq!(option_data.call_bid, Some(0.0001));
488        assert_eq!(option_data.volume, 196.1);
489        assert_eq!(option_data.open_interest, 75.2);
490    }
491
492    fn create_test_option_pair() -> OptionInstrumentPair {
493        let call_instrument = create_test_instrument("BTC-10SEP25-106000-C", 106000.0, "call");
494        let call_ticker = create_test_ticker(
495            "BTC-10SEP25-106000-C",
496            0.047,
497            0.0487,
498            Some(0.0001),
499            Some(0.05),
500            10.0,
501            30.8,
502            104.2,
503            49.6,
504            Some(0.99972),
505            Some(0.0),
506            Some(66.62),
507        );
508
509        let put_instrument = create_test_instrument("BTC-10SEP25-106000-P", 106000.0, "put");
510        let put_ticker = create_test_ticker(
511            "BTC-10SEP25-106000-P",
512            0.0002,
513            0.0,
514            Some(0.0),
515            Some(0.019),
516            0.0,
517            10.0,
518            91.9,
519            25.6,
520            Some(-0.00077),
521            Some(0.0),
522            Some(107.51),
523        );
524
525        OptionInstrumentPair {
526            call: Some(OptionInstrument {
527                instrument: call_instrument,
528                ticker: call_ticker,
529            }),
530            put: Some(OptionInstrument {
531                instrument: put_instrument,
532                ticker: put_ticker,
533            }),
534        }
535    }
536
537    #[test]
538    fn test_option_pair_expiration() {
539        let pair = create_test_option_pair();
540        let expiration = pair.expiration();
541
542        assert!(expiration.is_some());
543        let exp_date = expiration.unwrap();
544        assert_eq!(exp_date.timestamp_millis(), 1757491200000);
545    }
546
547    #[test]
548    fn test_option_pair_instrument() {
549        let pair = create_test_option_pair();
550        let instrument = pair.instrument();
551
552        assert!(instrument.is_some());
553        let inst = instrument.unwrap();
554        assert_eq!(inst.instrument_name, "BTC-10SEP25-106000-C");
555        assert_eq!(inst.strike, Some(106000.0));
556    }
557
558    #[test]
559    fn test_option_pair_ticker() {
560        let pair = create_test_option_pair();
561        let ticker = pair.ticker();
562
563        assert!(ticker.is_some());
564        let tick = ticker.unwrap();
565        assert_eq!(tick.instrument_name, "BTC-10SEP25-106000-C");
566        assert_eq!(tick.last_price, Some(0.047));
567    }
568
569    #[test]
570    fn test_option_pair_volume() {
571        let pair = create_test_option_pair();
572        let volume = pair.volume();
573
574        // Should be sum of call (104.2) + put (91.9) = 196.1
575        assert!((volume - 196.1).abs() < 1e-10);
576    }
577
578    #[test]
579    fn test_option_pair_open_interest() {
580        let pair = create_test_option_pair();
581        let open_interest = pair.open_interest();
582
583        // Should be sum of call (49.6) + put (25.6) = 75.2
584        assert_eq!(open_interest, 75.2);
585    }
586
587    #[test]
588    fn test_option_pair_interest_rate() {
589        let pair = create_test_option_pair();
590        let interest_rate = pair.interest_rate();
591
592        // Both call and put have 0.0 interest rate
593        assert_eq!(interest_rate, 0.0);
594    }
595
596    #[test]
597    fn test_option_pair_value() {
598        let pair = create_test_option_pair();
599        let value = pair.value();
600
601        assert!(value.is_some());
602        // Should be able to serialize to JSON
603        let json_value = value.unwrap();
604        assert!(json_value.is_object());
605    }
606
607    #[test]
608    fn test_option_pair_call_spread() {
609        let pair = create_test_option_pair();
610        let call_spread = pair.call_spread();
611
612        assert_eq!(call_spread.bid, Some(0.0001));
613        assert_eq!(call_spread.ask, Some(0.05));
614        assert_eq!(call_spread.mid, Some((0.0001 + 0.05) / 2.0));
615    }
616
617    #[test]
618    fn test_option_pair_put_spread() {
619        let pair = create_test_option_pair();
620        let put_spread = pair.put_spread();
621
622        assert_eq!(put_spread.bid, Some(0.0));
623        assert_eq!(put_spread.ask, Some(0.019));
624        assert_eq!(put_spread.mid, Some((0.0 + 0.019) / 2.0));
625    }
626
627    #[test]
628    fn test_option_pair_iv() {
629        let pair = create_test_option_pair();
630        let (call_iv, put_iv) = pair.iv();
631
632        assert_eq!(call_iv, Some(66.62));
633        assert_eq!(put_iv, Some(107.51));
634    }
635
636    #[test]
637    fn test_option_pair_greeks() {
638        let pair = create_test_option_pair();
639        let greeks = pair.greeks();
640
641        assert_eq!(greeks.delta_call, Some(0.99972));
642        assert_eq!(greeks.delta_put, Some(-0.00077));
643        assert_eq!(greeks.gamma, Some(0.0));
644    }
645
646    #[test]
647    fn test_option_pair_data() {
648        let pair = create_test_option_pair();
649        let data = pair.data();
650
651        assert_eq!(data.strike_price, 106000.0);
652        assert_eq!(data.call_bid, Some(0.0001));
653        assert_eq!(data.call_ask, Some(0.05));
654        assert_eq!(data.put_bid, Some(0.0));
655        assert_eq!(data.put_ask, Some(0.019));
656        assert_eq!(data.implied_volatility, (Some(66.62), Some(107.51)));
657        assert_eq!(data.delta_call, Some(0.99972));
658        assert_eq!(data.delta_put, Some(-0.00077));
659        assert_eq!(data.gamma, Some(0.0));
660        assert!((data.volume - 196.1).abs() < 1e-10);
661        assert_eq!(data.open_interest, 75.2);
662        assert_eq!(data.underlying_price, Some(111421.0915));
663        assert_eq!(data.risk_free_rate, 0.0);
664    }
665
666    #[test]
667    fn test_option_pair_with_only_call() {
668        let call_instrument = create_test_instrument("BTC-10SEP25-106000-C", 106000.0, "call");
669        let call_ticker = create_test_ticker(
670            "BTC-10SEP25-106000-C",
671            0.047,
672            0.0487,
673            Some(0.0001),
674            Some(0.05),
675            10.0,
676            30.8,
677            104.2,
678            49.6,
679            Some(0.99972),
680            Some(0.0),
681            Some(66.62),
682        );
683
684        let pair = OptionInstrumentPair {
685            call: Some(OptionInstrument {
686                instrument: call_instrument,
687                ticker: call_ticker,
688            }),
689            put: None,
690        };
691
692        assert_eq!(pair.volume(), 104.2);
693        assert_eq!(pair.open_interest(), 49.6);
694
695        let put_spread = pair.put_spread();
696        assert_eq!(put_spread.bid, None);
697        assert_eq!(put_spread.ask, None);
698        assert_eq!(put_spread.mid, None);
699
700        let (call_iv, put_iv) = pair.iv();
701        assert_eq!(call_iv, Some(66.62));
702        assert_eq!(put_iv, None);
703    }
704
705    #[test]
706    fn test_option_pair_with_only_put() {
707        let put_instrument = create_test_instrument("BTC-10SEP25-106000-P", 106000.0, "put");
708        let put_ticker = create_test_ticker(
709            "BTC-10SEP25-106000-P",
710            0.0002,
711            0.0,
712            Some(0.0),
713            Some(0.019),
714            0.0,
715            10.0,
716            91.9,
717            25.6,
718            Some(-0.00077),
719            Some(0.0),
720            Some(107.51),
721        );
722
723        let pair = OptionInstrumentPair {
724            call: None,
725            put: Some(OptionInstrument {
726                instrument: put_instrument,
727                ticker: put_ticker,
728            }),
729        };
730
731        assert_eq!(pair.volume(), 91.9);
732        assert_eq!(pair.open_interest(), 25.6);
733
734        let call_spread = pair.call_spread();
735        assert_eq!(call_spread.bid, None);
736        assert_eq!(call_spread.ask, None);
737        assert_eq!(call_spread.mid, None);
738
739        let (call_iv, put_iv) = pair.iv();
740        assert_eq!(call_iv, None);
741        assert_eq!(put_iv, Some(107.51));
742    }
743
744    #[test]
745    fn test_empty_option_pair() {
746        let pair = OptionInstrumentPair {
747            call: None,
748            put: None,
749        };
750
751        assert_eq!(pair.volume(), 0.0);
752        assert_eq!(pair.open_interest(), 0.0);
753        assert_eq!(pair.interest_rate(), 0.0);
754        assert!(pair.instrument().is_none());
755        assert!(pair.ticker().is_none());
756        assert!(pair.expiration().is_none());
757
758        let (call_iv, put_iv) = pair.iv();
759        assert_eq!(call_iv, None);
760        assert_eq!(put_iv, None);
761
762        let greeks = pair.greeks();
763        assert_eq!(greeks.delta_call, None);
764        assert_eq!(greeks.delta_put, None);
765        assert_eq!(greeks.gamma, None);
766    }
767}