Skip to main content

nautilus_model/python/reports/
mass_status.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use indexmap::IndexMap;
17use nautilus_core::{
18    UUID4,
19    python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3},
20};
21use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
22
23use crate::{
24    identifiers::{AccountId, ClientId, InstrumentId, Venue, VenueOrderId},
25    reports::{
26        fill::FillReport, mass_status::ExecutionMassStatus, order::OrderStatusReport,
27        position::PositionStatusReport,
28    },
29};
30
31#[pymethods]
32#[pyo3_stub_gen::derive::gen_stub_pymethods]
33impl ExecutionMassStatus {
34    /// Represents an execution mass status report for an execution client - including
35    /// status of all orders, trades for those orders and open positions.
36    #[new]
37    #[pyo3(signature = (client_id, account_id, venue, ts_init, report_id=None))]
38    fn py_new(
39        client_id: ClientId,
40        account_id: AccountId,
41        venue: Venue,
42        ts_init: u64,
43        report_id: Option<UUID4>,
44    ) -> Self {
45        Self::new(client_id, account_id, venue, ts_init.into(), report_id)
46    }
47
48    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
49        match op {
50            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
51            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
52            _ => py.NotImplemented(),
53        }
54    }
55
56    fn __repr__(&self) -> String {
57        self.to_string()
58    }
59
60    fn __str__(&self) -> String {
61        self.to_string()
62    }
63
64    #[getter]
65    #[pyo3(name = "client_id")]
66    const fn py_client_id(&self) -> ClientId {
67        self.client_id
68    }
69
70    #[getter]
71    #[pyo3(name = "account_id")]
72    const fn py_account_id(&self) -> AccountId {
73        self.account_id
74    }
75
76    #[getter]
77    #[pyo3(name = "venue")]
78    const fn py_venue(&self) -> Venue {
79        self.venue
80    }
81
82    #[getter]
83    #[pyo3(name = "report_id")]
84    const fn py_report_id(&self) -> UUID4 {
85        self.report_id
86    }
87
88    #[getter]
89    #[pyo3(name = "ts_init")]
90    const fn py_ts_init(&self) -> u64 {
91        self.ts_init.as_u64()
92    }
93
94    /// Returns the lower timestamp bound applied to historical reports.
95    #[getter]
96    #[pyo3(name = "lookback_start")]
97    fn py_lookback_start(&self) -> Option<u64> {
98        self.lookback_start().map(|timestamp| timestamp.as_u64())
99    }
100
101    /// Returns whether every report source required for this mass status completed.
102    #[getter]
103    #[pyo3(name = "reports_complete")]
104    const fn py_reports_complete(&self) -> bool {
105        self.reports_complete()
106    }
107
108    /// Get a copy of the order reports map.
109    #[getter]
110    #[pyo3(name = "order_reports")]
111    fn py_order_reports(&self) -> IndexMap<VenueOrderId, OrderStatusReport> {
112        self.order_reports()
113    }
114
115    /// Get a copy of the fill reports map.
116    #[getter]
117    #[pyo3(name = "fill_reports")]
118    fn py_fill_reports(&self) -> IndexMap<VenueOrderId, Vec<FillReport>> {
119        self.fill_reports()
120    }
121
122    /// Get a copy of the position reports map.
123    #[getter]
124    #[pyo3(name = "position_reports")]
125    fn py_position_reports(&self) -> IndexMap<InstrumentId, Vec<PositionStatusReport>> {
126        self.position_reports()
127    }
128
129    /// Add order reports to the mass status.
130    #[pyo3(name = "add_order_reports")]
131    fn py_add_order_reports(&mut self, reports: Vec<OrderStatusReport>) {
132        self.add_order_reports(reports);
133    }
134
135    /// Add fill reports to the mass status.
136    #[pyo3(name = "add_fill_reports")]
137    fn py_add_fill_reports(&mut self, reports: Vec<FillReport>) {
138        self.add_fill_reports(reports);
139    }
140
141    /// Add position reports to the mass status.
142    #[pyo3(name = "add_position_reports")]
143    fn py_add_position_reports(&mut self, reports: Vec<PositionStatusReport>) {
144        self.add_position_reports(reports);
145    }
146
147    /// Creates an `ExecutionMassStatus` from a Python dictionary.
148    ///
149    /// # Errors
150    ///
151    /// Returns a Python exception if conversion from dict fails.
152    #[staticmethod]
153    #[pyo3(name = "from_dict")]
154    pub fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
155        from_dict_pyo3(py, values)
156    }
157
158    #[pyo3(name = "to_dict")]
159    fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
160        let dict = PyDict::new(py);
161        dict.set_item("type", stringify!(ExecutionMassStatus))?;
162        dict.set_item("client_id", self.client_id.to_string())?;
163        dict.set_item("account_id", self.account_id.to_string())?;
164        dict.set_item("venue", self.venue.to_string())?;
165        dict.set_item("report_id", self.report_id.to_string())?;
166        dict.set_item("ts_init", self.ts_init.as_u64())?;
167        dict.set_item(
168            "lookback_start",
169            self.lookback_start().map(|timestamp| timestamp.as_u64()),
170        )?;
171        dict.set_item("reports_complete", self.reports_complete())?;
172
173        let order_reports_dict = PyDict::new(py);
174        for (key, value) in &self.order_reports() {
175            order_reports_dict.set_item(key.to_string(), value.py_to_dict(py)?)?;
176        }
177        dict.set_item("order_reports", order_reports_dict)?;
178
179        let fill_reports_dict = PyDict::new(py);
180        for (key, value) in &self.fill_reports() {
181            let reports: PyResult<Vec<_>> = value.iter().map(|r| r.py_to_dict(py)).collect();
182            fill_reports_dict.set_item(key.to_string(), reports?)?;
183        }
184        dict.set_item("fill_reports", fill_reports_dict)?;
185
186        let position_reports_dict = PyDict::new(py);
187        for (key, value) in &self.position_reports() {
188            let reports: PyResult<Vec<_>> = value.iter().map(|r| r.py_to_dict(py)).collect();
189            position_reports_dict.set_item(key.to_string(), reports?)?;
190        }
191        dict.set_item("position_reports", position_reports_dict)?;
192
193        Ok(dict.into())
194    }
195}