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