1use indexmap::IndexMap;
17use nautilus_core::{
18 UUID4, UnixNanos,
19 python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3},
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#[pymethods]
40#[pyo3_stub_gen::derive::gen_stub_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, 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 trigger_price: Option<Price>,
73 trigger_type: Option<TriggerType>,
74 limit_offset: Option<Decimal>,
75 trailing_offset: Option<Decimal>,
76 trailing_offset_type: Option<TrailingOffsetType>,
77 expire_time: Option<u64>,
78 display_qty: Option<Quantity>,
79 emulation_trigger: Option<TriggerType>,
80 trigger_instrument_id: Option<InstrumentId>,
81 contingency_type: Option<ContingencyType>,
82 order_list_id: Option<OrderListId>,
83 linked_order_ids: Option<Vec<ClientOrderId>>,
84 parent_order_id: Option<ClientOrderId>,
85 exec_algorithm_id: Option<ExecAlgorithmId>,
86 exec_algorithm_params: Option<IndexMap<String, String>>,
87 exec_spawn_id: Option<ClientOrderId>,
88 tags: Option<Vec<String>>,
89 ) -> Self {
90 Self::new(
91 trader_id,
92 strategy_id,
93 instrument_id,
94 client_order_id,
95 order_side,
96 order_type,
97 quantity,
98 time_in_force,
99 post_only,
100 reduce_only,
101 quote_quantity,
102 reconciliation,
103 event_id,
104 ts_event.into(),
105 ts_init.into(),
106 price,
107 trigger_price,
108 trigger_type,
109 limit_offset,
110 trailing_offset,
111 trailing_offset_type,
112 expire_time.map(UnixNanos::from),
113 display_qty,
114 emulation_trigger,
115 trigger_instrument_id,
116 contingency_type,
117 order_list_id,
118 linked_order_ids,
119 parent_order_id,
120 exec_algorithm_id,
121 exec_algorithm_params.map(str_indexmap_to_ustr),
122 exec_spawn_id,
123 tags.map(|vec| vec.iter().map(|s| Ustr::from(s)).collect()),
124 )
125 }
126
127 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
128 match op {
129 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
130 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
131 _ => py.NotImplemented(),
132 }
133 }
134
135 fn __repr__(&self) -> String {
136 format!("{self:?}")
137 }
138
139 fn __str__(&self) -> String {
140 self.to_string()
141 }
142
143 #[getter]
144 #[pyo3(name = "order_type")]
145 fn py_order_type(&self) -> OrderType {
146 self.order_type
147 }
148
149 #[staticmethod]
150 #[pyo3(name = "from_dict")]
151 fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
152 from_dict_pyo3(py, values)
153 }
154
155 #[pyo3(name = "to_dict")]
156 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
157 let dict = PyDict::new(py);
158 dict.set_item("type", stringify!(OrderInitialized))?;
159 dict.set_item("trader_id", self.trader_id.to_string())?;
160 dict.set_item("strategy_id", self.strategy_id.to_string())?;
161 dict.set_item("instrument_id", self.instrument_id.to_string())?;
162 dict.set_item("client_order_id", self.client_order_id.to_string())?;
163 dict.set_item("order_side", self.order_side.to_string())?;
164 dict.set_item("order_type", self.order_type.to_string())?;
165 dict.set_item("quantity", self.quantity.to_string())?;
166 dict.set_item("time_in_force", self.time_in_force.to_string())?;
167 dict.set_item("post_only", self.post_only)?;
168 dict.set_item("reduce_only", self.reduce_only)?;
169 dict.set_item("quote_quantity", self.quote_quantity)?;
170 dict.set_item("reconciliation", self.reconciliation)?;
171 let options = PyDict::new(py);
173
174 if self.order_type == OrderType::StopMarket {
175 options.set_item("trigger_type", self.trigger_type.map(|x| x.to_string()))?;
176 options.set_item("trigger_price", self.trigger_price.map(|x| x.to_string()))?;
177 options.set_item("expire_time_ns", self.expire_time.map(|x| x.to_string()))?;
178 }
179 dict.set_item("options", options)?;
180 dict.set_item("event_id", self.event_id.to_string())?;
181 dict.set_item("ts_event", self.ts_event.as_u64())?;
182 dict.set_item("ts_init", self.ts_init.as_u64())?;
183 match self.price {
184 Some(price) => dict.set_item("price", price.to_string())?,
185 None => dict.set_item("price", py.None())?,
186 }
187
188 match self.trigger_price {
189 Some(trigger_price) => dict.set_item("trigger_price", trigger_price.to_string())?,
190 None => dict.set_item("trigger_price", py.None())?,
191 }
192
193 match self.trigger_type {
194 Some(trigger_type) => dict.set_item("trigger_type", trigger_type.to_string())?,
195 None => dict.set_item("trigger_type", py.None())?,
196 }
197
198 match self.limit_offset {
199 Some(limit_offset) => dict.set_item("limit_offset", limit_offset.to_string())?,
200 None => dict.set_item("limit_offset", py.None())?,
201 }
202
203 match self.trailing_offset {
204 Some(trailing_offset) => {
205 dict.set_item("trailing_offset", trailing_offset.to_string())?;
206 }
207 None => dict.set_item("trailing_offset", py.None())?,
208 }
209
210 match self.trailing_offset_type {
211 Some(trailing_offset_type) => {
212 dict.set_item("trailing_offset_type", trailing_offset_type.to_string())?;
213 }
214 None => dict.set_item("trailing_offset_type", py.None())?,
215 }
216
217 match self.expire_time {
218 Some(expire_time) => dict.set_item("expire_time", expire_time.as_u64())?,
219 None => dict.set_item("expire_time", py.None())?,
220 }
221
222 match self.display_qty {
223 Some(display_qty) => dict.set_item("display_qty", display_qty.to_string())?,
224 None => dict.set_item("display_qty", py.None())?,
225 }
226
227 match self.emulation_trigger {
228 Some(emulation_trigger) => {
229 dict.set_item("emulation_trigger", emulation_trigger.to_string())?;
230 }
231 None => dict.set_item("emulation_trigger", py.None())?,
232 }
233
234 match self.trigger_instrument_id {
235 Some(trigger_instrument_id) => {
236 dict.set_item("trigger_instrument_id", trigger_instrument_id.to_string())?;
237 }
238 None => dict.set_item("trigger_instrument_id", py.None())?,
239 }
240
241 match self.contingency_type {
242 Some(contingency_type) => {
243 dict.set_item("contingency_type", contingency_type.to_string())?;
244 }
245 None => dict.set_item("contingency_type", py.None())?,
246 }
247
248 match self.order_list_id {
249 Some(order_list_id) => dict.set_item("order_list_id", order_list_id.to_string())?,
250 None => dict.set_item("order_list_id", py.None())?,
251 }
252
253 match &self.linked_order_ids {
254 Some(linked_order_ids) => {
255 let py_linked_order_ids = PyList::empty(py);
256 for linked_order_id in linked_order_ids {
257 py_linked_order_ids.append(linked_order_id.to_string())?;
258 }
259 dict.set_item("linked_order_ids", py_linked_order_ids)?;
260 }
261 None => dict.set_item("linked_order_ids", py.None())?,
262 }
263
264 match self.parent_order_id {
265 Some(parent_order_id) => {
266 dict.set_item("parent_order_id", parent_order_id.to_string())?;
267 }
268 None => dict.set_item("parent_order_id", py.None())?,
269 }
270
271 match self.exec_algorithm_id {
272 Some(exec_algorithm_id) => {
273 dict.set_item("exec_algorithm_id", exec_algorithm_id.to_string())?;
274 }
275 None => dict.set_item("exec_algorithm_id", py.None())?,
276 }
277
278 match &self.exec_algorithm_params {
279 Some(exec_algorithm_params) => {
280 let py_exec_algorithm_params = PyDict::new(py);
281 for (key, value) in exec_algorithm_params {
282 py_exec_algorithm_params.set_item(key.to_string(), value.to_string())?;
283 }
284 dict.set_item("exec_algorithm_params", py_exec_algorithm_params)?;
285 }
286 None => dict.set_item("exec_algorithm_params", py.None())?,
287 }
288
289 match self.exec_spawn_id {
290 Some(exec_spawn_id) => dict.set_item("exec_spawn_id", exec_spawn_id.to_string())?,
291 None => dict.set_item("exec_spawn_id", py.None())?,
292 }
293
294 match &self.tags {
295 Some(tags) => dict.set_item(
296 "tags",
297 tags.iter().map(|x| x.to_string()).collect::<Vec<String>>(),
298 )?,
299 None => dict.set_item("tags", py.None())?,
300 }
301 Ok(dict.into())
302 }
303}