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