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, 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]
44#[pyo3_stub_gen::derive::gen_stub_pymethods]
45impl InstrumentClose {
46 #[new]
48 #[pyo3(signature = (instrument_id, close_price, close_type, ts_event, ts_init))]
49 fn py_new(
50 instrument_id: InstrumentId,
51 close_price: Price,
52 close_type: InstrumentCloseType,
53 ts_event: u64,
54 ts_init: u64,
55 ) -> Self {
56 Self {
57 instrument_id,
58 close_price,
59 close_type,
60 ts_event: ts_event.into(),
61 ts_init: ts_init.into(),
62 }
63 }
64
65 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
66 match op {
67 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
68 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
69 _ => py.NotImplemented(),
70 }
71 }
72
73 fn __hash__(&self) -> isize {
74 let mut h = DefaultHasher::new();
75 self.hash(&mut h);
76 h.finish() as isize
77 }
78
79 fn __repr__(&self) -> String {
80 format!("{}({})", stringify!(InstrumentStatus), self)
81 }
82
83 fn __str__(&self) -> String {
84 self.to_string()
85 }
86
87 #[getter]
88 #[pyo3(name = "instrument_id")]
89 fn py_instrument_id(&self) -> InstrumentId {
90 self.instrument_id
91 }
92
93 #[getter]
94 #[pyo3(name = "close_price")]
95 #[must_use]
96 pub fn py_close_price(&self) -> Price {
97 self.close_price
98 }
99
100 #[getter]
101 #[pyo3(name = "close_type")]
102 #[must_use]
103 pub fn py_close_type(&self) -> InstrumentCloseType {
104 self.close_type
105 }
106
107 #[getter]
108 #[pyo3(name = "ts_event")]
109 #[must_use]
110 pub fn py_ts_event(&self) -> u64 {
111 self.ts_event.as_u64()
112 }
113
114 #[getter]
115 #[pyo3(name = "ts_init")]
116 #[must_use]
117 pub fn py_ts_init(&self) -> u64 {
118 self.ts_init.as_u64()
119 }
120
121 #[staticmethod]
122 #[pyo3(name = "fully_qualified_name")]
123 fn py_fully_qualified_name() -> String {
124 format!("{}:{}", PY_MODULE_MODEL, stringify!(InstrumentClose))
125 }
126
127 #[staticmethod]
129 #[pyo3(name = "get_metadata")]
130 fn py_get_metadata(
131 instrument_id: &InstrumentId,
132 price_precision: u8,
133 ) -> HashMap<String, String> {
134 Self::get_metadata(instrument_id, price_precision)
135 }
136
137 #[staticmethod]
139 #[pyo3(name = "get_fields")]
140 fn py_get_fields(py: Python<'_>) -> PyResult<Bound<'_, PyDict>> {
141 let py_dict = PyDict::new(py);
142 for (k, v) in Self::get_fields() {
143 py_dict.set_item(k, v)?;
144 }
145
146 Ok(py_dict)
147 }
148
149 #[staticmethod]
151 #[pyo3(name = "from_dict")]
152 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
153 from_dict_pyo3(py, values)
154 }
155
156 #[pyo3(name = "to_dict")]
158 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
159 to_dict_pyo3(py, self)
160 }
161
162 #[pyo3(name = "to_json_bytes")]
164 fn py_to_json_bytes(&self, py: Python<'_>) -> Py<PyAny> {
165 self.to_json_bytes().unwrap().into_py_any_unwrap(py)
166 }
167
168 #[pyo3(name = "to_msgpack_bytes")]
170 fn py_to_msgpack_bytes(&self, py: Python<'_>) -> Py<PyAny> {
171 self.to_msgpack_bytes().unwrap().into_py_any_unwrap(py)
172 }
173}
174
175#[pymethods]
176impl InstrumentClose {
177 #[staticmethod]
178 #[pyo3(name = "from_json")]
179 fn py_from_json(data: &[u8]) -> PyResult<Self> {
180 Self::from_json_bytes(data).map_err(to_pyvalue_err)
181 }
182
183 #[staticmethod]
184 #[pyo3(name = "from_msgpack")]
185 fn py_from_msgpack(data: &[u8]) -> PyResult<Self> {
186 Self::from_msgpack_bytes(data).map_err(to_pyvalue_err)
187 }
188}
189
190impl InstrumentClose {
191 pub fn from_pyobject(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
197 let instrument_id = obj.getattr("instrument_id")?.extract::<InstrumentId>()?;
198 let close_price = obj.getattr("close_price")?.extract::<Price>()?;
199 let close_type = obj
200 .getattr("close_type")?
201 .extract::<InstrumentCloseType>()?;
202 let ts_event = obj.getattr("ts_event")?.extract::<u64>()?;
203 let ts_init = obj.getattr("ts_init")?.extract::<u64>()?;
204
205 Ok(Self {
206 instrument_id,
207 close_price,
208 close_type,
209 ts_event: ts_event.into(),
210 ts_init: ts_init.into(),
211 })
212 }
213}
214
215pub fn pyobjects_to_instrument_closes(
221 data: Vec<Bound<'_, PyAny>>,
222) -> PyResult<Vec<InstrumentClose>> {
223 let closes = data
224 .into_iter()
225 .map(|obj| InstrumentClose::from_pyobject(&obj))
226 .collect::<PyResult<Vec<InstrumentClose>>>()?;
227
228 if !crate::data::is_monotonically_increasing_by_init(&closes) {
230 return Err(to_pyvalue_err(ERROR_MONOTONICITY));
231 }
232
233 Ok(closes)
234}