use std::{
collections::{HashMap, hash_map::DefaultHasher},
hash::{Hash, Hasher},
};
use nautilus_core::{
python::{
IntoPyObjectNautilusExt,
serialization::{from_dict_pyo3, to_dict_pyo3},
to_pyvalue_err,
},
serialization::{
Serializable,
msgpack::{FromMsgPack, ToMsgPack},
},
};
use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
use super::ERROR_MONOTONICITY;
use crate::{
data::close::InstrumentClose, enums::InstrumentCloseType, identifiers::InstrumentId,
python::common::PY_MODULE_MODEL, types::Price,
};
#[pymethods]
#[pyo3_stub_gen::derive::gen_stub_pymethods]
impl InstrumentClose {
#[new]
#[pyo3(signature = (instrument_id, close_price, close_type, ts_event, ts_init))]
fn py_new(
instrument_id: InstrumentId,
close_price: Price,
close_type: InstrumentCloseType,
ts_event: u64,
ts_init: u64,
) -> Self {
Self {
instrument_id,
close_price,
close_type,
ts_event: ts_event.into(),
ts_init: ts_init.into(),
}
}
fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
match op {
CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
_ => py.NotImplemented(),
}
}
fn __hash__(&self) -> isize {
let mut h = DefaultHasher::new();
self.hash(&mut h);
h.finish() as isize
}
fn __repr__(&self) -> String {
format!("{}({})", stringify!(InstrumentStatus), self)
}
fn __str__(&self) -> String {
self.to_string()
}
#[getter]
#[pyo3(name = "instrument_id")]
fn py_instrument_id(&self) -> InstrumentId {
self.instrument_id
}
#[getter]
#[pyo3(name = "close_price")]
pub fn py_close_price(&self) -> Price {
self.close_price
}
#[getter]
#[pyo3(name = "close_type")]
pub fn py_close_type(&self) -> InstrumentCloseType {
self.close_type
}
#[getter]
#[pyo3(name = "ts_event")]
pub fn py_ts_event(&self) -> u64 {
self.ts_event.as_u64()
}
#[getter]
#[pyo3(name = "ts_init")]
pub fn py_ts_init(&self) -> u64 {
self.ts_init.as_u64()
}
#[staticmethod]
#[pyo3(name = "fully_qualified_name")]
fn py_fully_qualified_name() -> String {
format!("{}:{}", PY_MODULE_MODEL, stringify!(InstrumentClose))
}
#[staticmethod]
#[pyo3(name = "get_metadata")]
fn py_get_metadata(
instrument_id: &InstrumentId,
price_precision: u8,
) -> HashMap<String, String> {
Self::get_metadata(instrument_id, price_precision)
}
#[staticmethod]
#[pyo3(name = "get_fields")]
fn py_get_fields(py: Python<'_>) -> PyResult<Bound<'_, PyDict>> {
let py_dict = PyDict::new(py);
for (k, v) in Self::get_fields() {
py_dict.set_item(k, v)?;
}
Ok(py_dict)
}
#[staticmethod]
#[pyo3(name = "from_dict")]
fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
from_dict_pyo3(py, values)
}
#[pyo3(name = "to_dict")]
fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
to_dict_pyo3(py, self)
}
#[pyo3(name = "to_json_bytes")]
fn py_to_json_bytes(&self, py: Python<'_>) -> Py<PyAny> {
self.to_json_bytes().unwrap().into_py_any_unwrap(py)
}
#[pyo3(name = "to_msgpack_bytes")]
fn py_to_msgpack_bytes(&self, py: Python<'_>) -> Py<PyAny> {
self.to_msgpack_bytes().unwrap().into_py_any_unwrap(py)
}
}
#[pymethods]
impl InstrumentClose {
#[staticmethod]
#[pyo3(name = "from_json")]
fn py_from_json(data: &[u8]) -> PyResult<Self> {
Self::from_json_bytes(data).map_err(to_pyvalue_err)
}
#[staticmethod]
#[pyo3(name = "from_msgpack")]
fn py_from_msgpack(data: &[u8]) -> PyResult<Self> {
Self::from_msgpack_bytes(data).map_err(to_pyvalue_err)
}
}
impl InstrumentClose {
pub fn from_pyobject(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
let instrument_id = obj.getattr("instrument_id")?.extract::<InstrumentId>()?;
let close_price = obj.getattr("close_price")?.extract::<Price>()?;
let close_type = obj
.getattr("close_type")?
.extract::<InstrumentCloseType>()?;
let ts_event = obj.getattr("ts_event")?.extract::<u64>()?;
let ts_init = obj.getattr("ts_init")?.extract::<u64>()?;
Ok(Self {
instrument_id,
close_price,
close_type,
ts_event: ts_event.into(),
ts_init: ts_init.into(),
})
}
}
pub fn pyobjects_to_instrument_closes(
data: Vec<Bound<'_, PyAny>>,
) -> PyResult<Vec<InstrumentClose>> {
let closes = data
.into_iter()
.map(|obj| InstrumentClose::from_pyobject(&obj))
.collect::<PyResult<Vec<InstrumentClose>>>()?;
if !crate::data::is_monotonically_increasing_by_init(&closes) {
return Err(to_pyvalue_err(ERROR_MONOTONICITY));
}
Ok(closes)
}