nautilus_model/python/data/
close.rs1use std::{
17 collections::{HashMap, hash_map::DefaultHasher},
18 hash::{Hash, Hasher},
19};
20
21use nautilus_core::{
22 python::{
23 IntoPyObjectNautilusExt,
24 serialization::{from_dict_pyo3, to_dict_pyo3},
25 to_pyvalue_err,
26 },
27 serialization::{
28 Serializable,
29 msgpack::{FromMsgPack, ToMsgPack},
30 },
31};
32use pyo3::{basic::CompareOp, exceptions::PyValueError, prelude::*, types::PyDict};
33
34use super::ERROR_MONOTONICITY;
35use crate::{
36 data::close::InstrumentClose, enums::InstrumentCloseType, identifiers::InstrumentId,
37 python::common::PY_MODULE_MODEL, types::Price,
38};
39
40#[pymethods]
44impl InstrumentClose {
45 #[new]
46 #[pyo3(signature = (instrument_id, close_price, close_type, ts_event, ts_init))]
47 fn py_new(
48 instrument_id: InstrumentId,
49 close_price: Price,
50 close_type: InstrumentCloseType,
51 ts_event: u64,
52 ts_init: u64,
53 ) -> Self {
54 Self {
55 instrument_id,
56 close_price,
57 close_type,
58 ts_event: ts_event.into(),
59 ts_init: ts_init.into(),
60 }
61 }
62
63 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
64 match op {
65 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
66 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
67 _ => py.NotImplemented(),
68 }
69 }
70
71 fn __hash__(&self) -> isize {
72 let mut h = DefaultHasher::new();
73 self.hash(&mut h);
74 h.finish() as isize
75 }
76
77 fn __repr__(&self) -> String {
78 format!("{}({})", stringify!(InstrumentStatus), self)
79 }
80
81 fn __str__(&self) -> String {
82 self.to_string()
83 }
84
85 #[getter]
86 #[pyo3(name = "instrument_id")]
87 fn py_instrument_id(&self) -> InstrumentId {
88 self.instrument_id
89 }
90
91 #[getter]
92 #[pyo3(name = "close_price")]
93 pub fn py_close_price(&self) -> Price {
94 self.close_price
95 }
96
97 #[getter]
98 #[pyo3(name = "close_type")]
99 pub fn py_close_type(&self) -> InstrumentCloseType {
100 self.close_type
101 }
102
103 #[getter]
104 #[pyo3(name = "ts_event")]
105 pub fn py_ts_event(&self) -> u64 {
106 self.ts_event.as_u64()
107 }
108
109 #[getter]
110 #[pyo3(name = "ts_init")]
111 pub fn py_ts_init(&self) -> u64 {
112 self.ts_init.as_u64()
113 }
114
115 #[staticmethod]
116 #[pyo3(name = "fully_qualified_name")]
117 fn py_fully_qualified_name() -> String {
118 format!("{}:{}", PY_MODULE_MODEL, stringify!(InstrumentClose))
119 }
120
121 #[staticmethod]
122 #[pyo3(name = "get_metadata")]
123 fn py_get_metadata(
124 instrument_id: &InstrumentId,
125 price_precision: u8,
126 ) -> PyResult<HashMap<String, String>> {
127 Ok(Self::get_metadata(instrument_id, price_precision))
128 }
129
130 #[staticmethod]
131 #[pyo3(name = "get_fields")]
132 fn py_get_fields(py: Python<'_>) -> PyResult<Bound<'_, PyDict>> {
133 let py_dict = PyDict::new(py);
134 for (k, v) in Self::get_fields() {
135 py_dict.set_item(k, v)?;
136 }
137
138 Ok(py_dict)
139 }
140
141 #[staticmethod]
143 #[pyo3(name = "from_dict")]
144 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
145 from_dict_pyo3(py, values)
146 }
147
148 #[staticmethod]
149 #[pyo3(name = "from_json")]
150 fn py_from_json(data: Vec<u8>) -> PyResult<Self> {
151 Self::from_json_bytes(&data).map_err(to_pyvalue_err)
152 }
153
154 #[staticmethod]
155 #[pyo3(name = "from_msgpack")]
156 fn py_from_msgpack(data: Vec<u8>) -> PyResult<Self> {
157 Self::from_msgpack_bytes(&data).map_err(to_pyvalue_err)
158 }
159
160 #[pyo3(name = "to_dict")]
162 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
163 to_dict_pyo3(py, self)
164 }
165
166 #[pyo3(name = "to_json_bytes")]
168 fn py_to_json_bytes(&self, py: Python<'_>) -> Py<PyAny> {
169 self.to_json_bytes().unwrap().into_py_any_unwrap(py)
171 }
172
173 #[pyo3(name = "to_msgpack_bytes")]
175 fn py_to_msgpack_bytes(&self, py: Python<'_>) -> Py<PyAny> {
176 self.to_msgpack_bytes().unwrap().into_py_any_unwrap(py)
178 }
179}
180
181impl InstrumentClose {
182 pub fn from_pyobject(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
188 let instrument_id = obj.getattr("instrument_id")?.extract::<InstrumentId>()?;
189 let close_price = obj.getattr("close_price")?.extract::<Price>()?;
190 let close_type = obj
191 .getattr("close_type")?
192 .extract::<InstrumentCloseType>()?;
193 let ts_event = obj.getattr("ts_event")?.extract::<u64>()?;
194 let ts_init = obj.getattr("ts_init")?.extract::<u64>()?;
195
196 Ok(Self {
197 instrument_id,
198 close_price,
199 close_type,
200 ts_event: ts_event.into(),
201 ts_init: ts_init.into(),
202 })
203 }
204}
205
206pub fn pyobjects_to_instrument_closes(
212 data: Vec<Bound<'_, PyAny>>,
213) -> PyResult<Vec<InstrumentClose>> {
214 let closes = data
215 .into_iter()
216 .map(|obj| InstrumentClose::from_pyobject(&obj))
217 .collect::<PyResult<Vec<InstrumentClose>>>()?;
218
219 if !crate::data::is_monotonically_increasing_by_init(&closes) {
221 return Err(PyValueError::new_err(ERROR_MONOTONICITY));
222 }
223
224 Ok(closes)
225}