1use indexmap::IndexMap;
17use nautilus_core::{
18 UUID4, UnixNanos,
19 python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3, to_pyvalue_err},
20};
21use pyo3::{
22 basic::CompareOp,
23 prelude::*,
24 types::{PyDict, PyList},
25};
26use rust_decimal::Decimal;
27use ustr::Ustr;
28
29use crate::{
30 enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType},
31 events::OrderInitialized,
32 identifiers::{
33 ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, StrategyId, TraderId,
34 },
35 orders::str_indexmap_to_ustr,
36 types::{Price, Quantity},
37};
38
39#[pyo3_stub_gen::derive::gen_stub_pymethods]
40#[pymethods]
41impl OrderInitialized {
42 #[expect(clippy::too_many_arguments)]
49 #[expect(
50 clippy::fn_params_excessive_bools,
51 reason = "domain event constructor requires multiple boolean flags"
52 )]
53 #[new]
54 #[pyo3(signature = (trader_id, strategy_id, instrument_id, client_order_id, order_side, order_type, quantity, time_in_force, post_only, reduce_only, quote_quantity, reconciliation, event_id, ts_event, ts_init, price=None, activation_price=None, trigger_price=None, trigger_type=None, limit_offset=None, trailing_offset=None, trailing_offset_type=None, expire_time=None, display_qty=None, emulation_trigger=None, trigger_instrument_id=None, contingency_type=None, order_list_id=None, linked_order_ids=None, parent_order_id=None, exec_algorithm_id=None, exec_algorithm_params=None, exec_spawn_id=None, tags=None))]
55 fn py_new(
56 trader_id: TraderId,
57 strategy_id: StrategyId,
58 instrument_id: InstrumentId,
59 client_order_id: ClientOrderId,
60 order_side: OrderSide,
61 order_type: OrderType,
62 quantity: Quantity,
63 time_in_force: TimeInForce,
64 post_only: bool,
65 reduce_only: bool,
66 quote_quantity: bool,
67 reconciliation: bool,
68 event_id: UUID4,
69 ts_event: u64,
70 ts_init: u64,
71 price: Option<Price>,
72 activation_price: Option<Price>,
73 trigger_price: Option<Price>,
74 trigger_type: Option<TriggerType>,
75 limit_offset: Option<Decimal>,
76 trailing_offset: Option<Decimal>,
77 trailing_offset_type: Option<TrailingOffsetType>,
78 expire_time: Option<u64>,
79 display_qty: Option<Quantity>,
80 emulation_trigger: Option<TriggerType>,
81 trigger_instrument_id: Option<InstrumentId>,
82 contingency_type: Option<ContingencyType>,
83 order_list_id: Option<OrderListId>,
84 linked_order_ids: Option<Vec<ClientOrderId>>,
85 parent_order_id: Option<ClientOrderId>,
86 exec_algorithm_id: Option<ExecAlgorithmId>,
87 exec_algorithm_params: Option<IndexMap<String, String>>,
88 exec_spawn_id: Option<ClientOrderId>,
89 tags: Option<Vec<String>>,
90 ) -> PyResult<Self> {
91 Self::new_checked(
92 trader_id,
93 strategy_id,
94 instrument_id,
95 client_order_id,
96 order_side,
97 order_type,
98 quantity,
99 time_in_force,
100 post_only,
101 reduce_only,
102 quote_quantity,
103 reconciliation,
104 event_id,
105 ts_event.into(),
106 ts_init.into(),
107 price,
108 activation_price,
109 trigger_price,
110 trigger_type,
111 limit_offset,
112 trailing_offset,
113 trailing_offset_type,
114 expire_time.map(UnixNanos::from),
115 display_qty,
116 emulation_trigger,
117 trigger_instrument_id,
118 contingency_type,
119 order_list_id,
120 linked_order_ids,
121 parent_order_id,
122 exec_algorithm_id,
123 exec_algorithm_params.map(str_indexmap_to_ustr),
124 exec_spawn_id,
125 tags.map(|vec| vec.iter().map(|s| Ustr::from(s)).collect()),
126 )
127 .map_err(to_pyvalue_err)
128 }
129
130 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
131 match op {
132 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
133 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
134 _ => py.NotImplemented(),
135 }
136 }
137
138 fn __repr__(&self) -> String {
139 format!("{self:?}")
140 }
141
142 fn __str__(&self) -> String {
143 self.to_string()
144 }
145
146 #[getter]
147 #[pyo3(name = "trader_id")]
148 fn py_trader_id(&self) -> TraderId {
149 self.trader_id
150 }
151
152 #[getter]
153 #[pyo3(name = "strategy_id")]
154 fn py_strategy_id(&self) -> StrategyId {
155 self.strategy_id
156 }
157
158 #[getter]
159 #[pyo3(name = "instrument_id")]
160 fn py_instrument_id(&self) -> InstrumentId {
161 self.instrument_id
162 }
163
164 #[getter]
165 #[pyo3(name = "client_order_id")]
166 fn py_client_order_id(&self) -> ClientOrderId {
167 self.client_order_id
168 }
169
170 #[getter]
171 #[pyo3(name = "order_side")]
172 fn py_order_side(&self) -> OrderSide {
173 self.order_side
174 }
175
176 #[getter]
177 #[pyo3(name = "order_type")]
178 fn py_order_type(&self) -> OrderType {
179 self.order_type
180 }
181
182 #[getter]
183 #[pyo3(name = "quantity")]
184 fn py_quantity(&self) -> Quantity {
185 self.quantity
186 }
187
188 #[getter]
189 #[pyo3(name = "time_in_force")]
190 fn py_time_in_force(&self) -> TimeInForce {
191 self.time_in_force
192 }
193
194 #[getter]
195 #[pyo3(name = "post_only")]
196 fn py_post_only(&self) -> bool {
197 self.post_only
198 }
199
200 #[getter]
201 #[pyo3(name = "reduce_only")]
202 fn py_reduce_only(&self) -> bool {
203 self.reduce_only
204 }
205
206 #[getter]
207 #[pyo3(name = "quote_quantity")]
208 fn py_quote_quantity(&self) -> bool {
209 self.quote_quantity
210 }
211
212 #[getter]
213 #[pyo3(name = "reconciliation")]
214 fn py_reconciliation(&self) -> bool {
215 self.reconciliation
216 }
217
218 #[getter]
219 #[pyo3(name = "event_id")]
220 fn py_event_id(&self) -> UUID4 {
221 self.event_id
222 }
223
224 #[getter]
225 #[pyo3(name = "ts_event")]
226 fn py_ts_event(&self) -> u64 {
227 self.ts_event.as_u64()
228 }
229
230 #[getter]
231 #[pyo3(name = "ts_init")]
232 fn py_ts_init(&self) -> u64 {
233 self.ts_init.as_u64()
234 }
235
236 #[getter]
237 #[pyo3(name = "price")]
238 fn py_price(&self) -> Option<Price> {
239 self.price
240 }
241
242 #[getter]
243 #[pyo3(name = "activation_price")]
244 fn py_activation_price(&self) -> Option<Price> {
245 self.activation_price
246 }
247
248 #[getter]
249 #[pyo3(name = "trigger_price")]
250 fn py_trigger_price(&self) -> Option<Price> {
251 self.trigger_price
252 }
253
254 #[getter]
255 #[pyo3(name = "trigger_type")]
256 fn py_trigger_type(&self) -> Option<TriggerType> {
257 self.trigger_type
258 }
259
260 #[getter]
261 #[pyo3(name = "limit_offset")]
262 fn py_limit_offset(&self) -> Option<Decimal> {
263 self.limit_offset
264 }
265
266 #[getter]
267 #[pyo3(name = "trailing_offset")]
268 fn py_trailing_offset(&self) -> Option<Decimal> {
269 self.trailing_offset
270 }
271
272 #[getter]
273 #[pyo3(name = "trailing_offset_type")]
274 fn py_trailing_offset_type(&self) -> Option<TrailingOffsetType> {
275 self.trailing_offset_type
276 }
277
278 #[getter]
279 #[pyo3(name = "expire_time")]
280 fn py_expire_time(&self) -> Option<u64> {
281 self.expire_time.map(|ts| ts.as_u64())
282 }
283
284 #[getter]
285 #[pyo3(name = "display_qty")]
286 fn py_display_qty(&self) -> Option<Quantity> {
287 self.display_qty
288 }
289
290 #[getter]
291 #[pyo3(name = "emulation_trigger")]
292 fn py_emulation_trigger(&self) -> Option<TriggerType> {
293 self.emulation_trigger
294 }
295
296 #[getter]
297 #[pyo3(name = "trigger_instrument_id")]
298 fn py_trigger_instrument_id(&self) -> Option<InstrumentId> {
299 self.trigger_instrument_id
300 }
301
302 #[getter]
303 #[pyo3(name = "contingency_type")]
304 fn py_contingency_type(&self) -> Option<ContingencyType> {
305 self.contingency_type
306 }
307
308 #[getter]
309 #[pyo3(name = "order_list_id")]
310 fn py_order_list_id(&self) -> Option<OrderListId> {
311 self.order_list_id
312 }
313
314 #[getter]
315 #[pyo3(name = "linked_order_ids")]
316 fn py_linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
317 self.linked_order_ids.clone()
318 }
319
320 #[getter]
321 #[pyo3(name = "parent_order_id")]
322 fn py_parent_order_id(&self) -> Option<ClientOrderId> {
323 self.parent_order_id
324 }
325
326 #[getter]
327 #[pyo3(name = "exec_algorithm_id")]
328 fn py_exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
329 self.exec_algorithm_id
330 }
331
332 #[getter]
333 #[pyo3(name = "exec_algorithm_params")]
334 fn py_exec_algorithm_params(&self) -> Option<IndexMap<String, String>> {
335 self.exec_algorithm_params.as_ref().map(|params| {
336 params
337 .iter()
338 .map(|(key, value)| (key.to_string(), value.to_string()))
339 .collect()
340 })
341 }
342
343 #[getter]
344 #[pyo3(name = "exec_spawn_id")]
345 fn py_exec_spawn_id(&self) -> Option<ClientOrderId> {
346 self.exec_spawn_id
347 }
348
349 #[getter]
350 #[pyo3(name = "tags")]
351 fn py_tags(&self) -> Option<Vec<String>> {
352 self.tags
353 .as_ref()
354 .map(|tags| tags.iter().map(ToString::to_string).collect())
355 }
356
357 #[getter]
358 #[pyo3(name = "causation_id")]
359 fn py_causation_id(&self) -> Option<UUID4> {
360 self.causation_id
361 }
362
363 #[staticmethod]
364 #[pyo3(name = "from_dict")]
365 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
366 from_dict_pyo3(py, values)
367 }
368
369 #[pyo3(name = "to_dict")]
370 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
371 let dict = PyDict::new(py);
372 dict.set_item("type", stringify!(OrderInitialized))?;
373 dict.set_item("trader_id", self.trader_id.to_string())?;
374 dict.set_item("strategy_id", self.strategy_id.to_string())?;
375 dict.set_item("instrument_id", self.instrument_id.to_string())?;
376 dict.set_item("client_order_id", self.client_order_id.to_string())?;
377 dict.set_item("order_side", self.order_side.to_string())?;
378 dict.set_item("order_type", self.order_type.to_string())?;
379 dict.set_item("quantity", self.quantity.to_string())?;
380 dict.set_item("time_in_force", self.time_in_force.to_string())?;
381 dict.set_item("post_only", self.post_only)?;
382 dict.set_item("reduce_only", self.reduce_only)?;
383 dict.set_item("quote_quantity", self.quote_quantity)?;
384 dict.set_item("reconciliation", self.reconciliation)?;
385 dict.set_item("event_id", self.event_id.to_string())?;
386 dict.set_item("ts_event", self.ts_event.as_u64())?;
387 dict.set_item("ts_init", self.ts_init.as_u64())?;
388 match self.price {
389 Some(price) => dict.set_item("price", price.to_string())?,
390 None => dict.set_item("price", py.None())?,
391 }
392
393 match self.activation_price {
394 Some(activation_price) => {
395 dict.set_item("activation_price", activation_price.to_string())?;
396 }
397 None => dict.set_item("activation_price", py.None())?,
398 }
399
400 match self.trigger_price {
401 Some(trigger_price) => dict.set_item("trigger_price", trigger_price.to_string())?,
402 None => dict.set_item("trigger_price", py.None())?,
403 }
404
405 match self.trigger_type {
406 Some(trigger_type) => dict.set_item("trigger_type", trigger_type.to_string())?,
407 None => dict.set_item("trigger_type", py.None())?,
408 }
409
410 match self.limit_offset {
411 Some(limit_offset) => dict.set_item("limit_offset", limit_offset.to_string())?,
412 None => dict.set_item("limit_offset", py.None())?,
413 }
414
415 match self.trailing_offset {
416 Some(trailing_offset) => {
417 dict.set_item("trailing_offset", trailing_offset.to_string())?;
418 }
419 None => dict.set_item("trailing_offset", py.None())?,
420 }
421
422 match self.trailing_offset_type {
423 Some(trailing_offset_type) => {
424 dict.set_item("trailing_offset_type", trailing_offset_type.to_string())?;
425 }
426 None => dict.set_item("trailing_offset_type", py.None())?,
427 }
428
429 match self.expire_time {
430 Some(expire_time) => dict.set_item("expire_time", expire_time.as_u64())?,
431 None => dict.set_item("expire_time", py.None())?,
432 }
433
434 match self.display_qty {
435 Some(display_qty) => dict.set_item("display_qty", display_qty.to_string())?,
436 None => dict.set_item("display_qty", py.None())?,
437 }
438
439 match self.emulation_trigger {
440 Some(emulation_trigger) => {
441 dict.set_item("emulation_trigger", emulation_trigger.to_string())?;
442 }
443 None => dict.set_item("emulation_trigger", py.None())?,
444 }
445
446 match self.trigger_instrument_id {
447 Some(trigger_instrument_id) => {
448 dict.set_item("trigger_instrument_id", trigger_instrument_id.to_string())?;
449 }
450 None => dict.set_item("trigger_instrument_id", py.None())?,
451 }
452
453 match self.contingency_type {
454 Some(contingency_type) => {
455 dict.set_item("contingency_type", contingency_type.to_string())?;
456 }
457 None => dict.set_item("contingency_type", py.None())?,
458 }
459
460 match self.order_list_id {
461 Some(order_list_id) => dict.set_item("order_list_id", order_list_id.to_string())?,
462 None => dict.set_item("order_list_id", py.None())?,
463 }
464
465 match &self.linked_order_ids {
466 Some(linked_order_ids) => {
467 let py_linked_order_ids = PyList::empty(py);
468 for linked_order_id in linked_order_ids {
469 py_linked_order_ids.append(linked_order_id.to_string())?;
470 }
471 dict.set_item("linked_order_ids", py_linked_order_ids)?;
472 }
473 None => dict.set_item("linked_order_ids", py.None())?,
474 }
475
476 match self.parent_order_id {
477 Some(parent_order_id) => {
478 dict.set_item("parent_order_id", parent_order_id.to_string())?;
479 }
480 None => dict.set_item("parent_order_id", py.None())?,
481 }
482
483 match self.exec_algorithm_id {
484 Some(exec_algorithm_id) => {
485 dict.set_item("exec_algorithm_id", exec_algorithm_id.to_string())?;
486 }
487 None => dict.set_item("exec_algorithm_id", py.None())?,
488 }
489
490 match &self.exec_algorithm_params {
491 Some(exec_algorithm_params) => {
492 let py_exec_algorithm_params = PyDict::new(py);
493 for (key, value) in exec_algorithm_params {
494 py_exec_algorithm_params.set_item(key.to_string(), value.to_string())?;
495 }
496 dict.set_item("exec_algorithm_params", py_exec_algorithm_params)?;
497 }
498 None => dict.set_item("exec_algorithm_params", py.None())?,
499 }
500
501 match self.exec_spawn_id {
502 Some(exec_spawn_id) => dict.set_item("exec_spawn_id", exec_spawn_id.to_string())?,
503 None => dict.set_item("exec_spawn_id", py.None())?,
504 }
505
506 match &self.tags {
507 Some(tags) => dict.set_item(
508 "tags",
509 tags.iter()
510 .map(ToString::to_string)
511 .collect::<Vec<String>>(),
512 )?,
513 None => dict.set_item("tags", py.None())?,
514 }
515
516 match self.causation_id {
517 Some(causation_id) => dict.set_item("causation_id", causation_id.to_string())?,
518 None => dict.set_item("causation_id", py.None())?,
519 }
520 Ok(dict.into())
521 }
522}