Skip to main content

nautilus_model/python/identifiers/
option_series_id.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{
17    collections::hash_map::DefaultHasher,
18    hash::{Hash, Hasher},
19    str::FromStr,
20};
21
22use nautilus_core::UnixNanos;
23use pyo3::{prelude::*, pyclass::CompareOp};
24
25use crate::{
26    identifiers::{OptionSeriesId, Venue},
27    python::option_series_id_error_to_pyvalue_err,
28};
29
30#[pymethods]
31#[pyo3_stub_gen::derive::gen_stub_pymethods]
32impl OptionSeriesId {
33    /// Identifies a unique option series: a specific venue + underlying + settlement currency + expiration.
34    #[new]
35    fn py_new(
36        venue: &str,
37        underlying: &str,
38        settlement_currency: &str,
39        expiration_ns: u64,
40    ) -> PyResult<Self> {
41        Self::from_expiry_ns(
42            venue,
43            underlying,
44            settlement_currency,
45            UnixNanos::from(expiration_ns),
46        )
47        .map_err(option_series_id_error_to_pyvalue_err)
48    }
49
50    /// Creates an `OptionSeriesId` from venue name, underlying symbol, settlement currency, and date string.
51    ///
52    /// The `date_str` is parsed via `UnixNanos::FromStr`, which accepts `"YYYY-MM-DD"`,
53    /// RFC 3339 timestamps, integer nanoseconds, or floating-point seconds.
54    ///
55    /// # Errors
56    ///
57    /// Returns an error if `venue` or `date_str` is invalid.
58    #[staticmethod]
59    #[pyo3(name = "from_expiry")]
60    fn py_from_expiry(
61        venue: &str,
62        underlying: &str,
63        settlement_currency: &str,
64        date_str: &str,
65    ) -> PyResult<Self> {
66        Self::from_expiry(venue, underlying, settlement_currency, date_str)
67            .map_err(option_series_id_error_to_pyvalue_err)
68    }
69
70    #[staticmethod]
71    #[pyo3(name = "from_str")]
72    fn py_from_str(value: &str) -> PyResult<Self> {
73        Self::from_str(value).map_err(option_series_id_error_to_pyvalue_err)
74    }
75
76    #[getter]
77    #[pyo3(name = "venue")]
78    fn py_venue(&self) -> Venue {
79        self.venue
80    }
81
82    #[getter]
83    #[pyo3(name = "underlying")]
84    fn py_underlying(&self) -> String {
85        self.underlying.to_string()
86    }
87
88    #[getter]
89    #[pyo3(name = "settlement_currency")]
90    fn py_settlement_currency(&self) -> String {
91        self.settlement_currency.to_string()
92    }
93
94    #[getter]
95    #[pyo3(name = "expiration_ns")]
96    fn py_expiration_ns(&self) -> u64 {
97        self.expiration_ns.as_u64()
98    }
99
100    #[getter]
101    #[pyo3(name = "value")]
102    fn py_value(&self) -> String {
103        self.to_string()
104    }
105
106    fn __richcmp__(&self, other: &Self, op: CompareOp) -> bool {
107        match op {
108            CompareOp::Eq => self == other,
109            CompareOp::Ne => self != other,
110            CompareOp::Ge => self >= other,
111            CompareOp::Gt => self > other,
112            CompareOp::Le => self <= other,
113            CompareOp::Lt => self < other,
114        }
115    }
116
117    fn __hash__(&self) -> isize {
118        let mut h = DefaultHasher::new();
119        self.hash(&mut h);
120        h.finish() as isize
121    }
122
123    fn __repr__(&self) -> String {
124        format!("OptionSeriesId('{self}')")
125    }
126
127    fn __str__(&self) -> String {
128        self.to_string()
129    }
130}