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