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 pub fn py_close_price(&self) -> Price {
96 self.close_price
97 }
98
99 #[getter]
100 #[pyo3(name = "close_type")]
101 pub fn py_close_type(&self) -> InstrumentCloseType {
102 self.close_type
103 }
104
105 #[getter]
106 #[pyo3(name = "ts_event")]
107 pub fn py_ts_event(&self) -> u64 {
108 self.ts_event.as_u64()
109 }
110
111 #[getter]
112 #[pyo3(name = "ts_init")]
113 pub fn py_ts_init(&self) -> u64 {
114 self.ts_init.as_u64()
115 }
116
117 #[staticmethod]
118 #[pyo3(name = "fully_qualified_name")]
119 fn py_fully_qualified_name() -> String {
120 format!("{}:{}", PY_MODULE_MODEL, stringify!(InstrumentClose))
121 }
122
123 #[staticmethod]
125 #[pyo3(name = "get_metadata")]
126 fn py_get_metadata(
127 instrument_id: &InstrumentId,
128 price_precision: u8,
129 ) -> HashMap<String, String> {
130 Self::get_metadata(instrument_id, price_precision)
131 }
132
133 #[staticmethod]
135 #[pyo3(name = "get_fields")]
136 fn py_get_fields(py: Python<'_>) -> PyResult<Bound<'_, PyDict>> {
137 let py_dict = PyDict::new(py);
138 for (k, v) in Self::get_fields() {
139 py_dict.set_item(k, v)?;
140 }
141
142 Ok(py_dict)
143 }
144
145 #[staticmethod]
147 #[pyo3(name = "from_dict")]
148 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
149 from_dict_pyo3(py, values)
150 }
151
152 #[pyo3(name = "to_dict")]
154 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
155 to_dict_pyo3(py, self)
156 }
157
158 #[pyo3(name = "to_json_bytes")]
160 fn py_to_json_bytes(&self, py: Python<'_>) -> Py<PyAny> {
161 self.to_json_bytes().unwrap().into_py_any_unwrap(py)
162 }
163
164 #[pyo3(name = "to_msgpack_bytes")]
166 fn py_to_msgpack_bytes(&self, py: Python<'_>) -> Py<PyAny> {
167 self.to_msgpack_bytes().unwrap().into_py_any_unwrap(py)
168 }
169}
170
171#[pymethods]
172impl InstrumentClose {
173 #[staticmethod]
174 #[pyo3(name = "from_json")]
175 fn py_from_json(data: &[u8]) -> PyResult<Self> {
176 Self::from_json_bytes(data).map_err(to_pyvalue_err)
177 }
178
179 #[staticmethod]
180 #[pyo3(name = "from_msgpack")]
181 fn py_from_msgpack(data: &[u8]) -> PyResult<Self> {
182 Self::from_msgpack_bytes(data).map_err(to_pyvalue_err)
183 }
184}
185
186impl InstrumentClose {
187 pub fn from_pyobject(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
193 let instrument_id = obj.getattr("instrument_id")?.extract::<InstrumentId>()?;
194 let close_price = obj.getattr("close_price")?.extract::<Price>()?;
195 let close_type = obj
196 .getattr("close_type")?
197 .extract::<InstrumentCloseType>()?;
198 let ts_event = obj.getattr("ts_event")?.extract::<u64>()?;
199 let ts_init = obj.getattr("ts_init")?.extract::<u64>()?;
200
201 Ok(Self {
202 instrument_id,
203 close_price,
204 close_type,
205 ts_event: ts_event.into(),
206 ts_init: ts_init.into(),
207 })
208 }
209}
210
211pub fn pyobjects_to_instrument_closes(
217 data: Vec<Bound<'_, PyAny>>,
218) -> PyResult<Vec<InstrumentClose>> {
219 let closes = data
220 .into_iter()
221 .map(|obj| InstrumentClose::from_pyobject(&obj))
222 .collect::<PyResult<Vec<InstrumentClose>>>()?;
223
224 if !crate::data::is_monotonically_increasing_by_init(&closes) {
226 return Err(to_pyvalue_err(ERROR_MONOTONICITY));
227 }
228
229 Ok(closes)
230}