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