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