1pub mod any;
19#[cfg(any(test, feature = "stubs"))]
20pub mod builder;
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 ahash::AHashSet;
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
44#[cfg(any(test, feature = "stubs"))]
45pub use crate::orders::builder::OrderTestBuilder;
46pub use crate::orders::{
47 any::{LimitOrderAny, OrderAny, PassiveOrderAny, StopOrderAny},
48 limit::LimitOrder,
49 limit_if_touched::LimitIfTouchedOrder,
50 list::OrderList,
51 market::MarketOrder,
52 market_if_touched::MarketIfTouchedOrder,
53 market_to_limit::MarketToLimitOrder,
54 stop_limit::StopLimitOrder,
55 stop_market::StopMarketOrder,
56 trailing_stop_limit::TrailingStopLimitOrder,
57 trailing_stop_market::TrailingStopMarketOrder,
58};
59use crate::{
60 enums::{
61 ContingencyType, LiquiditySide, OrderSide, OrderSideSpecified, OrderStatus, OrderType,
62 PositionSide, TimeInForce, TrailingOffsetType, TriggerType,
63 },
64 events::{
65 OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied, OrderEmulated,
66 OrderEventAny, OrderExpired, OrderFilled, OrderInitialized, OrderModifyRejected,
67 OrderPendingCancel, OrderPendingUpdate, OrderRejected, OrderReleased, OrderSubmitted,
68 OrderTriggered, OrderUpdated,
69 },
70 identifiers::{
71 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
72 StrategyId, Symbol, TradeId, TraderId, Venue, VenueOrderId,
73 },
74 orderbook::OwnBookOrder,
75 types::{Currency, Money, Price, Quantity},
76};
77
78pub const STOP_ORDER_TYPES: &[OrderType] = &[
80 OrderType::StopMarket,
81 OrderType::StopLimit,
82 OrderType::MarketIfTouched,
83 OrderType::LimitIfTouched,
84];
85
86pub const LIMIT_ORDER_TYPES: &[OrderType] = &[
88 OrderType::Limit,
89 OrderType::StopLimit,
90 OrderType::LimitIfTouched,
91 OrderType::MarketIfTouched,
92];
93
94pub const LOCAL_ACTIVE_ORDER_STATUSES: &[OrderStatus] = &[
96 OrderStatus::Initialized,
97 OrderStatus::Emulated,
98 OrderStatus::Released,
99];
100
101pub const CANCELLABLE_ORDER_STATUSES: &[OrderStatus] = &[
110 OrderStatus::Accepted,
111 OrderStatus::Triggered,
112 OrderStatus::PendingUpdate,
113 OrderStatus::PartiallyFilled,
114];
115
116#[must_use]
125pub fn cancellable_order_statuses_set() -> &'static AHashSet<OrderStatus> {
126 OrderStatus::cancellable_statuses_set()
127}
128
129#[derive(thiserror::Error, Debug)]
130pub enum OrderError {
131 #[error("Order not found: {0}")]
132 NotFound(ClientOrderId),
133 #[error("Order invariant failed: must have a side for this operation")]
134 NoOrderSide,
135 #[error("Invalid event for order type")]
136 InvalidOrderEvent,
137 #[error("Invalid order state transition")]
138 InvalidStateTransition,
139 #[error("Order was already initialized")]
140 AlreadyInitialized,
141 #[error("Order had no previous state")]
142 NoPreviousState,
143 #[error("Duplicate fill: trade_id {0} already applied to order")]
144 DuplicateFill(TradeId),
145 #[error("{0}")]
146 Invariant(#[from] anyhow::Error),
147}
148
149#[must_use]
151pub fn ustr_indexmap_to_str(h: IndexMap<Ustr, Ustr>) -> IndexMap<String, String> {
152 h.into_iter()
153 .map(|(k, v)| (k.to_string(), v.to_string()))
154 .collect()
155}
156
157#[must_use]
159pub fn str_indexmap_to_ustr(h: IndexMap<String, String>) -> IndexMap<Ustr, Ustr> {
160 h.into_iter()
161 .map(|(k, v)| (Ustr::from(&k), Ustr::from(&v)))
162 .collect()
163}
164
165#[inline]
166pub(crate) fn check_display_qty(
167 display_qty: Option<Quantity>,
168 quantity: Quantity,
169) -> Result<(), OrderError> {
170 if let Some(q) = display_qty
171 && q > quantity
172 {
173 return Err(OrderError::Invariant(anyhow::anyhow!(
174 "`display_qty` may not exceed `quantity`"
175 )));
176 }
177 Ok(())
178}
179
180#[inline]
181pub(crate) fn check_time_in_force(
182 time_in_force: TimeInForce,
183 expire_time: Option<UnixNanos>,
184) -> Result<(), OrderError> {
185 if time_in_force == TimeInForce::Gtd && expire_time.unwrap_or_default() == 0 {
186 return Err(OrderError::Invariant(anyhow::anyhow!(
187 "`expire_time` is required for `GTD` order"
188 )));
189 }
190 Ok(())
191}
192
193impl OrderStatus {
194 #[rustfmt::skip]
200 pub fn transition(&mut self, event: &OrderEventAny) -> Result<Self, OrderError> {
201 let new_state = match (self, event) {
202 (Self::Initialized, OrderEventAny::Denied(_)) => Self::Denied,
203 (Self::Initialized, OrderEventAny::Emulated(_)) => Self::Emulated, (Self::Initialized, OrderEventAny::Released(_)) => Self::Released, (Self::Initialized, OrderEventAny::Submitted(_)) => Self::Submitted,
206 (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,
218 (Self::Submitted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
219 (Self::Submitted, OrderEventAny::Rejected(_)) => Self::Rejected,
220 (Self::Submitted, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Submitted, OrderEventAny::Accepted(_)) => Self::Accepted,
222 (Self::Submitted, OrderEventAny::Updated(_)) => Self::Submitted,
223 (Self::Submitted, OrderEventAny::Filled(_)) => Self::Filled,
224 (Self::Accepted, OrderEventAny::Rejected(_)) => Self::Rejected, (Self::Accepted, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
226 (Self::Accepted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
227 (Self::Accepted, OrderEventAny::Canceled(_)) => Self::Canceled,
228 (Self::Accepted, OrderEventAny::Triggered(_)) => Self::Triggered,
229 (Self::Accepted, OrderEventAny::Updated(_)) => Self::Accepted, (Self::Accepted, OrderEventAny::Expired(_)) => Self::Expired,
231 (Self::Accepted, OrderEventAny::Filled(_)) => Self::Filled,
232 (Self::Canceled, OrderEventAny::Filled(_)) => Self::Filled, (Self::PendingUpdate, OrderEventAny::Rejected(_)) => Self::Rejected,
234 (Self::PendingUpdate, OrderEventAny::Accepted(_)) => Self::Accepted,
235 (Self::PendingUpdate, OrderEventAny::Canceled(_)) => Self::Canceled,
236 (Self::PendingUpdate, OrderEventAny::Expired(_)) => Self::Expired,
237 (Self::PendingUpdate, OrderEventAny::Triggered(_)) => Self::Triggered,
238 (Self::PendingUpdate, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate, (Self::PendingUpdate, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
240 (Self::PendingUpdate, OrderEventAny::ModifyRejected(_)) => Self::PendingUpdate, (Self::PendingUpdate, OrderEventAny::Filled(_)) => Self::Filled,
242 (Self::PendingCancel, OrderEventAny::Rejected(_)) => Self::Rejected,
243 (Self::PendingCancel, OrderEventAny::PendingCancel(_)) => Self::PendingCancel, (Self::PendingCancel, OrderEventAny::CancelRejected(_)) => Self::PendingCancel, (Self::PendingCancel, OrderEventAny::Canceled(_)) => Self::Canceled,
246 (Self::PendingCancel, OrderEventAny::Expired(_)) => Self::Expired,
247 (Self::PendingCancel, OrderEventAny::Accepted(_)) => Self::Accepted, (Self::PendingCancel, OrderEventAny::Filled(_)) => Self::Filled,
249 (Self::Triggered, OrderEventAny::Rejected(_)) => Self::Rejected,
250 (Self::Triggered, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
251 (Self::Triggered, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
252 (Self::Triggered, OrderEventAny::Canceled(_)) => Self::Canceled,
253 (Self::Triggered, OrderEventAny::Expired(_)) => Self::Expired,
254 (Self::Triggered, OrderEventAny::Filled(_)) => Self::Filled,
255 (Self::Triggered, OrderEventAny::Updated(_)) => Self::Triggered,
256 (Self::PartiallyFilled, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
257 (Self::PartiallyFilled, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
258 (Self::PartiallyFilled, OrderEventAny::Canceled(_)) => Self::Canceled,
259 (Self::PartiallyFilled, OrderEventAny::Expired(_)) => Self::Expired,
260 (Self::PartiallyFilled, OrderEventAny::Filled(_)) => Self::Filled,
261 (Self::PartiallyFilled, OrderEventAny::Accepted(_)) => Self::Accepted,
262 (Self::PartiallyFilled, OrderEventAny::Updated(_)) => Self::PartiallyFilled,
263 _ => return Err(OrderError::InvalidStateTransition),
264 };
265 Ok(new_state)
266 }
267}
268
269#[enum_dispatch]
270pub trait Order: 'static + Send {
271 fn into_any(self) -> OrderAny;
272 fn status(&self) -> OrderStatus;
273 fn trader_id(&self) -> TraderId;
274 fn strategy_id(&self) -> StrategyId;
275 fn instrument_id(&self) -> InstrumentId;
276 fn symbol(&self) -> Symbol;
277 fn venue(&self) -> Venue;
278 fn client_order_id(&self) -> ClientOrderId;
279 fn venue_order_id(&self) -> Option<VenueOrderId>;
280 fn position_id(&self) -> Option<PositionId>;
281 fn account_id(&self) -> Option<AccountId>;
282 fn last_trade_id(&self) -> Option<TradeId>;
283 fn order_side(&self) -> OrderSide;
284 fn order_type(&self) -> OrderType;
285 fn quantity(&self) -> Quantity;
286 fn time_in_force(&self) -> TimeInForce;
287 fn expire_time(&self) -> Option<UnixNanos>;
288 fn price(&self) -> Option<Price>;
289 fn trigger_price(&self) -> Option<Price>;
290 fn activation_price(&self) -> Option<Price> {
291 None
292 }
293 fn trigger_type(&self) -> Option<TriggerType>;
294 fn liquidity_side(&self) -> Option<LiquiditySide>;
295 fn is_post_only(&self) -> bool;
296 fn is_reduce_only(&self) -> bool;
297 fn is_quote_quantity(&self) -> bool;
298 fn display_qty(&self) -> Option<Quantity>;
299 fn limit_offset(&self) -> Option<Decimal>;
300 fn trailing_offset(&self) -> Option<Decimal>;
301 fn trailing_offset_type(&self) -> Option<TrailingOffsetType>;
302 fn emulation_trigger(&self) -> Option<TriggerType>;
303 fn trigger_instrument_id(&self) -> Option<InstrumentId>;
304 fn contingency_type(&self) -> Option<ContingencyType>;
305 fn order_list_id(&self) -> Option<OrderListId>;
306 fn linked_order_ids(&self) -> Option<&[ClientOrderId]>;
307 fn parent_order_id(&self) -> Option<ClientOrderId>;
308 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId>;
309 fn exec_algorithm_params(&self) -> Option<&IndexMap<Ustr, Ustr>>;
310 fn exec_spawn_id(&self) -> Option<ClientOrderId>;
311 fn tags(&self) -> Option<&[Ustr]>;
312 fn filled_qty(&self) -> Quantity;
313 fn leaves_qty(&self) -> Quantity;
314 fn overfill_qty(&self) -> Quantity;
315
316 fn calculate_overfill(&self, fill_qty: Quantity) -> Quantity {
318 let potential_filled = self.filled_qty() + fill_qty;
319 potential_filled.saturating_sub(self.quantity())
320 }
321
322 fn avg_px(&self) -> Option<f64>;
323 fn slippage(&self) -> Option<f64>;
324 fn init_id(&self) -> UUID4;
325 fn ts_init(&self) -> UnixNanos;
326 fn ts_submitted(&self) -> Option<UnixNanos>;
327 fn ts_accepted(&self) -> Option<UnixNanos>;
328 fn ts_closed(&self) -> Option<UnixNanos>;
329 fn ts_last(&self) -> UnixNanos;
330
331 fn order_side_specified(&self) -> OrderSideSpecified {
332 self.order_side().as_specified()
333 }
334 fn commissions(&self) -> &IndexMap<Currency, Money>;
335
336 fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError>;
342 fn update(&mut self, event: &OrderUpdated);
343
344 fn events(&self) -> Vec<&OrderEventAny>;
345
346 fn last_event(&self) -> &OrderEventAny {
347 self.events()
349 .last()
350 .expect("Order invariant violated: no events")
351 }
352
353 fn event_count(&self) -> usize {
354 self.events().len()
355 }
356
357 fn venue_order_ids(&self) -> Vec<&VenueOrderId>;
358
359 fn trade_ids(&self) -> Vec<&TradeId>;
360
361 fn has_price(&self) -> bool;
362
363 fn is_duplicate_fill(&self, fill: &OrderFilled) -> bool {
365 self.events().iter().any(|event| {
366 if let OrderEventAny::Filled(existing) = event {
367 existing.trade_id == fill.trade_id
368 && existing.order_side == fill.order_side
369 && existing.last_qty == fill.last_qty
370 && existing.last_px == fill.last_px
371 } else {
372 false
373 }
374 })
375 }
376
377 fn is_buy(&self) -> bool {
378 self.order_side() == OrderSide::Buy
379 }
380
381 fn is_sell(&self) -> bool {
382 self.order_side() == OrderSide::Sell
383 }
384
385 fn is_passive(&self) -> bool {
386 self.order_type() != OrderType::Market
387 }
388
389 fn is_aggressive(&self) -> bool {
390 self.order_type() == OrderType::Market
391 }
392
393 fn is_emulated(&self) -> bool {
394 self.status() == OrderStatus::Emulated
395 }
396
397 fn is_active_local(&self) -> bool {
398 matches!(
399 self.status(),
400 OrderStatus::Initialized | OrderStatus::Emulated | OrderStatus::Released
401 )
402 }
403
404 fn is_primary(&self) -> bool {
405 self.exec_algorithm_id().is_some()
406 && self
407 .exec_spawn_id()
408 .is_some_and(|spawn_id| self.client_order_id() == spawn_id)
409 }
410
411 fn is_spawned(&self) -> bool {
412 self.exec_algorithm_id().is_some()
413 && self
414 .exec_spawn_id()
415 .is_some_and(|spawn_id| self.client_order_id() != spawn_id)
416 }
417
418 fn is_contingency(&self) -> bool {
419 self.contingency_type().is_some()
420 }
421
422 fn is_parent_order(&self) -> bool {
423 match self.contingency_type() {
424 Some(c) => c == ContingencyType::Oto,
425 None => false,
426 }
427 }
428
429 fn is_child_order(&self) -> bool {
430 self.parent_order_id().is_some()
431 }
432
433 fn is_open(&self) -> bool {
434 if let Some(emulation_trigger) = self.emulation_trigger()
435 && emulation_trigger != TriggerType::NoTrigger
436 {
437 return false;
438 }
439
440 matches!(
441 self.status(),
442 OrderStatus::Accepted
443 | OrderStatus::Triggered
444 | OrderStatus::PendingCancel
445 | OrderStatus::PendingUpdate
446 | OrderStatus::PartiallyFilled
447 )
448 }
449
450 fn is_canceled(&self) -> bool {
451 self.status() == OrderStatus::Canceled
452 }
453
454 fn is_closed(&self) -> bool {
455 matches!(
456 self.status(),
457 OrderStatus::Denied
458 | OrderStatus::Rejected
459 | OrderStatus::Canceled
460 | OrderStatus::Expired
461 | OrderStatus::Filled
462 )
463 }
464
465 fn is_inflight(&self) -> bool {
466 if let Some(emulation_trigger) = self.emulation_trigger()
467 && emulation_trigger != TriggerType::NoTrigger
468 {
469 return false;
470 }
471
472 matches!(
473 self.status(),
474 OrderStatus::Submitted | OrderStatus::PendingCancel | OrderStatus::PendingUpdate
475 )
476 }
477
478 fn is_pending_update(&self) -> bool {
479 self.status() == OrderStatus::PendingUpdate
480 }
481
482 fn is_pending_cancel(&self) -> bool {
483 self.status() == OrderStatus::PendingCancel
484 }
485
486 fn to_own_book_order(&self) -> OwnBookOrder {
487 OwnBookOrder::new(
488 self.trader_id(),
489 self.client_order_id(),
490 self.venue_order_id(),
491 self.order_side().as_specified(),
492 self.price().expect("`OwnBookOrder` must have a price"), self.quantity(),
494 self.order_type(),
495 self.time_in_force(),
496 self.status(),
497 self.ts_last(),
498 self.ts_accepted().unwrap_or_default(),
499 self.ts_submitted().unwrap_or_default(),
500 self.ts_init(),
501 )
502 }
503
504 fn is_triggered(&self) -> Option<bool>; fn set_position_id(&mut self, position_id: Option<PositionId>);
506 fn set_quantity(&mut self, quantity: Quantity);
507 fn set_leaves_qty(&mut self, leaves_qty: Quantity);
508 fn set_emulation_trigger(&mut self, emulation_trigger: Option<TriggerType>);
509 fn set_is_quote_quantity(&mut self, is_quote_quantity: bool);
510 fn set_liquidity_side(&mut self, liquidity_side: LiquiditySide);
511 fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool;
512 fn previous_status(&self) -> Option<OrderStatus>;
513}
514
515impl<T> From<&T> for OrderInitialized
516where
517 T: Order,
518{
519 fn from(order: &T) -> Self {
520 Self {
521 trader_id: order.trader_id(),
522 strategy_id: order.strategy_id(),
523 instrument_id: order.instrument_id(),
524 client_order_id: order.client_order_id(),
525 order_side: order.order_side(),
526 order_type: order.order_type(),
527 quantity: order.quantity(),
528 price: order.price(),
529 trigger_price: order.trigger_price(),
530 trigger_type: order.trigger_type(),
531 time_in_force: order.time_in_force(),
532 expire_time: order.expire_time(),
533 post_only: order.is_post_only(),
534 reduce_only: order.is_reduce_only(),
535 quote_quantity: order.is_quote_quantity(),
536 display_qty: order.display_qty(),
537 limit_offset: order.limit_offset(),
538 trailing_offset: order.trailing_offset(),
539 trailing_offset_type: order.trailing_offset_type(),
540 emulation_trigger: order.emulation_trigger(),
541 trigger_instrument_id: order.trigger_instrument_id(),
542 contingency_type: order.contingency_type(),
543 order_list_id: order.order_list_id(),
544 linked_order_ids: order.linked_order_ids().map(|x| x.to_vec()),
545 parent_order_id: order.parent_order_id(),
546 exec_algorithm_id: order.exec_algorithm_id(),
547 exec_algorithm_params: order.exec_algorithm_params().map(|x| x.to_owned()),
548 exec_spawn_id: order.exec_spawn_id(),
549 tags: order.tags().map(|x| x.to_vec()),
550 event_id: order.init_id(),
551 ts_event: order.ts_init(),
552 ts_init: order.ts_init(),
553 reconciliation: false,
554 }
555 }
556}
557
558#[derive(Clone, Debug, Serialize, Deserialize)]
559pub struct OrderCore {
560 pub events: Vec<OrderEventAny>,
561 pub commissions: IndexMap<Currency, Money>,
562 pub venue_order_ids: Vec<VenueOrderId>,
563 pub trade_ids: Vec<TradeId>,
564 pub previous_status: Option<OrderStatus>,
565 pub status: OrderStatus,
566 pub trader_id: TraderId,
567 pub strategy_id: StrategyId,
568 pub instrument_id: InstrumentId,
569 pub client_order_id: ClientOrderId,
570 pub venue_order_id: Option<VenueOrderId>,
571 pub position_id: Option<PositionId>,
572 pub account_id: Option<AccountId>,
573 pub last_trade_id: Option<TradeId>,
574 pub side: OrderSide,
575 pub order_type: OrderType,
576 pub quantity: Quantity,
577 pub time_in_force: TimeInForce,
578 pub liquidity_side: Option<LiquiditySide>,
579 pub is_reduce_only: bool,
580 pub is_quote_quantity: bool,
581 pub emulation_trigger: Option<TriggerType>,
582 pub contingency_type: Option<ContingencyType>,
583 pub order_list_id: Option<OrderListId>,
584 pub linked_order_ids: Option<Vec<ClientOrderId>>,
585 pub parent_order_id: Option<ClientOrderId>,
586 pub exec_algorithm_id: Option<ExecAlgorithmId>,
587 pub exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
588 pub exec_spawn_id: Option<ClientOrderId>,
589 pub tags: Option<Vec<Ustr>>,
590 pub filled_qty: Quantity,
591 pub leaves_qty: Quantity,
592 pub overfill_qty: Quantity,
593 pub avg_px: Option<f64>,
594 pub slippage: Option<f64>,
595 pub init_id: UUID4,
596 pub ts_init: UnixNanos,
597 pub ts_submitted: Option<UnixNanos>,
598 pub ts_accepted: Option<UnixNanos>,
599 pub ts_closed: Option<UnixNanos>,
600 pub ts_last: UnixNanos,
601}
602
603impl OrderCore {
604 pub fn new(init: OrderInitialized) -> Self {
606 let events: Vec<OrderEventAny> = vec![OrderEventAny::Initialized(init.clone())];
607 Self {
608 events,
609 commissions: IndexMap::new(),
610 venue_order_ids: Vec::new(),
611 trade_ids: Vec::new(),
612 previous_status: None,
613 status: OrderStatus::Initialized,
614 trader_id: init.trader_id,
615 strategy_id: init.strategy_id,
616 instrument_id: init.instrument_id,
617 client_order_id: init.client_order_id,
618 venue_order_id: None,
619 position_id: None,
620 account_id: None,
621 last_trade_id: None,
622 side: init.order_side,
623 order_type: init.order_type,
624 quantity: init.quantity,
625 time_in_force: init.time_in_force,
626 liquidity_side: Some(LiquiditySide::NoLiquiditySide),
627 is_reduce_only: init.reduce_only,
628 is_quote_quantity: init.quote_quantity,
629 emulation_trigger: init.emulation_trigger.or(Some(TriggerType::NoTrigger)),
630 contingency_type: init
631 .contingency_type
632 .or(Some(ContingencyType::NoContingency)),
633 order_list_id: init.order_list_id,
634 linked_order_ids: init.linked_order_ids,
635 parent_order_id: init.parent_order_id,
636 exec_algorithm_id: init.exec_algorithm_id,
637 exec_algorithm_params: init.exec_algorithm_params,
638 exec_spawn_id: init.exec_spawn_id,
639 tags: init.tags,
640 filled_qty: Quantity::zero(init.quantity.precision),
641 leaves_qty: init.quantity,
642 overfill_qty: Quantity::zero(init.quantity.precision),
643 avg_px: None,
644 slippage: None,
645 init_id: init.event_id,
646 ts_init: init.ts_event,
647 ts_submitted: None,
648 ts_accepted: None,
649 ts_closed: None,
650 ts_last: init.ts_event,
651 }
652 }
653
654 pub fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError> {
661 if self.client_order_id != event.client_order_id() {
662 return Err(OrderError::Invariant(anyhow::anyhow!(
663 "Event client_order_id {} does not match order client_order_id {}",
664 event.client_order_id(),
665 self.client_order_id
666 )));
667 }
668 if self.strategy_id != event.strategy_id() {
669 return Err(OrderError::Invariant(anyhow::anyhow!(
670 "Event strategy_id {} does not match order strategy_id {}",
671 event.strategy_id(),
672 self.strategy_id
673 )));
674 }
675
676 if !matches!(
681 event,
682 OrderEventAny::Initialized(_)
683 | OrderEventAny::ModifyRejected(_)
684 | OrderEventAny::CancelRejected(_)
685 ) && !matches!(
686 self.status,
687 OrderStatus::PendingUpdate | OrderStatus::PendingCancel
688 ) {
689 self.previous_status = Some(self.status);
690 }
691
692 if let OrderEventAny::Filled(fill) = &event
694 && self.trade_ids.contains(&fill.trade_id)
695 {
696 return Err(OrderError::DuplicateFill(fill.trade_id));
697 }
698
699 let new_status = self.status.transition(&event)?;
700 self.status = new_status;
701
702 match &event {
703 OrderEventAny::Initialized(_) => return Err(OrderError::AlreadyInitialized),
704 OrderEventAny::Denied(event) => self.denied(event),
705 OrderEventAny::Emulated(event) => self.emulated(event),
706 OrderEventAny::Released(event) => self.released(event),
707 OrderEventAny::Submitted(event) => self.submitted(event),
708 OrderEventAny::Rejected(event) => self.rejected(event),
709 OrderEventAny::Accepted(event) => self.accepted(event),
710 OrderEventAny::PendingUpdate(event) => self.pending_update(event),
711 OrderEventAny::PendingCancel(event) => self.pending_cancel(event),
712 OrderEventAny::ModifyRejected(event) => self.modify_rejected(event)?,
713 OrderEventAny::CancelRejected(event) => self.cancel_rejected(event)?,
714 OrderEventAny::Updated(event) => self.updated(event),
715 OrderEventAny::Triggered(event) => self.triggered(event),
716 OrderEventAny::Canceled(event) => self.canceled(event),
717 OrderEventAny::Expired(event) => self.expired(event),
718 OrderEventAny::Filled(event) => self.filled(event),
719 }
720
721 self.ts_last = event.ts_event();
722 self.events.push(event);
723 Ok(())
724 }
725
726 fn denied(&mut self, event: &OrderDenied) {
727 self.ts_closed = Some(event.ts_event);
728 }
729
730 fn emulated(&self, _event: &OrderEmulated) {
731 }
733
734 fn released(&mut self, _event: &OrderReleased) {
735 self.emulation_trigger = None;
736 }
737
738 fn submitted(&mut self, event: &OrderSubmitted) {
739 self.account_id = Some(event.account_id);
740 self.ts_submitted = Some(event.ts_event);
741 }
742
743 fn accepted(&mut self, event: &OrderAccepted) {
744 self.account_id = Some(event.account_id);
745 self.venue_order_id = Some(event.venue_order_id);
746 self.venue_order_ids.push(event.venue_order_id);
747 self.ts_accepted = Some(event.ts_event);
748 }
749
750 fn rejected(&mut self, event: &OrderRejected) {
751 self.ts_closed = Some(event.ts_event);
752 }
753
754 fn pending_update(&self, _event: &OrderPendingUpdate) {
755 }
757
758 fn pending_cancel(&self, _event: &OrderPendingCancel) {
759 }
761
762 fn modify_rejected(&mut self, _event: &OrderModifyRejected) -> Result<(), OrderError> {
763 self.status = self.previous_status.ok_or(OrderError::NoPreviousState)?;
764 Ok(())
765 }
766
767 fn cancel_rejected(&mut self, _event: &OrderCancelRejected) -> Result<(), OrderError> {
768 self.status = self.previous_status.ok_or(OrderError::NoPreviousState)?;
769 Ok(())
770 }
771
772 fn triggered(&mut self, _event: &OrderTriggered) {}
773
774 fn canceled(&mut self, event: &OrderCanceled) {
775 self.ts_closed = Some(event.ts_event);
776 }
777
778 fn expired(&mut self, event: &OrderExpired) {
779 self.ts_closed = Some(event.ts_event);
780 }
781
782 fn updated(&mut self, event: &OrderUpdated) {
783 if let Some(venue_order_id) = &event.venue_order_id
784 && (self.venue_order_id.is_none()
785 || venue_order_id != self.venue_order_id.as_ref().unwrap())
786 {
787 self.venue_order_id = Some(*venue_order_id);
788 self.venue_order_ids.push(*venue_order_id);
789 }
790 }
791
792 fn filled(&mut self, event: &OrderFilled) {
793 let new_filled_qty = Quantity::from_raw(
795 self.filled_qty.raw.saturating_add(event.last_qty.raw),
796 self.filled_qty.precision,
797 );
798
799 if new_filled_qty > self.quantity {
801 let overfill_raw = new_filled_qty.raw - self.quantity.raw;
802 self.overfill_qty = Quantity::from_raw(
803 self.overfill_qty.raw.saturating_add(overfill_raw),
804 self.filled_qty.precision,
805 );
806 }
807
808 if new_filled_qty < self.quantity {
809 self.status = OrderStatus::PartiallyFilled;
810 } else {
811 self.status = OrderStatus::Filled;
812 self.ts_closed = Some(event.ts_event);
813 }
814
815 self.venue_order_id = Some(event.venue_order_id);
816 self.position_id = event.position_id;
817 self.trade_ids.push(event.trade_id);
818 self.last_trade_id = Some(event.trade_id);
819 self.liquidity_side = Some(event.liquidity_side);
820 self.filled_qty = new_filled_qty;
821 self.leaves_qty = self.leaves_qty.saturating_sub(event.last_qty);
822 self.ts_last = event.ts_event;
823 if self.ts_accepted.is_none() {
824 self.ts_accepted = Some(event.ts_event);
826 }
827
828 self.set_avg_px(event.last_qty, event.last_px);
829 }
830
831 fn set_avg_px(&mut self, last_qty: Quantity, last_px: Price) {
832 if self.avg_px.is_none() {
833 self.avg_px = Some(last_px.as_f64());
834 return;
835 }
836
837 let prev_filled_qty = (self.filled_qty - last_qty).as_f64();
839 let last_qty_f64 = last_qty.as_f64();
840 let total_qty = prev_filled_qty + last_qty_f64;
841
842 let avg_px = self
843 .avg_px
844 .unwrap()
845 .mul_add(prev_filled_qty, last_px.as_f64() * last_qty_f64)
846 / total_qty;
847 self.avg_px = Some(avg_px);
848 }
849
850 pub fn set_slippage(&mut self, price: Price) {
851 self.slippage = self.avg_px.and_then(|avg_px| {
852 let current_price = price.as_f64();
853 match self.side {
854 OrderSide::Buy if avg_px > current_price => Some(avg_px - current_price),
855 OrderSide::Sell if avg_px < current_price => Some(current_price - avg_px),
856 _ => None,
857 }
858 });
859 }
860
861 #[must_use]
863 pub fn opposite_side(side: OrderSide) -> OrderSide {
864 match side {
865 OrderSide::Buy => OrderSide::Sell,
866 OrderSide::Sell => OrderSide::Buy,
867 OrderSide::NoOrderSide => OrderSide::NoOrderSide,
868 }
869 }
870
871 #[must_use]
873 pub fn closing_side(side: PositionSide) -> OrderSide {
874 match side {
875 PositionSide::Long => OrderSide::Sell,
876 PositionSide::Short => OrderSide::Buy,
877 PositionSide::Flat => OrderSide::NoOrderSide,
878 PositionSide::NoPositionSide => OrderSide::NoOrderSide,
879 }
880 }
881
882 #[must_use]
886 pub fn signed_decimal_qty(&self) -> Decimal {
887 match self.side {
888 OrderSide::Buy => self.quantity.as_decimal(),
889 OrderSide::Sell => -self.quantity.as_decimal(),
890 _ => panic!("Invalid order side"),
891 }
892 }
893
894 #[must_use]
895 pub fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool {
896 if side == PositionSide::Flat {
897 return false;
898 }
899
900 match (self.side, side) {
901 (OrderSide::Buy, PositionSide::Long) => false,
902 (OrderSide::Buy, PositionSide::Short) => self.leaves_qty <= position_qty,
903 (OrderSide::Sell, PositionSide::Short) => false,
904 (OrderSide::Sell, PositionSide::Long) => self.leaves_qty <= position_qty,
905 _ => true,
906 }
907 }
908
909 #[must_use]
910 pub fn commission(&self, currency: &Currency) -> Option<Money> {
911 self.commissions.get(currency).copied()
912 }
913
914 #[must_use]
915 pub fn commissions(&self) -> IndexMap<Currency, Money> {
916 self.commissions.clone()
917 }
918
919 #[must_use]
920 pub fn commissions_vec(&self) -> Vec<Money> {
921 self.commissions.values().copied().collect()
922 }
923
924 #[must_use]
925 pub fn init_event(&self) -> Option<OrderEventAny> {
926 self.events.first().cloned()
927 }
928}
929
930#[cfg(test)]
931mod tests {
932 use rstest::rstest;
933 use rust_decimal_macros::dec;
934
935 use super::*;
936 use crate::{
937 enums::{OrderSide, OrderStatus, PositionSide},
938 events::order::{
939 accepted::OrderAcceptedBuilder, canceled::OrderCanceledBuilder,
940 denied::OrderDeniedBuilder, filled::OrderFilledBuilder,
941 initialized::OrderInitializedBuilder, submitted::OrderSubmittedBuilder,
942 triggered::OrderTriggeredBuilder, updated::OrderUpdatedBuilder,
943 },
944 orders::MarketOrder,
945 };
946
947 #[rstest]
958 #[case(OrderSide::Buy, OrderSide::Sell)]
959 #[case(OrderSide::Sell, OrderSide::Buy)]
960 #[case(OrderSide::NoOrderSide, OrderSide::NoOrderSide)]
961 fn test_order_opposite_side(#[case] order_side: OrderSide, #[case] expected_side: OrderSide) {
962 let result = OrderCore::opposite_side(order_side);
963 assert_eq!(result, expected_side);
964 }
965
966 #[rstest]
967 #[case(PositionSide::Long, OrderSide::Sell)]
968 #[case(PositionSide::Short, OrderSide::Buy)]
969 #[case(PositionSide::NoPositionSide, OrderSide::NoOrderSide)]
970 fn test_closing_side(#[case] position_side: PositionSide, #[case] expected_side: OrderSide) {
971 let result = OrderCore::closing_side(position_side);
972 assert_eq!(result, expected_side);
973 }
974
975 #[rstest]
976 #[case(OrderSide::Buy, dec!(10_000))]
977 #[case(OrderSide::Sell, dec!(-10_000))]
978 fn test_signed_decimal_qty(#[case] order_side: OrderSide, #[case] expected: Decimal) {
979 let order: MarketOrder = OrderInitializedBuilder::default()
980 .order_side(order_side)
981 .quantity(Quantity::from(10_000))
982 .build()
983 .unwrap()
984 .into();
985
986 let result = order.signed_decimal_qty();
987 assert_eq!(result, expected);
988 }
989
990 #[rustfmt::skip]
991 #[rstest]
992 #[case(OrderSide::Buy, Quantity::from(100), PositionSide::Long, Quantity::from(50), false)]
993 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(50), true)]
994 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(100), true)]
995 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
996 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
997 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(50), true)]
998 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(100), true)]
999 #[case(OrderSide::Sell, Quantity::from(100), PositionSide::Short, Quantity::from(50), false)]
1000 fn test_would_reduce_only(
1001 #[case] order_side: OrderSide,
1002 #[case] order_qty: Quantity,
1003 #[case] position_side: PositionSide,
1004 #[case] position_qty: Quantity,
1005 #[case] expected: bool,
1006 ) {
1007 let order: MarketOrder = OrderInitializedBuilder::default()
1008 .order_side(order_side)
1009 .quantity(order_qty)
1010 .build()
1011 .unwrap()
1012 .into();
1013
1014 assert_eq!(
1015 order.would_reduce_only(position_side, position_qty),
1016 expected
1017 );
1018 }
1019
1020 #[rstest]
1021 fn test_order_state_transition_denied() {
1022 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
1023 let denied = OrderDeniedBuilder::default().build().unwrap();
1024 let event = OrderEventAny::Denied(denied);
1025
1026 order.apply(event.clone()).unwrap();
1027
1028 assert_eq!(order.status, OrderStatus::Denied);
1029 assert!(order.is_closed());
1030 assert!(!order.is_open());
1031 assert_eq!(order.event_count(), 2);
1032 assert_eq!(order.last_event(), &event);
1033 }
1034
1035 #[rstest]
1036 fn test_order_life_cycle_to_filled() {
1037 let init = OrderInitializedBuilder::default().build().unwrap();
1038 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1039 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1040 let filled = OrderFilledBuilder::default().build().unwrap();
1041
1042 let mut order: MarketOrder = init.clone().into();
1043 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1044 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1045 order.apply(OrderEventAny::Filled(filled)).unwrap();
1046
1047 assert_eq!(order.client_order_id, init.client_order_id);
1048 assert_eq!(order.status(), OrderStatus::Filled);
1049 assert_eq!(order.filled_qty(), Quantity::from(100_000));
1050 assert_eq!(order.leaves_qty(), Quantity::from(0));
1051 assert_eq!(order.avg_px(), Some(1.0));
1052 assert!(!order.is_open());
1053 assert!(order.is_closed());
1054 assert_eq!(order.commission(&Currency::USD()), None);
1055 assert_eq!(order.commissions(), &IndexMap::new());
1056 }
1057
1058 #[rstest]
1059 fn test_order_state_transition_to_canceled() {
1060 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
1061 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1062 let canceled = OrderCanceledBuilder::default().build().unwrap();
1063
1064 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1065 order.apply(OrderEventAny::Canceled(canceled)).unwrap();
1066
1067 assert_eq!(order.status(), OrderStatus::Canceled);
1068 assert!(order.is_closed());
1069 assert!(!order.is_open());
1070 }
1071
1072 #[rstest]
1073 fn test_order_life_cycle_to_partially_filled() {
1074 let init = OrderInitializedBuilder::default().build().unwrap();
1075 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1076 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1077 let filled = OrderFilledBuilder::default()
1078 .last_qty(Quantity::from(50_000))
1079 .build()
1080 .unwrap();
1081
1082 let mut order: MarketOrder = init.clone().into();
1083 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1084 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1085 order.apply(OrderEventAny::Filled(filled)).unwrap();
1086
1087 assert_eq!(order.client_order_id, init.client_order_id);
1088 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
1089 assert_eq!(order.filled_qty(), Quantity::from(50_000));
1090 assert_eq!(order.leaves_qty(), Quantity::from(50_000));
1091 assert!(order.is_open());
1092 assert!(!order.is_closed());
1093 }
1094
1095 #[rstest]
1096 fn test_order_commission_calculation() {
1097 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
1098 order
1099 .commissions
1100 .insert(Currency::USD(), Money::new(10.0, Currency::USD()));
1101
1102 assert_eq!(
1103 order.commission(&Currency::USD()),
1104 Some(Money::new(10.0, Currency::USD()))
1105 );
1106 assert_eq!(
1107 order.commissions_vec(),
1108 vec![Money::new(10.0, Currency::USD())]
1109 );
1110 }
1111
1112 #[rstest]
1113 fn test_order_is_primary() {
1114 let order: MarketOrder = OrderInitializedBuilder::default()
1115 .exec_algorithm_id(Some(ExecAlgorithmId::from("ALGO-001")))
1116 .exec_spawn_id(Some(ClientOrderId::from("O-001")))
1117 .client_order_id(ClientOrderId::from("O-001"))
1118 .build()
1119 .unwrap()
1120 .into();
1121
1122 assert!(order.is_primary());
1123 assert!(!order.is_spawned());
1124 }
1125
1126 #[rstest]
1127 fn test_order_is_spawned() {
1128 let order: MarketOrder = OrderInitializedBuilder::default()
1129 .exec_algorithm_id(Some(ExecAlgorithmId::from("ALGO-001")))
1130 .exec_spawn_id(Some(ClientOrderId::from("O-002")))
1131 .client_order_id(ClientOrderId::from("O-001"))
1132 .build()
1133 .unwrap()
1134 .into();
1135
1136 assert!(!order.is_primary());
1137 assert!(order.is_spawned());
1138 }
1139
1140 #[rstest]
1141 fn test_order_is_contingency() {
1142 let order: MarketOrder = OrderInitializedBuilder::default()
1143 .contingency_type(Some(ContingencyType::Oto))
1144 .build()
1145 .unwrap()
1146 .into();
1147
1148 assert!(order.is_contingency());
1149 assert!(order.is_parent_order());
1150 assert!(!order.is_child_order());
1151 }
1152
1153 #[rstest]
1154 fn test_order_is_child_order() {
1155 let order: MarketOrder = OrderInitializedBuilder::default()
1156 .parent_order_id(Some(ClientOrderId::from("PARENT-001")))
1157 .build()
1158 .unwrap()
1159 .into();
1160
1161 assert!(order.is_child_order());
1162 assert!(!order.is_parent_order());
1163 }
1164
1165 #[rstest]
1166 fn test_to_own_book_order_timestamp_ordering() {
1167 use crate::orders::limit::LimitOrder;
1168
1169 let init = OrderInitializedBuilder::default()
1171 .price(Some(Price::from("100.00")))
1172 .build()
1173 .unwrap();
1174 let submitted = OrderSubmittedBuilder::default()
1175 .ts_event(UnixNanos::from(1_000_000))
1176 .build()
1177 .unwrap();
1178 let accepted = OrderAcceptedBuilder::default()
1179 .ts_event(UnixNanos::from(2_000_000))
1180 .build()
1181 .unwrap();
1182
1183 let mut order: LimitOrder = init.into();
1184 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1185 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1186
1187 let own_book_order = order.to_own_book_order();
1188
1189 assert_eq!(own_book_order.ts_submitted, UnixNanos::from(1_000_000));
1191 assert_eq!(own_book_order.ts_accepted, UnixNanos::from(2_000_000));
1192 assert_eq!(own_book_order.ts_last, UnixNanos::from(2_000_000));
1193 }
1194
1195 #[rstest]
1196 fn test_order_accepted_without_submitted_sets_account_id() {
1197 let init = OrderInitializedBuilder::default().build().unwrap();
1199 let accepted = OrderAcceptedBuilder::default()
1200 .account_id(AccountId::from("EXTERNAL-001"))
1201 .build()
1202 .unwrap();
1203
1204 let mut order: MarketOrder = init.into();
1205
1206 assert_eq!(order.account_id(), None);
1208
1209 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1211
1212 assert_eq!(order.account_id(), Some(AccountId::from("EXTERNAL-001")));
1214 assert_eq!(order.status(), OrderStatus::Accepted);
1215 }
1216
1217 #[rstest]
1218 fn test_order_accepted_after_submitted_preserves_account_id() {
1219 let init = OrderInitializedBuilder::default().build().unwrap();
1221 let submitted = OrderSubmittedBuilder::default()
1222 .account_id(AccountId::from("SUBMITTED-001"))
1223 .build()
1224 .unwrap();
1225 let accepted = OrderAcceptedBuilder::default()
1226 .account_id(AccountId::from("ACCEPTED-001"))
1227 .build()
1228 .unwrap();
1229
1230 let mut order: MarketOrder = init.into();
1231 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1232
1233 assert_eq!(order.account_id(), Some(AccountId::from("SUBMITTED-001")));
1235
1236 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1238
1239 assert_eq!(order.account_id(), Some(AccountId::from("ACCEPTED-001")));
1241 assert_eq!(order.status(), OrderStatus::Accepted);
1242 }
1243
1244 #[rstest]
1245 fn test_overfill_tracks_overfill_qty() {
1246 let init = OrderInitializedBuilder::default()
1248 .quantity(Quantity::from(100_000))
1249 .build()
1250 .unwrap();
1251 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1252 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1253 let overfill = OrderFilledBuilder::default()
1254 .last_qty(Quantity::from(110_000)) .build()
1256 .unwrap();
1257
1258 let mut order: MarketOrder = init.into();
1259 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1260 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1261 order.apply(OrderEventAny::Filled(overfill)).unwrap();
1262
1263 assert_eq!(order.overfill_qty(), Quantity::from(10_000));
1265 assert_eq!(order.filled_qty(), Quantity::from(110_000));
1266 assert_eq!(order.leaves_qty(), Quantity::from(0));
1267 assert_eq!(order.status(), OrderStatus::Filled);
1268 }
1269
1270 #[rstest]
1271 fn test_partial_fill_then_overfill() {
1272 let init = OrderInitializedBuilder::default()
1274 .quantity(Quantity::from(100_000))
1275 .build()
1276 .unwrap();
1277 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1278 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1279 let fill1 = OrderFilledBuilder::default()
1280 .last_qty(Quantity::from(80_000))
1281 .trade_id(TradeId::from("TRADE-1"))
1282 .build()
1283 .unwrap();
1284 let fill2 = OrderFilledBuilder::default()
1285 .last_qty(Quantity::from(30_000)) .trade_id(TradeId::from("TRADE-2"))
1287 .build()
1288 .unwrap();
1289
1290 let mut order: MarketOrder = init.into();
1291 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1292 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1293 order.apply(OrderEventAny::Filled(fill1)).unwrap();
1294
1295 assert_eq!(order.overfill_qty(), Quantity::from(0));
1297 assert_eq!(order.filled_qty(), Quantity::from(80_000));
1298 assert_eq!(order.leaves_qty(), Quantity::from(20_000));
1299
1300 order.apply(OrderEventAny::Filled(fill2)).unwrap();
1301
1302 assert_eq!(order.overfill_qty(), Quantity::from(10_000));
1304 assert_eq!(order.filled_qty(), Quantity::from(110_000));
1305 assert_eq!(order.leaves_qty(), Quantity::from(0));
1306 assert_eq!(order.status(), OrderStatus::Filled);
1307 }
1308
1309 #[rstest]
1310 fn test_exact_fill_no_overfill() {
1311 let init = OrderInitializedBuilder::default()
1313 .quantity(Quantity::from(100_000))
1314 .build()
1315 .unwrap();
1316 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1317 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1318 let filled = OrderFilledBuilder::default()
1319 .last_qty(Quantity::from(100_000)) .build()
1321 .unwrap();
1322
1323 let mut order: MarketOrder = init.into();
1324 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1325 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1326 order.apply(OrderEventAny::Filled(filled)).unwrap();
1327
1328 assert_eq!(order.overfill_qty(), Quantity::from(0));
1330 assert_eq!(order.filled_qty(), Quantity::from(100_000));
1331 assert_eq!(order.leaves_qty(), Quantity::from(0));
1332 }
1333
1334 #[rstest]
1335 fn test_partial_fill_then_overfill_with_fractional_quantities() {
1336 let init = OrderInitializedBuilder::default()
1340 .quantity(Quantity::from("2450.5"))
1341 .build()
1342 .unwrap();
1343 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1344 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1345 let fill1 = OrderFilledBuilder::default()
1346 .last_qty(Quantity::from("1202.5"))
1347 .trade_id(TradeId::from("TRADE-1"))
1348 .build()
1349 .unwrap();
1350 let fill2 = OrderFilledBuilder::default()
1351 .last_qty(Quantity::from("1285.5")) .trade_id(TradeId::from("TRADE-2"))
1353 .build()
1354 .unwrap();
1355
1356 let mut order: MarketOrder = init.into();
1357 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1358 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1359 order.apply(OrderEventAny::Filled(fill1)).unwrap();
1360
1361 assert_eq!(order.overfill_qty(), Quantity::from(0));
1363 assert_eq!(order.filled_qty(), Quantity::from("1202.5"));
1364 assert_eq!(order.leaves_qty(), Quantity::from("1248.0"));
1365 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
1366
1367 order.apply(OrderEventAny::Filled(fill2)).unwrap();
1368
1369 assert_eq!(order.overfill_qty(), Quantity::from("37.5"));
1371 assert_eq!(order.filled_qty(), Quantity::from("2488.0"));
1372 assert_eq!(order.leaves_qty(), Quantity::from(0));
1373 assert_eq!(order.status(), OrderStatus::Filled);
1374 }
1375
1376 #[rstest]
1377 fn test_calculate_overfill_returns_zero_when_no_overfill() {
1378 let order: MarketOrder = OrderInitializedBuilder::default()
1379 .quantity(Quantity::from(100_000))
1380 .build()
1381 .unwrap()
1382 .into();
1383
1384 let overfill = order.calculate_overfill(Quantity::from(50_000));
1386 assert_eq!(overfill, Quantity::from(0));
1387
1388 let overfill = order.calculate_overfill(Quantity::from(100_000));
1390 assert_eq!(overfill, Quantity::from(0));
1391 }
1392
1393 #[rstest]
1394 fn test_calculate_overfill_returns_overfill_amount() {
1395 let order: MarketOrder = OrderInitializedBuilder::default()
1396 .quantity(Quantity::from(100_000))
1397 .build()
1398 .unwrap()
1399 .into();
1400
1401 let overfill = order.calculate_overfill(Quantity::from(110_000));
1403 assert_eq!(overfill, Quantity::from(10_000));
1404 }
1405
1406 #[rstest]
1407 fn test_calculate_overfill_accounts_for_existing_fills() {
1408 let init = OrderInitializedBuilder::default()
1409 .quantity(Quantity::from(100_000))
1410 .build()
1411 .unwrap();
1412 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1413 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1414 let partial_fill = OrderFilledBuilder::default()
1415 .last_qty(Quantity::from(60_000))
1416 .build()
1417 .unwrap();
1418
1419 let mut order: MarketOrder = init.into();
1420 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1421 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1422 order.apply(OrderEventAny::Filled(partial_fill)).unwrap();
1423
1424 let overfill = order.calculate_overfill(Quantity::from(50_000));
1427 assert_eq!(overfill, Quantity::from(10_000));
1428
1429 let overfill = order.calculate_overfill(Quantity::from(40_000));
1431 assert_eq!(overfill, Quantity::from(0));
1432 }
1433
1434 #[rstest]
1435 fn test_calculate_overfill_with_fractional_quantities() {
1436 let order: MarketOrder = OrderInitializedBuilder::default()
1437 .quantity(Quantity::from("2450.5"))
1438 .build()
1439 .unwrap()
1440 .into();
1441
1442 let overfill = order.calculate_overfill(Quantity::from("2488.0"));
1445 assert_eq!(overfill, Quantity::from("37.5"));
1446 }
1447
1448 #[rstest]
1449 fn test_duplicate_fill_rejected() {
1450 let init = OrderInitializedBuilder::default()
1451 .quantity(Quantity::from(100_000))
1452 .build()
1453 .unwrap();
1454 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1455 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1456 let fill1 = OrderFilledBuilder::default()
1457 .last_qty(Quantity::from(50_000))
1458 .trade_id(TradeId::from("TRADE-001"))
1459 .build()
1460 .unwrap();
1461 let fill2_duplicate = OrderFilledBuilder::default()
1462 .last_qty(Quantity::from(50_000))
1463 .trade_id(TradeId::from("TRADE-001")) .build()
1465 .unwrap();
1466
1467 let mut order: MarketOrder = init.into();
1468 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1469 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1470 order.apply(OrderEventAny::Filled(fill1)).unwrap();
1471
1472 assert_eq!(order.filled_qty(), Quantity::from(50_000));
1474 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
1475
1476 let result = order.apply(OrderEventAny::Filled(fill2_duplicate));
1478 assert!(result.is_err());
1479 match result.unwrap_err() {
1480 OrderError::DuplicateFill(trade_id) => {
1481 assert_eq!(trade_id, TradeId::from("TRADE-001"));
1482 }
1483 e => panic!("Expected DuplicateFill error, got: {e:?}"),
1484 }
1485
1486 assert_eq!(order.filled_qty(), Quantity::from(50_000));
1488 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
1489 }
1490
1491 #[rstest]
1492 fn test_different_trade_ids_allowed() {
1493 let init = OrderInitializedBuilder::default()
1494 .quantity(Quantity::from(100_000))
1495 .build()
1496 .unwrap();
1497 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1498 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1499 let fill1 = OrderFilledBuilder::default()
1500 .last_qty(Quantity::from(50_000))
1501 .trade_id(TradeId::from("TRADE-001"))
1502 .build()
1503 .unwrap();
1504 let fill2 = OrderFilledBuilder::default()
1505 .last_qty(Quantity::from(50_000))
1506 .trade_id(TradeId::from("TRADE-002")) .build()
1508 .unwrap();
1509
1510 let mut order: MarketOrder = init.into();
1511 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1512 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1513 order.apply(OrderEventAny::Filled(fill1)).unwrap();
1514 order.apply(OrderEventAny::Filled(fill2)).unwrap();
1515
1516 assert_eq!(order.filled_qty(), Quantity::from(100_000));
1518 assert_eq!(order.status(), OrderStatus::Filled);
1519 assert_eq!(order.trade_ids.len(), 2);
1520 }
1521
1522 #[rstest]
1523 fn test_partially_filled_order_can_be_updated() {
1524 let init = OrderInitializedBuilder::default()
1527 .quantity(Quantity::from(100_000))
1528 .build()
1529 .unwrap();
1530 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1531 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1532 let partial_fill = OrderFilledBuilder::default()
1533 .last_qty(Quantity::from(40_000))
1534 .build()
1535 .unwrap();
1536 let updated = OrderUpdatedBuilder::default()
1537 .quantity(Quantity::from(80_000)) .build()
1539 .unwrap();
1540
1541 let mut order: MarketOrder = init.into();
1542 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1543 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1544 order.apply(OrderEventAny::Filled(partial_fill)).unwrap();
1545
1546 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
1547 assert_eq!(order.filled_qty(), Quantity::from(40_000));
1548
1549 order.apply(OrderEventAny::Updated(updated)).unwrap();
1550
1551 assert_eq!(order.status(), OrderStatus::PartiallyFilled);
1552 assert_eq!(order.quantity(), Quantity::from(80_000));
1553 assert_eq!(order.leaves_qty(), Quantity::from(40_000)); }
1555
1556 #[rstest]
1557 fn test_triggered_order_can_be_updated() {
1558 let init = OrderInitializedBuilder::default()
1561 .quantity(Quantity::from(100_000))
1562 .build()
1563 .unwrap();
1564 let submitted = OrderSubmittedBuilder::default().build().unwrap();
1565 let accepted = OrderAcceptedBuilder::default().build().unwrap();
1566 let triggered = OrderTriggeredBuilder::default().build().unwrap();
1567 let updated = OrderUpdatedBuilder::default()
1568 .quantity(Quantity::from(80_000))
1569 .build()
1570 .unwrap();
1571
1572 let mut order: MarketOrder = init.into();
1573 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
1574 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
1575 order.apply(OrderEventAny::Triggered(triggered)).unwrap();
1576
1577 assert_eq!(order.status(), OrderStatus::Triggered);
1578
1579 order.apply(OrderEventAny::Updated(updated)).unwrap();
1580
1581 assert_eq!(order.status(), OrderStatus::Triggered);
1582 assert_eq!(order.quantity(), Quantity::from(80_000));
1583 }
1584}