1use nautilus_core::{
17 UUID4,
18 python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3},
19};
20use nautilus_model::{
21 enums::{
22 ContingencyType, OrderSide, OrderStatus, OrderType, TimeInForce, TrailingOffsetType,
23 TriggerType,
24 },
25 identifiers::{AccountId, ClientOrderId, InstrumentId, OrderListId, PositionId, VenueOrderId},
26 types::{Price, Quantity},
27};
28use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
29use rust_decimal::Decimal;
30
31use crate::reports::order::OrderStatusReport;
32
33#[pymethods]
34impl OrderStatusReport {
35 #[new]
36 #[allow(clippy::too_many_arguments)]
37 #[pyo3(signature = (
38 account_id,
39 instrument_id,
40 venue_order_id,
41 order_side,
42 order_type,
43 time_in_force,
44 order_status,
45 quantity,
46 filled_qty,
47 ts_accepted,
48 ts_last,
49 ts_init,
50 client_order_id=None,
51 report_id=None,
52 order_list_id=None,
53 venue_position_id=None,
54 contingency_type=None,
55 expire_time=None,
56 price=None,
57 trigger_price=None,
58 trigger_type=None,
59 limit_offset=None,
60 trailing_offset=None,
61 trailing_offset_type=None,
62 avg_px=None,
63 display_qty=None,
64 post_only=false,
65 reduce_only=false,
66 cancel_reason=None,
67 ts_triggered=None,
68 ))]
69 fn py_new(
70 account_id: AccountId,
71 instrument_id: InstrumentId,
72 venue_order_id: VenueOrderId,
73 order_side: OrderSide,
74 order_type: OrderType,
75 time_in_force: TimeInForce,
76 order_status: OrderStatus,
77 quantity: Quantity,
78 filled_qty: Quantity,
79 ts_accepted: u64,
80 ts_last: u64,
81 ts_init: u64,
82 client_order_id: Option<ClientOrderId>,
83 report_id: Option<UUID4>,
84 order_list_id: Option<OrderListId>,
85 venue_position_id: Option<PositionId>,
86 contingency_type: Option<ContingencyType>,
87 expire_time: Option<u64>,
88 price: Option<Price>,
89 trigger_price: Option<Price>,
90 trigger_type: Option<TriggerType>,
91 limit_offset: Option<Decimal>,
92 trailing_offset: Option<Decimal>,
93 trailing_offset_type: Option<TrailingOffsetType>,
94 avg_px: Option<f64>,
95 display_qty: Option<Quantity>,
96 post_only: bool,
97 reduce_only: bool,
98 cancel_reason: Option<String>,
99 ts_triggered: Option<u64>,
100 ) -> PyResult<Self> {
101 let mut report = Self::new(
102 account_id,
103 instrument_id,
104 client_order_id,
105 venue_order_id,
106 order_side,
107 order_type,
108 time_in_force,
109 order_status,
110 quantity,
111 filled_qty,
112 ts_accepted.into(),
113 ts_last.into(),
114 ts_init.into(),
115 report_id,
116 );
117
118 if let Some(order_list_id) = order_list_id {
119 report = report.with_order_list_id(order_list_id);
120 }
121 if let Some(venue_position_id) = venue_position_id {
122 report = report.with_venue_position_id(venue_position_id);
123 }
124 if let Some(contingency_type) = contingency_type {
125 report = report.with_contingency_type(contingency_type);
126 }
127 if let Some(expire_time) = expire_time {
128 report = report.with_expire_time(expire_time.into());
129 }
130 if let Some(price) = price {
131 report = report.with_price(price);
132 }
133 if let Some(trigger_price) = trigger_price {
134 report = report.with_trigger_price(trigger_price);
135 }
136 if let Some(trigger_type) = trigger_type {
137 report = report.with_trigger_type(trigger_type);
138 }
139 if let Some(limit_offset) = limit_offset {
140 report = report.with_limit_offset(limit_offset);
141 }
142 if let Some(trailing_offset) = trailing_offset {
143 report = report.with_trailing_offset(trailing_offset);
144 }
145 if let Some(trailing_offset_type) = trailing_offset_type {
146 report = report.with_trailing_offset_type(trailing_offset_type);
147 }
148 if let Some(avg_px) = avg_px {
149 report = report.with_avg_px(avg_px);
150 }
151 if let Some(display_qty) = display_qty {
152 report = report.with_display_qty(display_qty);
153 }
154 if post_only {
155 report = report.with_post_only(post_only);
156 }
157 if reduce_only {
158 report = report.with_reduce_only(reduce_only);
159 }
160 if let Some(cancel_reason) = cancel_reason {
161 report = report.with_cancel_reason(cancel_reason);
162 }
163 if let Some(ts_triggered) = ts_triggered {
164 report = report.with_ts_triggered(ts_triggered.into());
165 }
166
167 Ok(report)
168 }
169
170 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
171 match op {
172 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
173 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
174 _ => py.NotImplemented(),
175 }
176 }
177
178 fn __repr__(&self) -> String {
179 self.to_string()
180 }
181
182 fn __str__(&self) -> String {
183 self.to_string()
184 }
185
186 #[getter]
187 #[pyo3(name = "account_id")]
188 const fn py_account_id(&self) -> AccountId {
189 self.account_id
190 }
191
192 #[getter]
193 #[pyo3(name = "instrument_id")]
194 const fn py_instrument_id(&self) -> InstrumentId {
195 self.instrument_id
196 }
197
198 #[getter]
199 #[pyo3(name = "venue_order_id")]
200 const fn py_venue_order_id(&self) -> VenueOrderId {
201 self.venue_order_id
202 }
203
204 #[getter]
205 #[pyo3(name = "order_side")]
206 const fn py_order_side(&self) -> OrderSide {
207 self.order_side
208 }
209
210 #[getter]
211 #[pyo3(name = "order_type")]
212 const fn py_order_type(&self) -> OrderType {
213 self.order_type
214 }
215
216 #[getter]
217 #[pyo3(name = "time_in_force")]
218 const fn py_time_in_force(&self) -> TimeInForce {
219 self.time_in_force
220 }
221
222 #[getter]
223 #[pyo3(name = "order_status")]
224 const fn py_order_status(&self) -> OrderStatus {
225 self.order_status
226 }
227
228 #[getter]
229 #[pyo3(name = "quantity")]
230 const fn py_quantity(&self) -> Quantity {
231 self.quantity
232 }
233
234 #[getter]
235 #[pyo3(name = "filled_qty")]
236 const fn py_filled_qty(&self) -> Quantity {
237 self.filled_qty
238 }
239
240 #[getter]
241 #[pyo3(name = "report_id")]
242 const fn py_report_id(&self) -> UUID4 {
243 self.report_id
244 }
245
246 #[getter]
247 #[pyo3(name = "ts_accepted")]
248 const fn py_ts_accepted(&self) -> u64 {
249 self.ts_accepted.as_u64()
250 }
251
252 #[getter]
253 #[pyo3(name = "ts_last")]
254 const fn py_ts_last(&self) -> u64 {
255 self.ts_last.as_u64()
256 }
257
258 #[getter]
259 #[pyo3(name = "ts_init")]
260 const fn py_ts_init(&self) -> u64 {
261 self.ts_init.as_u64()
262 }
263
264 #[getter]
265 #[pyo3(name = "client_order_id")]
266 const fn py_client_order_id(&self) -> Option<ClientOrderId> {
267 self.client_order_id
268 }
269
270 #[getter]
271 #[pyo3(name = "order_list_id")]
272 const fn py_order_list_id(&self) -> Option<OrderListId> {
273 self.order_list_id
274 }
275
276 #[getter]
277 #[pyo3(name = "venue_position_id")]
278 const fn py_venue_position_id(&self) -> Option<PositionId> {
279 self.venue_position_id
280 }
281
282 #[getter]
283 #[pyo3(name = "contingency_type")]
284 const fn py_contingency_type(&self) -> ContingencyType {
285 self.contingency_type
286 }
287
288 #[getter]
289 #[pyo3(name = "expire_time")]
290 fn py_expire_time(&self) -> Option<u64> {
291 self.expire_time.map(|t| t.as_u64())
292 }
293
294 #[getter]
295 #[pyo3(name = "price")]
296 const fn py_price(&self) -> Option<Price> {
297 self.price
298 }
299
300 #[getter]
301 #[pyo3(name = "trigger_price")]
302 const fn py_trigger_price(&self) -> Option<Price> {
303 self.trigger_price
304 }
305
306 #[getter]
307 #[pyo3(name = "trigger_type")]
308 const fn py_trigger_type(&self) -> Option<TriggerType> {
309 self.trigger_type
310 }
311
312 #[getter]
313 #[pyo3(name = "limit_offset")]
314 const fn py_limit_offset(&self) -> Option<Decimal> {
315 self.limit_offset
316 }
317
318 #[getter]
319 #[pyo3(name = "trailing_offset")]
320 const fn py_trailing_offset(&self) -> Option<Decimal> {
321 self.trailing_offset
322 }
323
324 #[getter]
325 #[pyo3(name = "trailing_offset_type")]
326 const fn py_trailing_offset_type(&self) -> TrailingOffsetType {
327 self.trailing_offset_type
328 }
329
330 #[getter]
331 #[pyo3(name = "avg_px")]
332 const fn py_avg_px(&self) -> Option<f64> {
333 self.avg_px
334 }
335
336 #[getter]
337 #[pyo3(name = "display_qty")]
338 const fn py_display_qty(&self) -> Option<Quantity> {
339 self.display_qty
340 }
341
342 #[getter]
343 #[pyo3(name = "post_only")]
344 const fn py_post_only(&self) -> bool {
345 self.post_only
346 }
347
348 #[getter]
349 #[pyo3(name = "reduce_only")]
350 const fn py_reduce_only(&self) -> bool {
351 self.reduce_only
352 }
353
354 #[getter]
355 #[pyo3(name = "cancel_reason")]
356 fn py_cancel_reason(&self) -> Option<String> {
357 self.cancel_reason.clone()
358 }
359
360 #[getter]
361 #[pyo3(name = "ts_triggered")]
362 fn py_ts_triggered(&self) -> Option<u64> {
363 self.ts_triggered.map(|t| t.as_u64())
364 }
365
366 #[staticmethod]
367 #[pyo3(name = "from_dict")]
368 pub fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
369 from_dict_pyo3(py, values)
370 }
371
372 #[pyo3(name = "to_dict")]
373 pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
374 let dict = PyDict::new(py);
375 dict.set_item("type", stringify!(OrderStatusReport))?;
376 dict.set_item("account_id", self.account_id.to_string())?;
377 dict.set_item("instrument_id", self.instrument_id.to_string())?;
378 dict.set_item("venue_order_id", self.venue_order_id.to_string())?;
379 dict.set_item("order_side", self.order_side.to_string())?;
380 dict.set_item("order_type", self.order_type.to_string())?;
381 dict.set_item("time_in_force", self.time_in_force.to_string())?;
382 dict.set_item("order_status", self.order_status.to_string())?;
383 dict.set_item("quantity", self.quantity.to_string())?;
384 dict.set_item("filled_qty", self.filled_qty.to_string())?;
385 dict.set_item("report_id", self.report_id.to_string())?;
386 dict.set_item("ts_accepted", self.ts_accepted.as_u64())?;
387 dict.set_item("ts_last", self.ts_last.as_u64())?;
388 dict.set_item("ts_init", self.ts_init.as_u64())?;
389 dict.set_item("contingency_type", self.contingency_type.to_string())?;
390 dict.set_item(
391 "trailing_offset_type",
392 self.trailing_offset_type.to_string(),
393 )?;
394 dict.set_item("post_only", self.post_only)?;
395 dict.set_item("reduce_only", self.reduce_only)?;
396
397 match &self.client_order_id {
398 Some(id) => dict.set_item("client_order_id", id.to_string())?,
399 None => dict.set_item("client_order_id", py.None())?,
400 }
401 match &self.order_list_id {
402 Some(id) => dict.set_item("order_list_id", id.to_string())?,
403 None => dict.set_item("order_list_id", py.None())?,
404 }
405 match &self.expire_time {
406 Some(t) => dict.set_item("expire_time", t.as_u64())?,
407 None => dict.set_item("expire_time", py.None())?,
408 }
409 match &self.price {
410 Some(p) => dict.set_item("price", p.to_string())?,
411 None => dict.set_item("price", py.None())?,
412 }
413 match &self.trigger_price {
414 Some(p) => dict.set_item("trigger_price", p.to_string())?,
415 None => dict.set_item("trigger_price", py.None())?,
416 }
417 match &self.trigger_type {
418 Some(t) => dict.set_item("trigger_type", t.to_string())?,
419 None => dict.set_item("trigger_type", py.None())?,
420 }
421 match &self.limit_offset {
422 Some(o) => dict.set_item("limit_offset", o.to_string())?,
423 None => dict.set_item("limit_offset", py.None())?,
424 }
425 match &self.trailing_offset {
426 Some(o) => dict.set_item("trailing_offset", o.to_string())?,
427 None => dict.set_item("trailing_offset", py.None())?,
428 }
429 match &self.avg_px {
430 Some(p) => dict.set_item("avg_px", p)?,
431 None => dict.set_item("avg_px", py.None())?,
432 }
433 match &self.display_qty {
434 Some(q) => dict.set_item("display_qty", q.to_string())?,
435 None => dict.set_item("display_qty", py.None())?,
436 }
437 match &self.cancel_reason {
438 Some(r) => dict.set_item("cancel_reason", r)?,
439 None => dict.set_item("cancel_reason", py.None())?,
440 }
441 match &self.ts_triggered {
442 Some(t) => dict.set_item("ts_triggered", t.as_u64())?,
443 None => dict.set_item("ts_triggered", py.None())?,
444 }
445
446 Ok(dict.into())
447 }
448}