1pub mod any;
19pub mod builder;
20pub mod default;
21pub mod limit;
22pub mod limit_if_touched;
23pub mod list;
24pub mod market;
25pub mod market_if_touched;
26pub mod market_to_limit;
27pub mod stop_limit;
28pub mod stop_market;
29pub mod trailing_stop_limit;
30pub mod trailing_stop_market;
31
32#[cfg(any(test, feature = "stubs"))]
33pub mod stubs;
34
35use enum_dispatch::enum_dispatch;
37use indexmap::IndexMap;
38use nautilus_core::{UUID4, UnixNanos};
39use rust_decimal::Decimal;
40use serde::{Deserialize, Serialize};
41use ustr::Ustr;
42
43pub use crate::orders::{
44 any::{LimitOrderAny, OrderAny, PassiveOrderAny, StopOrderAny},
45 builder::OrderTestBuilder,
46 limit::LimitOrder,
47 limit_if_touched::LimitIfTouchedOrder,
48 list::OrderList,
49 market::MarketOrder,
50 market_if_touched::MarketIfTouchedOrder,
51 market_to_limit::MarketToLimitOrder,
52 stop_limit::StopLimitOrder,
53 stop_market::StopMarketOrder,
54 trailing_stop_limit::TrailingStopLimitOrder,
55 trailing_stop_market::TrailingStopMarketOrder,
56};
57use crate::{
58 enums::{
59 ContingencyType, LiquiditySide, OrderSide, OrderSideSpecified, OrderStatus, OrderType,
60 PositionSide, TimeInForce, TrailingOffsetType, TriggerType,
61 },
62 events::{
63 OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied, OrderEmulated,
64 OrderEventAny, OrderExpired, OrderFilled, OrderInitialized, OrderModifyRejected,
65 OrderPendingCancel, OrderPendingUpdate, OrderRejected, OrderReleased, OrderSubmitted,
66 OrderTriggered, OrderUpdated,
67 },
68 identifiers::{
69 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
70 StrategyId, Symbol, TradeId, TraderId, Venue, VenueOrderId,
71 },
72 orderbook::OwnBookOrder,
73 types::{Currency, Money, Price, Quantity},
74};
75
76#[allow(dead_code)] const STOP_ORDER_TYPES: &[OrderType] = &[
78 OrderType::StopMarket,
79 OrderType::StopLimit,
80 OrderType::MarketIfTouched,
81 OrderType::LimitIfTouched,
82];
83
84#[allow(dead_code)] const LIMIT_ORDER_TYPES: &[OrderType] = &[
86 OrderType::Limit,
87 OrderType::StopLimit,
88 OrderType::LimitIfTouched,
89 OrderType::MarketIfTouched,
90];
91
92#[allow(dead_code)] const LOCAL_ACTIVE_ORDER_STATUS: &[OrderStatus] = &[
94 OrderStatus::Initialized,
95 OrderStatus::Emulated,
96 OrderStatus::Released,
97];
98
99#[derive(thiserror::Error, Debug)]
100pub enum OrderError {
101 #[error("Order not found: {0}")]
102 NotFound(ClientOrderId),
103 #[error("Order invariant failed: must have a side for this operation")]
104 NoOrderSide,
105 #[error("Invalid event for order type")]
106 InvalidOrderEvent,
107 #[error("Invalid order state transition")]
108 InvalidStateTransition,
109 #[error("Order was already initialized")]
110 AlreadyInitialized,
111 #[error("Order had no previous state")]
112 NoPreviousState,
113 #[error("{0}")]
114 Invariant(#[from] anyhow::Error),
115}
116
117#[must_use]
119pub fn ustr_indexmap_to_str(h: IndexMap<Ustr, Ustr>) -> IndexMap<String, String> {
120 h.into_iter()
121 .map(|(k, v)| (k.to_string(), v.to_string()))
122 .collect()
123}
124
125#[must_use]
127pub fn str_indexmap_to_ustr(h: IndexMap<String, String>) -> IndexMap<Ustr, Ustr> {
128 h.into_iter()
129 .map(|(k, v)| (Ustr::from(&k), Ustr::from(&v)))
130 .collect()
131}
132
133#[inline]
134pub(crate) fn check_display_qty(
135 display_qty: Option<Quantity>,
136 quantity: Quantity,
137) -> Result<(), OrderError> {
138 if let Some(q) = display_qty
139 && q > quantity
140 {
141 return Err(OrderError::Invariant(anyhow::anyhow!(
142 "`display_qty` may not exceed `quantity`"
143 )));
144 }
145 Ok(())
146}
147
148#[inline]
149pub(crate) fn check_time_in_force(
150 time_in_force: TimeInForce,
151 expire_time: Option<UnixNanos>,
152) -> Result<(), OrderError> {
153 if time_in_force == TimeInForce::Gtd && expire_time.unwrap_or_default() == 0 {
154 return Err(OrderError::Invariant(anyhow::anyhow!(
155 "`expire_time` is required for `GTD` order"
156 )));
157 }
158 Ok(())
159}
160
161impl OrderStatus {
162 #[rustfmt::skip]
168 pub fn transition(&mut self, event: &OrderEventAny) -> Result<Self, OrderError> {
169 let new_state = match (self, event) {
170 (Self::Initialized, OrderEventAny::Denied(_)) => Self::Denied,
171 (Self::Initialized, OrderEventAny::Emulated(_)) => Self::Emulated, (Self::Initialized, OrderEventAny::Released(_)) => Self::Released, (Self::Initialized, OrderEventAny::Submitted(_)) => Self::Submitted,
174 (Self::Initialized, OrderEventAny::Rejected(_)) => Self::Rejected, (Self::Initialized, OrderEventAny::Accepted(_)) => Self::Accepted, (Self::Initialized, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Initialized, OrderEventAny::Expired(_)) => Self::Expired, (Self::Initialized, OrderEventAny::Triggered(_)) => Self::Triggered, (Self::Emulated, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Emulated, OrderEventAny::Expired(_)) => Self::Expired, (Self::Emulated, OrderEventAny::Released(_)) => Self::Released, (Self::Released, OrderEventAny::Submitted(_)) => Self::Submitted, (Self::Released, OrderEventAny::Denied(_)) => Self::Denied, (Self::Released, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Submitted, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
186 (Self::Submitted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
187 (Self::Submitted, OrderEventAny::Rejected(_)) => Self::Rejected,
188 (Self::Submitted, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Submitted, OrderEventAny::Accepted(_)) => Self::Accepted,
190 (Self::Submitted, OrderEventAny::Filled(_)) => Self::Filled,
191 (Self::Submitted, OrderEventAny::Updated(_)) => Self::Submitted,
192 (Self::Accepted, OrderEventAny::Rejected(_)) => Self::Rejected, (Self::Accepted, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
194 (Self::Accepted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
195 (Self::Accepted, OrderEventAny::Canceled(_)) => Self::Canceled,
196 (Self::Accepted, OrderEventAny::Triggered(_)) => Self::Triggered,
197 (Self::Accepted, OrderEventAny::Expired(_)) => Self::Expired,
198 (Self::Accepted, OrderEventAny::Filled(_)) => Self::Filled,
199 (Self::Accepted, OrderEventAny::Updated(_)) => Self::Accepted, (Self::Canceled, OrderEventAny::Filled(_)) => Self::Filled, (Self::PendingUpdate, OrderEventAny::Rejected(_)) => Self::Rejected,
202 (Self::PendingUpdate, OrderEventAny::Accepted(_)) => Self::Accepted,
203 (Self::PendingUpdate, OrderEventAny::Canceled(_)) => Self::Canceled,
204 (Self::PendingUpdate, OrderEventAny::Expired(_)) => Self::Expired,
205 (Self::PendingUpdate, OrderEventAny::Triggered(_)) => Self::Triggered,
206 (Self::PendingUpdate, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate, (Self::PendingUpdate, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
208 (Self::PendingUpdate, OrderEventAny::Filled(_)) => Self::Filled,
209 (Self::PendingCancel, OrderEventAny::Rejected(_)) => Self::Rejected,
210 (Self::PendingCancel, OrderEventAny::PendingCancel(_)) => Self::PendingCancel, (Self::PendingCancel, OrderEventAny::Canceled(_)) => Self::Canceled,
212 (Self::PendingCancel, OrderEventAny::Expired(_)) => Self::Expired,
213 (Self::PendingCancel, OrderEventAny::Accepted(_)) => Self::Accepted, (Self::PendingCancel, OrderEventAny::Filled(_)) => Self::Filled,
215 (Self::Triggered, OrderEventAny::Rejected(_)) => Self::Rejected,
216 (Self::Triggered, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
217 (Self::Triggered, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
218 (Self::Triggered, OrderEventAny::Canceled(_)) => Self::Canceled,
219 (Self::Triggered, OrderEventAny::Expired(_)) => Self::Expired,
220 (Self::Triggered, OrderEventAny::Filled(_)) => Self::Filled,
221 (Self::PartiallyFilled, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
222 (Self::PartiallyFilled, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
223 (Self::PartiallyFilled, OrderEventAny::Canceled(_)) => Self::Canceled,
224 (Self::PartiallyFilled, OrderEventAny::Expired(_)) => Self::Expired,
225 (Self::PartiallyFilled, OrderEventAny::Filled(_)) => Self::Filled,
226 (Self::PartiallyFilled, OrderEventAny::Accepted(_)) => Self::Accepted,
227 _ => return Err(OrderError::InvalidStateTransition),
228 };
229 Ok(new_state)
230 }
231}
232
233#[enum_dispatch]
234pub trait Order: 'static + Send {
235 fn into_any(self) -> OrderAny;
236 fn status(&self) -> OrderStatus;
237 fn trader_id(&self) -> TraderId;
238 fn strategy_id(&self) -> StrategyId;
239 fn instrument_id(&self) -> InstrumentId;
240 fn symbol(&self) -> Symbol;
241 fn venue(&self) -> Venue;
242 fn client_order_id(&self) -> ClientOrderId;
243 fn venue_order_id(&self) -> Option<VenueOrderId>;
244 fn position_id(&self) -> Option<PositionId>;
245 fn account_id(&self) -> Option<AccountId>;
246 fn last_trade_id(&self) -> Option<TradeId>;
247 fn order_side(&self) -> OrderSide;
248 fn order_type(&self) -> OrderType;
249 fn quantity(&self) -> Quantity;
250 fn time_in_force(&self) -> TimeInForce;
251 fn expire_time(&self) -> Option<UnixNanos>;
252 fn price(&self) -> Option<Price>;
253 fn trigger_price(&self) -> Option<Price>;
254 fn activation_price(&self) -> Option<Price> {
255 None
256 }
257 fn trigger_type(&self) -> Option<TriggerType>;
258 fn liquidity_side(&self) -> Option<LiquiditySide>;
259 fn is_post_only(&self) -> bool;
260 fn is_reduce_only(&self) -> bool;
261 fn is_quote_quantity(&self) -> bool;
262 fn display_qty(&self) -> Option<Quantity>;
263 fn limit_offset(&self) -> Option<Decimal>;
264 fn trailing_offset(&self) -> Option<Decimal>;
265 fn trailing_offset_type(&self) -> Option<TrailingOffsetType>;
266 fn emulation_trigger(&self) -> Option<TriggerType>;
267 fn trigger_instrument_id(&self) -> Option<InstrumentId>;
268 fn contingency_type(&self) -> Option<ContingencyType>;
269 fn order_list_id(&self) -> Option<OrderListId>;
270 fn linked_order_ids(&self) -> Option<&[ClientOrderId]>;
271 fn parent_order_id(&self) -> Option<ClientOrderId>;
272 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId>;
273 fn exec_algorithm_params(&self) -> Option<&IndexMap<Ustr, Ustr>>;
274 fn exec_spawn_id(&self) -> Option<ClientOrderId>;
275 fn tags(&self) -> Option<&[Ustr]>;
276 fn filled_qty(&self) -> Quantity;
277 fn leaves_qty(&self) -> Quantity;
278 fn avg_px(&self) -> Option<f64>;
279 fn slippage(&self) -> Option<f64>;
280 fn init_id(&self) -> UUID4;
281 fn ts_init(&self) -> UnixNanos;
282 fn ts_submitted(&self) -> Option<UnixNanos>;
283 fn ts_accepted(&self) -> Option<UnixNanos>;
284 fn ts_closed(&self) -> Option<UnixNanos>;
285 fn ts_last(&self) -> UnixNanos;
286
287 fn order_side_specified(&self) -> OrderSideSpecified {
288 self.order_side().as_specified()
289 }
290 fn commissions(&self) -> &IndexMap<Currency, Money>;
291
292 fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError>;
298 fn update(&mut self, event: &OrderUpdated);
299
300 fn events(&self) -> Vec<&OrderEventAny>;
301
302 fn last_event(&self) -> &OrderEventAny {
303 self.events().last().unwrap()
305 }
306
307 fn event_count(&self) -> usize {
308 self.events().len()
309 }
310
311 fn venue_order_ids(&self) -> Vec<&VenueOrderId>;
312
313 fn trade_ids(&self) -> Vec<&TradeId>;
314
315 fn has_price(&self) -> bool;
316
317 fn is_buy(&self) -> bool {
318 self.order_side() == OrderSide::Buy
319 }
320
321 fn is_sell(&self) -> bool {
322 self.order_side() == OrderSide::Sell
323 }
324
325 fn is_passive(&self) -> bool {
326 self.order_type() != OrderType::Market
327 }
328
329 fn is_aggressive(&self) -> bool {
330 self.order_type() == OrderType::Market
331 }
332
333 fn is_emulated(&self) -> bool {
334 self.status() == OrderStatus::Emulated
335 }
336
337 fn is_active_local(&self) -> bool {
338 matches!(
339 self.status(),
340 OrderStatus::Initialized | OrderStatus::Emulated | OrderStatus::Released
341 )
342 }
343
344 fn is_primary(&self) -> bool {
345 self.exec_algorithm_id().is_some()
347 && self.client_order_id() == self.exec_spawn_id().unwrap()
348 }
349
350 fn is_secondary(&self) -> bool {
351 self.exec_algorithm_id().is_some()
353 && self.client_order_id() != self.exec_spawn_id().unwrap()
354 }
355
356 fn is_contingency(&self) -> bool {
357 self.contingency_type().is_some()
358 }
359
360 fn is_parent_order(&self) -> bool {
361 match self.contingency_type() {
362 Some(c) => c == ContingencyType::Oto,
363 None => false,
364 }
365 }
366
367 fn is_child_order(&self) -> bool {
368 self.parent_order_id().is_some()
369 }
370
371 fn is_open(&self) -> bool {
372 if let Some(emulation_trigger) = self.emulation_trigger()
373 && emulation_trigger != TriggerType::NoTrigger
374 {
375 return false;
376 }
377
378 matches!(
379 self.status(),
380 OrderStatus::Accepted
381 | OrderStatus::Triggered
382 | OrderStatus::PendingCancel
383 | OrderStatus::PendingUpdate
384 | OrderStatus::PartiallyFilled
385 )
386 }
387
388 fn is_canceled(&self) -> bool {
389 self.status() == OrderStatus::Canceled
390 }
391
392 fn is_closed(&self) -> bool {
393 matches!(
394 self.status(),
395 OrderStatus::Denied
396 | OrderStatus::Rejected
397 | OrderStatus::Canceled
398 | OrderStatus::Expired
399 | OrderStatus::Filled
400 )
401 }
402
403 fn is_inflight(&self) -> bool {
404 if let Some(emulation_trigger) = self.emulation_trigger()
405 && emulation_trigger != TriggerType::NoTrigger
406 {
407 return false;
408 }
409
410 matches!(
411 self.status(),
412 OrderStatus::Submitted | OrderStatus::PendingCancel | OrderStatus::PendingUpdate
413 )
414 }
415
416 fn is_pending_update(&self) -> bool {
417 self.status() == OrderStatus::PendingUpdate
418 }
419
420 fn is_pending_cancel(&self) -> bool {
421 self.status() == OrderStatus::PendingCancel
422 }
423
424 fn is_spawned(&self) -> bool {
425 self.exec_spawn_id()
426 .is_some_and(|exec_spawn_id| exec_spawn_id != self.client_order_id())
427 }
428
429 fn to_own_book_order(&self) -> OwnBookOrder {
430 OwnBookOrder::new(
431 self.trader_id(),
432 self.client_order_id(),
433 self.venue_order_id(),
434 self.order_side().as_specified(),
435 self.price().expect("`OwnBookOrder` must have a price"), self.quantity(),
437 self.order_type(),
438 self.time_in_force(),
439 self.status(),
440 self.ts_last(),
441 self.ts_submitted().unwrap_or_default(),
442 self.ts_accepted().unwrap_or_default(),
443 self.ts_init(),
444 )
445 }
446
447 fn is_triggered(&self) -> Option<bool>; fn set_position_id(&mut self, position_id: Option<PositionId>);
449 fn set_quantity(&mut self, quantity: Quantity);
450 fn set_leaves_qty(&mut self, leaves_qty: Quantity);
451 fn set_emulation_trigger(&mut self, emulation_trigger: Option<TriggerType>);
452 fn set_is_quote_quantity(&mut self, is_quote_quantity: bool);
453 fn set_liquidity_side(&mut self, liquidity_side: LiquiditySide);
454 fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool;
455 fn previous_status(&self) -> Option<OrderStatus>;
456}
457
458impl<T> From<&T> for OrderInitialized
459where
460 T: Order,
461{
462 fn from(order: &T) -> Self {
463 Self {
464 trader_id: order.trader_id(),
465 strategy_id: order.strategy_id(),
466 instrument_id: order.instrument_id(),
467 client_order_id: order.client_order_id(),
468 order_side: order.order_side(),
469 order_type: order.order_type(),
470 quantity: order.quantity(),
471 price: order.price(),
472 trigger_price: order.trigger_price(),
473 trigger_type: order.trigger_type(),
474 time_in_force: order.time_in_force(),
475 expire_time: order.expire_time(),
476 post_only: order.is_post_only(),
477 reduce_only: order.is_reduce_only(),
478 quote_quantity: order.is_quote_quantity(),
479 display_qty: order.display_qty(),
480 limit_offset: order.limit_offset(),
481 trailing_offset: order.trailing_offset(),
482 trailing_offset_type: order.trailing_offset_type(),
483 emulation_trigger: order.emulation_trigger(),
484 trigger_instrument_id: order.trigger_instrument_id(),
485 contingency_type: order.contingency_type(),
486 order_list_id: order.order_list_id(),
487 linked_order_ids: order.linked_order_ids().map(|x| x.to_vec()),
488 parent_order_id: order.parent_order_id(),
489 exec_algorithm_id: order.exec_algorithm_id(),
490 exec_algorithm_params: order.exec_algorithm_params().map(|x| x.to_owned()),
491 exec_spawn_id: order.exec_spawn_id(),
492 tags: order.tags().map(|x| x.to_vec()),
493 event_id: order.init_id(),
494 ts_event: order.ts_init(),
495 ts_init: order.ts_init(),
496 reconciliation: false,
497 }
498 }
499}
500
501#[derive(Clone, Debug, Serialize, Deserialize)]
502pub struct OrderCore {
503 pub events: Vec<OrderEventAny>,
504 pub commissions: IndexMap<Currency, Money>,
505 pub venue_order_ids: Vec<VenueOrderId>,
506 pub trade_ids: Vec<TradeId>,
507 pub previous_status: Option<OrderStatus>,
508 pub status: OrderStatus,
509 pub trader_id: TraderId,
510 pub strategy_id: StrategyId,
511 pub instrument_id: InstrumentId,
512 pub client_order_id: ClientOrderId,
513 pub venue_order_id: Option<VenueOrderId>,
514 pub position_id: Option<PositionId>,
515 pub account_id: Option<AccountId>,
516 pub last_trade_id: Option<TradeId>,
517 pub side: OrderSide,
518 pub order_type: OrderType,
519 pub quantity: Quantity,
520 pub time_in_force: TimeInForce,
521 pub liquidity_side: Option<LiquiditySide>,
522 pub is_reduce_only: bool,
523 pub is_quote_quantity: bool,
524 pub emulation_trigger: Option<TriggerType>,
525 pub contingency_type: Option<ContingencyType>,
526 pub order_list_id: Option<OrderListId>,
527 pub linked_order_ids: Option<Vec<ClientOrderId>>,
528 pub parent_order_id: Option<ClientOrderId>,
529 pub exec_algorithm_id: Option<ExecAlgorithmId>,
530 pub exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
531 pub exec_spawn_id: Option<ClientOrderId>,
532 pub tags: Option<Vec<Ustr>>,
533 pub filled_qty: Quantity,
534 pub leaves_qty: Quantity,
535 pub avg_px: Option<f64>,
536 pub slippage: Option<f64>,
537 pub init_id: UUID4,
538 pub ts_init: UnixNanos,
539 pub ts_submitted: Option<UnixNanos>,
540 pub ts_accepted: Option<UnixNanos>,
541 pub ts_closed: Option<UnixNanos>,
542 pub ts_last: UnixNanos,
543}
544
545impl OrderCore {
546 pub fn new(init: OrderInitialized) -> Self {
548 let events: Vec<OrderEventAny> = vec![OrderEventAny::Initialized(init.clone())];
549 Self {
550 events,
551 commissions: IndexMap::new(),
552 venue_order_ids: Vec::new(),
553 trade_ids: Vec::new(),
554 previous_status: None,
555 status: OrderStatus::Initialized,
556 trader_id: init.trader_id,
557 strategy_id: init.strategy_id,
558 instrument_id: init.instrument_id,
559 client_order_id: init.client_order_id,
560 venue_order_id: None,
561 position_id: None,
562 account_id: None,
563 last_trade_id: None,
564 side: init.order_side,
565 order_type: init.order_type,
566 quantity: init.quantity,
567 time_in_force: init.time_in_force,
568 liquidity_side: Some(LiquiditySide::NoLiquiditySide),
569 is_reduce_only: init.reduce_only,
570 is_quote_quantity: init.quote_quantity,
571 emulation_trigger: init.emulation_trigger.or(Some(TriggerType::NoTrigger)),
572 contingency_type: init
573 .contingency_type
574 .or(Some(ContingencyType::NoContingency)),
575 order_list_id: init.order_list_id,
576 linked_order_ids: init.linked_order_ids,
577 parent_order_id: init.parent_order_id,
578 exec_algorithm_id: init.exec_algorithm_id,
579 exec_algorithm_params: init.exec_algorithm_params,
580 exec_spawn_id: init.exec_spawn_id,
581 tags: init.tags,
582 filled_qty: Quantity::zero(init.quantity.precision),
583 leaves_qty: init.quantity,
584 avg_px: None,
585 slippage: None,
586 init_id: init.event_id,
587 ts_init: init.ts_event,
588 ts_submitted: None,
589 ts_accepted: None,
590 ts_closed: None,
591 ts_last: init.ts_event,
592 }
593 }
594
595 pub fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError> {
605 assert_eq!(self.client_order_id, event.client_order_id());
606 assert_eq!(self.strategy_id, event.strategy_id());
607
608 let new_status = self.status.transition(&event)?;
609 self.previous_status = Some(self.status);
610 self.status = new_status;
611
612 match &event {
613 OrderEventAny::Initialized(_) => return Err(OrderError::AlreadyInitialized),
614 OrderEventAny::Denied(event) => self.denied(event),
615 OrderEventAny::Emulated(event) => self.emulated(event),
616 OrderEventAny::Released(event) => self.released(event),
617 OrderEventAny::Submitted(event) => self.submitted(event),
618 OrderEventAny::Rejected(event) => self.rejected(event),
619 OrderEventAny::Accepted(event) => self.accepted(event),
620 OrderEventAny::PendingUpdate(event) => self.pending_update(event),
621 OrderEventAny::PendingCancel(event) => self.pending_cancel(event),
622 OrderEventAny::ModifyRejected(event) => self.modify_rejected(event),
623 OrderEventAny::CancelRejected(event) => self.cancel_rejected(event),
624 OrderEventAny::Updated(event) => self.updated(event),
625 OrderEventAny::Triggered(event) => self.triggered(event),
626 OrderEventAny::Canceled(event) => self.canceled(event),
627 OrderEventAny::Expired(event) => self.expired(event),
628 OrderEventAny::Filled(event) => self.filled(event),
629 }
630
631 self.ts_last = event.ts_event();
632 self.events.push(event);
633 Ok(())
634 }
635
636 fn denied(&mut self, event: &OrderDenied) {
637 self.ts_closed = Some(event.ts_event);
638 }
639
640 fn emulated(&self, _event: &OrderEmulated) {
641 }
643
644 fn released(&mut self, _event: &OrderReleased) {
645 self.emulation_trigger = None;
646 }
647
648 fn submitted(&mut self, event: &OrderSubmitted) {
649 self.account_id = Some(event.account_id);
650 self.ts_submitted = Some(event.ts_event);
651 }
652
653 fn accepted(&mut self, event: &OrderAccepted) {
654 self.venue_order_id = Some(event.venue_order_id);
655 self.ts_accepted = Some(event.ts_event);
656 }
657
658 fn rejected(&mut self, event: &OrderRejected) {
659 self.ts_closed = Some(event.ts_event);
660 }
661
662 fn pending_update(&self, _event: &OrderPendingUpdate) {
663 }
665
666 fn pending_cancel(&self, _event: &OrderPendingCancel) {
667 }
669
670 fn modify_rejected(&mut self, _event: &OrderModifyRejected) {
671 self.status = self
672 .previous_status
673 .unwrap_or_else(|| panic!("{}", OrderError::NoPreviousState));
674 }
675
676 fn cancel_rejected(&mut self, _event: &OrderCancelRejected) {
677 self.status = self
678 .previous_status
679 .unwrap_or_else(|| panic!("{}", OrderError::NoPreviousState));
680 }
681
682 fn triggered(&mut self, _event: &OrderTriggered) {}
683
684 fn canceled(&mut self, event: &OrderCanceled) {
685 self.ts_closed = Some(event.ts_event);
686 }
687
688 fn expired(&mut self, event: &OrderExpired) {
689 self.ts_closed = Some(event.ts_event);
690 }
691
692 fn updated(&mut self, event: &OrderUpdated) {
693 if let Some(venue_order_id) = &event.venue_order_id
694 && (self.venue_order_id.is_none()
695 || venue_order_id != self.venue_order_id.as_ref().unwrap())
696 {
697 self.venue_order_id = Some(*venue_order_id);
698 self.venue_order_ids.push(*venue_order_id);
699 }
700 }
701
702 fn filled(&mut self, event: &OrderFilled) {
703 if self.filled_qty + event.last_qty < self.quantity {
704 self.status = OrderStatus::PartiallyFilled;
705 } else {
706 self.status = OrderStatus::Filled;
707 self.ts_closed = Some(event.ts_event);
708 }
709
710 self.venue_order_id = Some(event.venue_order_id);
711 self.position_id = event.position_id;
712 self.trade_ids.push(event.trade_id);
713 self.last_trade_id = Some(event.trade_id);
714 self.liquidity_side = Some(event.liquidity_side);
715 self.filled_qty += event.last_qty;
716 self.leaves_qty -= event.last_qty;
717 self.ts_last = event.ts_event;
718 if self.ts_accepted.is_none() {
719 self.ts_accepted = Some(event.ts_event);
721 }
722
723 self.set_avg_px(event.last_qty, event.last_px);
724 }
725
726 fn set_avg_px(&mut self, last_qty: Quantity, last_px: Price) {
727 if self.avg_px.is_none() {
728 self.avg_px = Some(last_px.as_f64());
729 }
730
731 let filled_qty = self.filled_qty.as_f64();
732 let total_qty = filled_qty + last_qty.as_f64();
733
734 let avg_px = self
735 .avg_px
736 .unwrap()
737 .mul_add(filled_qty, last_px.as_f64() * last_qty.as_f64())
738 / total_qty;
739 self.avg_px = Some(avg_px);
740 }
741
742 pub fn set_slippage(&mut self, price: Price) {
743 self.slippage = self.avg_px.and_then(|avg_px| {
744 let current_price = price.as_f64();
745 match self.side {
746 OrderSide::Buy if avg_px > current_price => Some(avg_px - current_price),
747 OrderSide::Sell if avg_px < current_price => Some(current_price - avg_px),
748 _ => None,
749 }
750 });
751 }
752
753 #[must_use]
755 pub fn opposite_side(side: OrderSide) -> OrderSide {
756 match side {
757 OrderSide::Buy => OrderSide::Sell,
758 OrderSide::Sell => OrderSide::Buy,
759 OrderSide::NoOrderSide => OrderSide::NoOrderSide,
760 }
761 }
762
763 #[must_use]
765 pub fn closing_side(side: PositionSide) -> OrderSide {
766 match side {
767 PositionSide::Long => OrderSide::Sell,
768 PositionSide::Short => OrderSide::Buy,
769 PositionSide::Flat => OrderSide::NoOrderSide,
770 PositionSide::NoPositionSide => OrderSide::NoOrderSide,
771 }
772 }
773
774 #[must_use]
778 pub fn signed_decimal_qty(&self) -> Decimal {
779 match self.side {
780 OrderSide::Buy => self.quantity.as_decimal(),
781 OrderSide::Sell => -self.quantity.as_decimal(),
782 _ => panic!("Invalid order side"),
783 }
784 }
785
786 #[must_use]
787 pub fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool {
788 if side == PositionSide::Flat {
789 return false;
790 }
791
792 match (self.side, side) {
793 (OrderSide::Buy, PositionSide::Long) => false,
794 (OrderSide::Buy, PositionSide::Short) => self.leaves_qty <= position_qty,
795 (OrderSide::Sell, PositionSide::Short) => false,
796 (OrderSide::Sell, PositionSide::Long) => self.leaves_qty <= position_qty,
797 _ => true,
798 }
799 }
800
801 #[must_use]
802 pub fn commission(&self, currency: &Currency) -> Option<Money> {
803 self.commissions.get(currency).copied()
804 }
805
806 #[must_use]
807 pub fn commissions(&self) -> IndexMap<Currency, Money> {
808 self.commissions.clone()
809 }
810
811 #[must_use]
812 pub fn commissions_vec(&self) -> Vec<Money> {
813 self.commissions.values().cloned().collect()
814 }
815
816 #[must_use]
817 pub fn init_event(&self) -> Option<OrderEventAny> {
818 self.events.first().cloned()
819 }
820}
821
822#[cfg(test)]
826mod tests {
827 use rstest::rstest;
828 use rust_decimal_macros::dec;
829
830 use super::*;
831 use crate::{
832 enums::{OrderSide, OrderStatus, PositionSide},
833 events::order::{
834 accepted::OrderAcceptedBuilder, canceled::OrderCanceledBuilder,
835 denied::OrderDeniedBuilder, filled::OrderFilledBuilder,
836 initialized::OrderInitializedBuilder, submitted::OrderSubmittedBuilder,
837 },
838 orders::MarketOrder,
839 };
840
841 #[rstest]
852 #[case(OrderSide::Buy, OrderSide::Sell)]
853 #[case(OrderSide::Sell, OrderSide::Buy)]
854 #[case(OrderSide::NoOrderSide, OrderSide::NoOrderSide)]
855 fn test_order_opposite_side(#[case] order_side: OrderSide, #[case] expected_side: OrderSide) {
856 let result = OrderCore::opposite_side(order_side);
857 assert_eq!(result, expected_side);
858 }
859
860 #[rstest]
861 #[case(PositionSide::Long, OrderSide::Sell)]
862 #[case(PositionSide::Short, OrderSide::Buy)]
863 #[case(PositionSide::NoPositionSide, OrderSide::NoOrderSide)]
864 fn test_closing_side(#[case] position_side: PositionSide, #[case] expected_side: OrderSide) {
865 let result = OrderCore::closing_side(position_side);
866 assert_eq!(result, expected_side);
867 }
868
869 #[rstest]
870 #[case(OrderSide::Buy, dec!(10_000))]
871 #[case(OrderSide::Sell, dec!(-10_000))]
872 fn test_signed_decimal_qty(#[case] order_side: OrderSide, #[case] expected: Decimal) {
873 let order: MarketOrder = OrderInitializedBuilder::default()
874 .order_side(order_side)
875 .quantity(Quantity::from(10_000))
876 .build()
877 .unwrap()
878 .into();
879
880 let result = order.signed_decimal_qty();
881 assert_eq!(result, expected);
882 }
883
884 #[rustfmt::skip]
885 #[rstest]
886 #[case(OrderSide::Buy, Quantity::from(100), PositionSide::Long, Quantity::from(50), false)]
887 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(50), true)]
888 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(100), true)]
889 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
890 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
891 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(50), true)]
892 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(100), true)]
893 #[case(OrderSide::Sell, Quantity::from(100), PositionSide::Short, Quantity::from(50), false)]
894 fn test_would_reduce_only(
895 #[case] order_side: OrderSide,
896 #[case] order_qty: Quantity,
897 #[case] position_side: PositionSide,
898 #[case] position_qty: Quantity,
899 #[case] expected: bool,
900 ) {
901 let order: MarketOrder = OrderInitializedBuilder::default()
902 .order_side(order_side)
903 .quantity(order_qty)
904 .build()
905 .unwrap()
906 .into();
907
908 assert_eq!(
909 order.would_reduce_only(position_side, position_qty),
910 expected
911 );
912 }
913
914 #[rstest]
915 fn test_order_state_transition_denied() {
916 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
917 let denied = OrderDeniedBuilder::default().build().unwrap();
918 let event = OrderEventAny::Denied(denied);
919
920 order.apply(event.clone()).unwrap();
921
922 assert_eq!(order.status, OrderStatus::Denied);
923 assert!(order.is_closed());
924 assert!(!order.is_open());
925 assert_eq!(order.event_count(), 2);
926 assert_eq!(order.last_event(), &event);
927 }
928
929 #[rstest]
930 fn test_order_life_cycle_to_filled() {
931 let init = OrderInitializedBuilder::default().build().unwrap();
932 let submitted = OrderSubmittedBuilder::default().build().unwrap();
933 let accepted = OrderAcceptedBuilder::default().build().unwrap();
934 let filled = OrderFilledBuilder::default().build().unwrap();
935
936 let mut order: MarketOrder = init.clone().into();
937 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
938 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
939 order.apply(OrderEventAny::Filled(filled)).unwrap();
940
941 assert_eq!(order.client_order_id, init.client_order_id);
942 assert_eq!(order.status(), OrderStatus::Filled);
943 assert_eq!(order.filled_qty(), Quantity::from(100_000));
944 assert_eq!(order.leaves_qty(), Quantity::from(0));
945 assert_eq!(order.avg_px(), Some(1.0));
946 assert!(!order.is_open());
947 assert!(order.is_closed());
948 assert_eq!(order.commission(&Currency::USD()), None);
949 assert_eq!(order.commissions(), &IndexMap::new());
950 }
951
952 #[rstest]
953 fn test_order_state_transition_to_canceled() {
954 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
955 let submitted = OrderSubmittedBuilder::default().build().unwrap();
956 let canceled = OrderCanceledBuilder::default().build().unwrap();
957
958 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
959 order.apply(OrderEventAny::Canceled(canceled)).unwrap();
960
961 assert_eq!(order.status(), OrderStatus::Canceled);
962 assert!(order.is_closed());
963 assert!(!order.is_open());
964 }
965
966 #[rstest]
967 fn test_order_life_cycle_to_partially_filled() {
968 let init = OrderInitializedBuilder::default().build().unwrap();
969 let submitted = OrderSubmittedBuilder::default().build().unwrap();
970 let accepted = OrderAcceptedBuilder::default().build().unwrap();
971 let filled = OrderFilledBuilder::default()
972 .last_qty(Quantity::from(50_000))
973 .build()
974 .unwrap();
975
976 let mut order: MarketOrder = init.clone().into();
977 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
978 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
979 order.apply(OrderEventAny::Filled(filled)).unwrap();
980
981 assert_eq!(order.client_order_id, init.client_order_id);
982 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
983 assert_eq!(order.filled_qty(), Quantity::from(50_000));
984 assert_eq!(order.leaves_qty(), Quantity::from(50_000));
985 assert!(order.is_open());
986 assert!(!order.is_closed());
987 }
988
989 #[rstest]
990 fn test_order_commission_calculation() {
991 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
992 order
993 .commissions
994 .insert(Currency::USD(), Money::new(10.0, Currency::USD()));
995
996 assert_eq!(
997 order.commission(&Currency::USD()),
998 Some(Money::new(10.0, Currency::USD()))
999 );
1000 assert_eq!(
1001 order.commissions_vec(),
1002 vec![Money::new(10.0, Currency::USD())]
1003 );
1004 }
1005
1006 #[rstest]
1007 fn test_order_is_primary() {
1008 let order: MarketOrder = OrderInitializedBuilder::default()
1009 .exec_algorithm_id(Some(ExecAlgorithmId::from("ALGO-001")))
1010 .exec_spawn_id(Some(ClientOrderId::from("O-001")))
1011 .client_order_id(ClientOrderId::from("O-001"))
1012 .build()
1013 .unwrap()
1014 .into();
1015
1016 assert!(order.is_primary());
1017 assert!(!order.is_secondary());
1018 }
1019
1020 #[rstest]
1021 fn test_order_is_secondary() {
1022 let order: MarketOrder = OrderInitializedBuilder::default()
1023 .exec_algorithm_id(Some(ExecAlgorithmId::from("ALGO-001")))
1024 .exec_spawn_id(Some(ClientOrderId::from("O-002")))
1025 .client_order_id(ClientOrderId::from("O-001"))
1026 .build()
1027 .unwrap()
1028 .into();
1029
1030 assert!(!order.is_primary());
1031 assert!(order.is_secondary());
1032 }
1033
1034 #[rstest]
1035 fn test_order_is_contingency() {
1036 let order: MarketOrder = OrderInitializedBuilder::default()
1037 .contingency_type(Some(ContingencyType::Oto))
1038 .build()
1039 .unwrap()
1040 .into();
1041
1042 assert!(order.is_contingency());
1043 assert!(order.is_parent_order());
1044 assert!(!order.is_child_order());
1045 }
1046
1047 #[rstest]
1048 fn test_order_is_child_order() {
1049 let order: MarketOrder = OrderInitializedBuilder::default()
1050 .parent_order_id(Some(ClientOrderId::from("PARENT-001")))
1051 .build()
1052 .unwrap()
1053 .into();
1054
1055 assert!(order.is_child_order());
1056 assert!(!order.is_parent_order());
1057 }
1058}