1use std::fmt::{Debug, Display};
17
18use indexmap::IndexMap;
19use nautilus_core::{UUID4, UnixNanos};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24use crate::{
25 enums::{
26 ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType,
27 TriggerType,
28 },
29 events::OrderEvent,
30 identifiers::{
31 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
32 StrategyId, TradeId, TraderId, VenueOrderId,
33 },
34 orders::OrderAny,
35 types::{Currency, Money, Price, Quantity},
36};
37
38#[repr(C)]
45#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(tag = "type")]
47#[cfg_attr(
48 feature = "python",
49 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
50)]
51#[cfg_attr(
52 feature = "python",
53 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
54)]
55pub struct OrderInitialized {
56 pub trader_id: TraderId,
58 pub strategy_id: StrategyId,
60 pub instrument_id: InstrumentId,
62 pub client_order_id: ClientOrderId,
64 pub order_side: OrderSide,
66 pub order_type: OrderType,
68 pub quantity: Quantity,
70 pub time_in_force: TimeInForce,
72 pub post_only: bool,
74 pub reduce_only: bool,
76 pub quote_quantity: bool,
78 pub reconciliation: bool,
80 pub event_id: UUID4,
82 pub ts_event: UnixNanos,
84 pub ts_init: UnixNanos,
86 pub price: Option<Price>,
88 pub trigger_price: Option<Price>,
90 pub trigger_type: Option<TriggerType>,
92 pub limit_offset: Option<Decimal>,
94 pub trailing_offset: Option<Decimal>,
96 pub trailing_offset_type: Option<TrailingOffsetType>,
98 pub expire_time: Option<UnixNanos>,
100 pub display_qty: Option<Quantity>,
102 pub emulation_trigger: Option<TriggerType>,
104 pub trigger_instrument_id: Option<InstrumentId>,
106 pub contingency_type: Option<ContingencyType>,
108 pub order_list_id: Option<OrderListId>,
110 pub linked_order_ids: Option<Vec<ClientOrderId>>,
112 pub parent_order_id: Option<ClientOrderId>,
114 pub exec_algorithm_id: Option<ExecAlgorithmId>,
116 pub exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
118 pub exec_spawn_id: Option<ClientOrderId>,
120 pub tags: Option<Vec<Ustr>>,
122}
123
124impl OrderInitialized {
125 #[expect(clippy::too_many_arguments)]
127 #[expect(
128 clippy::fn_params_excessive_bools,
129 reason = "domain event constructor requires multiple boolean flags"
130 )]
131 #[must_use]
132 pub fn new(
133 trader_id: TraderId,
134 strategy_id: StrategyId,
135 instrument_id: InstrumentId,
136 client_order_id: ClientOrderId,
137 order_side: OrderSide,
138 order_type: OrderType,
139 quantity: Quantity,
140 time_in_force: TimeInForce,
141 post_only: bool,
142 reduce_only: bool,
143 quote_quantity: bool,
144 reconciliation: bool,
145 event_id: UUID4,
146 ts_event: UnixNanos,
147 ts_init: UnixNanos,
148 price: Option<Price>,
149 trigger_price: Option<Price>,
150 trigger_type: Option<TriggerType>,
151 limit_offset: Option<Decimal>,
152 trailing_offset: Option<Decimal>,
153 trailing_offset_type: Option<TrailingOffsetType>,
154 expire_time: Option<UnixNanos>,
155 display_qty: Option<Quantity>,
156 emulation_trigger: Option<TriggerType>,
157 trigger_instrument_id: Option<InstrumentId>,
158 contingency_type: Option<ContingencyType>,
159 order_list_id: Option<OrderListId>,
160 linked_order_ids: Option<Vec<ClientOrderId>>,
161 parent_order_id: Option<ClientOrderId>,
162 exec_algorithm_id: Option<ExecAlgorithmId>,
163 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
164 exec_spawn_id: Option<ClientOrderId>,
165 tags: Option<Vec<Ustr>>,
166 ) -> Self {
167 Self {
168 trader_id,
169 strategy_id,
170 instrument_id,
171 client_order_id,
172 order_side,
173 order_type,
174 quantity,
175 time_in_force,
176 post_only,
177 reduce_only,
178 quote_quantity,
179 reconciliation,
180 event_id,
181 ts_event,
182 ts_init,
183 price,
184 trigger_price,
185 trigger_type,
186 limit_offset,
187 trailing_offset,
188 trailing_offset_type,
189 expire_time,
190 display_qty,
191 emulation_trigger,
192 trigger_instrument_id,
193 contingency_type,
194 order_list_id,
195 linked_order_ids,
196 parent_order_id,
197 exec_algorithm_id,
198 exec_algorithm_params,
199 exec_spawn_id,
200 tags,
201 }
202 }
203}
204
205impl Debug for OrderInitialized {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 write!(
208 f,
209 "{}(\
210 trader_id={}, \
211 strategy_id={}, \
212 instrument_id={}, \
213 client_order_id={}, \
214 side={}, \
215 type={}, \
216 quantity={}, \
217 time_in_force={}, \
218 post_only={}, \
219 reduce_only={}, \
220 quote_quantity={}, \
221 price={}, \
222 emulation_trigger={}, \
223 trigger_instrument_id={}, \
224 contingency_type={}, \
225 order_list_id={}, \
226 linked_order_ids=[{}], \
227 parent_order_id={}, \
228 exec_algorithm_id={}, \
229 exec_algorithm_params={}, \
230 exec_spawn_id={}, \
231 tags={}, \
232 event_id={}, \
233 ts_init={})",
234 stringify!(OrderInitialized),
235 self.trader_id,
236 self.strategy_id,
237 self.instrument_id,
238 self.client_order_id,
239 self.order_side,
240 self.order_type,
241 self.quantity,
242 self.time_in_force,
243 self.post_only,
244 self.reduce_only,
245 self.quote_quantity,
246 self.price
247 .map_or("None".to_string(), |price| format!("{price}")),
248 self.emulation_trigger
249 .map_or("None".to_string(), |trigger| format!("{trigger}")),
250 self.trigger_instrument_id
251 .map_or("None".to_string(), |instrument_id| format!(
252 "{instrument_id}"
253 )),
254 self.contingency_type
255 .map_or("None".to_string(), |contingency_type| format!(
256 "{contingency_type}"
257 )),
258 self.order_list_id
259 .map_or("None".to_string(), |order_list_id| format!(
260 "{order_list_id}"
261 )),
262 self.linked_order_ids
263 .as_ref()
264 .map_or("None".to_string(), |linked_order_ids| linked_order_ids
265 .iter()
266 .map(ToString::to_string)
267 .collect::<Vec<_>>()
268 .join(", ")),
269 self.parent_order_id
270 .map_or("None".to_string(), |parent_order_id| format!(
271 "{parent_order_id}"
272 )),
273 self.exec_algorithm_id
274 .map_or("None".to_string(), |exec_algorithm_id| format!(
275 "{exec_algorithm_id}"
276 )),
277 self.exec_algorithm_params
278 .as_ref()
279 .map_or("None".to_string(), |exec_algorithm_params| format!(
280 "{exec_algorithm_params:?}"
281 )),
282 self.exec_spawn_id
283 .map_or("None".to_string(), |exec_spawn_id| format!(
284 "{exec_spawn_id}"
285 )),
286 self.tags.as_ref().map_or("None".to_string(), |tags| tags
287 .iter()
288 .map(|x| x.to_string())
289 .collect::<Vec<String>>()
290 .join(", ")),
291 self.event_id,
292 self.ts_init
293 )
294 }
295}
296
297impl Display for OrderInitialized {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 write!(
300 f,
301 "{}(\
302 instrument_id={}, \
303 client_order_id={}, \
304 side={}, \
305 type={}, \
306 quantity={}, \
307 time_in_force={}, \
308 post_only={}, \
309 reduce_only={}, \
310 quote_quantity={}, \
311 price={}, \
312 emulation_trigger={}, \
313 trigger_instrument_id={}, \
314 contingency_type={}, \
315 order_list_id={}, \
316 linked_order_ids=[{}], \
317 parent_order_id={}, \
318 exec_algorithm_id={}, \
319 exec_algorithm_params={}, \
320 exec_spawn_id={}, \
321 tags={})",
322 stringify!(OrderInitialized),
323 self.instrument_id,
324 self.client_order_id,
325 self.order_side,
326 self.order_type,
327 self.quantity,
328 self.time_in_force,
329 self.post_only,
330 self.reduce_only,
331 self.quote_quantity,
332 self.price
333 .map_or("None".to_string(), |price| format!("{price}")),
334 self.emulation_trigger
335 .map_or("None".to_string(), |trigger| format!("{trigger}")),
336 self.trigger_instrument_id
337 .map_or("None".to_string(), |instrument_id| format!(
338 "{instrument_id}"
339 )),
340 self.contingency_type
341 .map_or("None".to_string(), |contingency_type| format!(
342 "{contingency_type}"
343 )),
344 self.order_list_id
345 .map_or("None".to_string(), |order_list_id| format!(
346 "{order_list_id}"
347 )),
348 self.linked_order_ids
349 .as_ref()
350 .map_or("None".to_string(), |linked_order_ids| linked_order_ids
351 .iter()
352 .map(ToString::to_string)
353 .collect::<Vec<_>>()
354 .join(", ")),
355 self.parent_order_id
356 .map_or("None".to_string(), |parent_order_id| format!(
357 "{parent_order_id}"
358 )),
359 self.exec_algorithm_id
360 .map_or("None".to_string(), |exec_algorithm_id| format!(
361 "{exec_algorithm_id}"
362 )),
363 self.exec_algorithm_params
364 .as_ref()
365 .map_or("None".to_string(), |exec_algorithm_params| format!(
366 "{exec_algorithm_params:?}"
367 )),
368 self.exec_spawn_id
369 .map_or("None".to_string(), |exec_spawn_id| format!(
370 "{exec_spawn_id}"
371 )),
372 self.tags.as_ref().map_or("None".to_string(), |tags| tags
373 .iter()
374 .map(|s| s.to_string())
375 .collect::<Vec<String>>()
376 .join(", ")),
377 )
378 }
379}
380
381impl OrderEvent for OrderInitialized {
382 fn id(&self) -> UUID4 {
383 self.event_id
384 }
385
386 fn type_name(&self) -> &'static str {
387 stringify!(OrderInitialized)
388 }
389
390 fn order_type(&self) -> Option<OrderType> {
391 Some(self.order_type)
392 }
393
394 fn order_side(&self) -> Option<OrderSide> {
395 Some(self.order_side)
396 }
397
398 fn trader_id(&self) -> TraderId {
399 self.trader_id
400 }
401
402 fn strategy_id(&self) -> StrategyId {
403 self.strategy_id
404 }
405
406 fn instrument_id(&self) -> InstrumentId {
407 self.instrument_id
408 }
409
410 fn trade_id(&self) -> Option<TradeId> {
411 None
412 }
413
414 fn currency(&self) -> Option<Currency> {
415 None
416 }
417
418 fn client_order_id(&self) -> ClientOrderId {
419 self.client_order_id
420 }
421
422 fn reason(&self) -> Option<Ustr> {
423 None
424 }
425
426 fn quantity(&self) -> Option<Quantity> {
427 Some(self.quantity)
428 }
429
430 fn time_in_force(&self) -> Option<TimeInForce> {
431 Some(self.time_in_force)
432 }
433
434 fn liquidity_side(&self) -> Option<LiquiditySide> {
435 None
436 }
437
438 fn post_only(&self) -> Option<bool> {
439 Some(self.post_only)
440 }
441
442 fn reduce_only(&self) -> Option<bool> {
443 Some(self.reduce_only)
444 }
445
446 fn quote_quantity(&self) -> Option<bool> {
447 Some(self.quote_quantity)
448 }
449
450 fn reconciliation(&self) -> bool {
451 false
452 }
453
454 fn price(&self) -> Option<Price> {
455 self.price
456 }
457
458 fn last_px(&self) -> Option<Price> {
459 None
460 }
461
462 fn last_qty(&self) -> Option<Quantity> {
463 None
464 }
465
466 fn trigger_price(&self) -> Option<Price> {
467 self.trigger_price
468 }
469
470 fn trigger_type(&self) -> Option<TriggerType> {
471 self.trigger_type
472 }
473
474 fn limit_offset(&self) -> Option<Decimal> {
475 self.limit_offset
476 }
477
478 fn trailing_offset(&self) -> Option<Decimal> {
479 self.trailing_offset
480 }
481
482 fn trailing_offset_type(&self) -> Option<TrailingOffsetType> {
483 self.trailing_offset_type
484 }
485
486 fn expire_time(&self) -> Option<UnixNanos> {
487 self.expire_time
488 }
489
490 fn display_qty(&self) -> Option<Quantity> {
491 self.display_qty
492 }
493
494 fn emulation_trigger(&self) -> Option<TriggerType> {
495 self.emulation_trigger
496 }
497
498 fn trigger_instrument_id(&self) -> Option<InstrumentId> {
499 self.trigger_instrument_id
500 }
501
502 fn contingency_type(&self) -> Option<ContingencyType> {
503 self.contingency_type
504 }
505
506 fn order_list_id(&self) -> Option<OrderListId> {
507 self.order_list_id
508 }
509
510 fn linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
511 self.linked_order_ids.clone()
512 }
513
514 fn parent_order_id(&self) -> Option<ClientOrderId> {
515 self.parent_order_id
516 }
517
518 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
519 self.exec_algorithm_id
520 }
521
522 fn exec_spawn_id(&self) -> Option<ClientOrderId> {
523 self.exec_spawn_id
524 }
525
526 fn venue_order_id(&self) -> Option<VenueOrderId> {
527 None
528 }
529
530 fn account_id(&self) -> Option<AccountId> {
531 None
532 }
533
534 fn position_id(&self) -> Option<PositionId> {
535 None
536 }
537
538 fn commission(&self) -> Option<Money> {
539 None
540 }
541
542 fn ts_event(&self) -> UnixNanos {
543 self.ts_event
544 }
545
546 fn ts_init(&self) -> UnixNanos {
547 self.ts_init
548 }
549}
550
551impl From<OrderInitialized> for OrderAny {
552 fn from(order: OrderInitialized) -> Self {
553 match order.order_type {
554 OrderType::Limit => Self::Limit(order.into()),
555 OrderType::Market => Self::Market(order.into()),
556 OrderType::StopMarket => Self::StopMarket(order.into()),
557 OrderType::StopLimit => Self::StopLimit(order.into()),
558 OrderType::LimitIfTouched => Self::LimitIfTouched(order.into()),
559 OrderType::TrailingStopLimit => Self::TrailingStopLimit(order.into()),
560 OrderType::TrailingStopMarket => Self::TrailingStopMarket(order.into()),
561 OrderType::MarketToLimit => Self::MarketToLimit(order.into()),
562 OrderType::MarketIfTouched => Self::MarketIfTouched(order.into()),
563 }
564 }
565}
566
567#[cfg(test)]
568mod test {
569 use rstest::rstest;
570
571 use crate::events::order::{initialized::OrderInitialized, stubs::*};
572 #[rstest]
573 fn test_order_initialized(order_initialized_buy_limit: OrderInitialized) {
574 let display = format!("{order_initialized_buy_limit}");
575 assert_eq!(
576 display,
577 "OrderInitialized(instrument_id=BTCUSDT.COINBASE, client_order_id=O-19700101-000000-001-001-1, \
578 side=BUY, type=LIMIT, quantity=0.561, time_in_force=DAY, post_only=true, reduce_only=true, \
579 quote_quantity=false, price=22000, emulation_trigger=BID_ASK, trigger_instrument_id=BTCUSDT.COINBASE, \
580 contingency_type=OTO, order_list_id=1, linked_order_ids=[O-2020872378424], parent_order_id=None, \
581 exec_algorithm_id=None, exec_algorithm_params=None, exec_spawn_id=None, tags=None)"
582 );
583 }
584
585 #[rstest]
586 fn test_order_initialized_serialization() {
587 let original = OrderInitialized::default();
588 let json = serde_json::to_string(&original).unwrap();
589 let deserialized: OrderInitialized = serde_json::from_str(&json).unwrap();
590 assert_eq!(original, deserialized);
591 }
592}