nautilus_model/python/instruments/
index_instrument.rs1use std::{
17 collections::hash_map::DefaultHasher,
18 hash::{Hash, Hasher},
19};
20
21use nautilus_core::{
22 from_pydict,
23 python::{IntoPyObjectNautilusExt, to_pyvalue_err},
24};
25use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
26
27use crate::{
28 identifiers::{InstrumentId, Symbol},
29 instruments::IndexInstrument,
30 types::{Currency, Price, Quantity},
31};
32
33#[pymethods]
34#[pyo3_stub_gen::derive::gen_stub_pymethods]
35impl IndexInstrument {
36 #[expect(clippy::too_many_arguments)]
40 #[new]
41 #[pyo3(signature = (instrument_id, raw_symbol, currency, price_precision, size_precision, price_increment, size_increment, ts_event, ts_init, tick_scheme=None, info=None))]
42 fn py_new(
43 instrument_id: InstrumentId,
44 raw_symbol: Symbol,
45 currency: Currency,
46 price_precision: u8,
47 size_precision: u8,
48 price_increment: Price,
49 size_increment: Quantity,
50 ts_event: u64,
51 ts_init: u64,
52 tick_scheme: Option<String>,
53 info: Option<Py<PyDict>>,
54 ) -> PyResult<Self> {
55 let info_map = if let Some(info_dict) = info {
56 Python::attach(|py| from_pydict(py, &info_dict))?
57 } else {
58 None
59 };
60
61 Self::new_checked(
62 instrument_id,
63 raw_symbol,
64 currency,
65 price_precision,
66 size_precision,
67 price_increment,
68 size_increment,
69 tick_scheme.map(|name| ustr::Ustr::from(name.as_str())),
70 info_map,
71 ts_event.into(),
72 ts_init.into(),
73 )
74 .map_err(to_pyvalue_err)
75 }
76
77 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
78 match op {
79 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
80 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
81 _ => py.NotImplemented(),
82 }
83 }
84
85 fn __hash__(&self) -> isize {
86 let mut hasher = DefaultHasher::new();
87 self.hash(&mut hasher);
88 hasher.finish() as isize
89 }
90
91 #[getter]
92 fn type_name(&self) -> &'static str {
93 stringify!(IndexInstrument)
94 }
95
96 #[getter]
97 #[pyo3(name = "id")]
98 fn py_id(&self) -> InstrumentId {
99 self.id
100 }
101
102 #[getter]
103 #[pyo3(name = "raw_symbol")]
104 fn py_raw_symbol(&self) -> Symbol {
105 self.raw_symbol
106 }
107
108 #[getter]
109 #[pyo3(name = "quote_currency")]
110 fn py_quote_currency(&self) -> Currency {
111 self.currency
112 }
113
114 #[getter]
115 #[pyo3(name = "price_precision")]
116 fn py_price_precision(&self) -> u8 {
117 self.price_precision
118 }
119
120 #[getter]
121 #[pyo3(name = "size_precision")]
122 fn py_size_precision(&self) -> u8 {
123 self.size_precision
124 }
125
126 #[getter]
127 #[pyo3(name = "price_increment")]
128 fn py_price_increment(&self) -> Price {
129 self.price_increment
130 }
131
132 #[getter]
133 #[pyo3(name = "size_increment")]
134 fn py_size_increment(&self) -> Quantity {
135 self.size_increment
136 }
137
138 #[getter]
139 #[pyo3(name = "ts_event")]
140 fn py_ts_event(&self) -> u64 {
141 self.ts_event.as_u64()
142 }
143
144 #[getter]
145 #[pyo3(name = "ts_init")]
146 fn py_ts_init(&self) -> u64 {
147 self.ts_init.as_u64()
148 }
149
150 #[getter]
151 #[pyo3(name = "info")]
152 fn py_info(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
153 if let Some(ref info_map) = self.info {
154 let py_dict = PyDict::new(py);
155
156 for (key, value) in info_map {
157 let json_str = serde_json::to_string(value).map_err(to_pyvalue_err)?;
158 let py_value =
159 PyModule::import(py, "json")?.call_method("loads", (json_str,), None)?;
160 py_dict.set_item(key, py_value)?;
161 }
162 Ok(py_dict.unbind())
163 } else {
164 Ok(PyDict::new(py).unbind())
165 }
166 }
167
168 #[staticmethod]
169 #[pyo3(name = "from_dict")]
170 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
171 crate::python::instruments::from_dict_instrument_pyo3(py, values)
172 }
173
174 #[pyo3(name = "to_dict")]
175 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
176 let dict = PyDict::new(py);
177 dict.set_item("type", stringify!(IndexInstrument))?;
178 dict.set_item("id", self.id.to_string())?;
179 dict.set_item("raw_symbol", self.raw_symbol.to_string())?;
180 dict.set_item("currency", self.currency.code.to_string())?;
181 dict.set_item("price_precision", self.price_precision)?;
182 dict.set_item("size_precision", self.size_precision)?;
183 dict.set_item("price_increment", self.price_increment.to_string())?;
184 dict.set_item("size_increment", self.size_increment.to_string())?;
185 dict.set_item("ts_event", self.ts_event.as_u64())?;
186 dict.set_item("ts_init", self.ts_init.as_u64())?;
187
188 if let Some(ref info_map) = self.info {
189 let info_dict = PyDict::new(py);
190
191 for (key, value) in info_map {
192 let json_str = serde_json::to_string(value).map_err(to_pyvalue_err)?;
193 let py_value =
194 PyModule::import(py, "json")?.call_method("loads", (json_str,), None)?;
195 info_dict.set_item(key, py_value)?;
196 }
197 dict.set_item("info", info_dict)?;
198 } else {
199 dict.set_item("info", PyDict::new(py))?;
200 }
201 dict.set_item(
202 "tick_scheme",
203 crate::python::instruments::tick_scheme_to_py(self),
204 )?;
205 Ok(dict.into())
206 }
207}