1pub mod any;
19pub mod builder;
20pub mod default;
21pub mod limit;
22pub mod limit_if_touched;
23pub mod list;
24pub mod market;
25pub mod market_if_touched;
26pub mod market_to_limit;
27pub mod stop_limit;
28pub mod stop_market;
29pub mod trailing_stop_limit;
30pub mod trailing_stop_market;
31
32#[cfg(feature = "stubs")]
33pub mod stubs;
34
35use enum_dispatch::enum_dispatch;
37use indexmap::IndexMap;
38use nautilus_core::{UUID4, UnixNanos};
39use rust_decimal::Decimal;
40use serde::{Deserialize, Serialize};
41use ustr::Ustr;
42
43pub use crate::orders::{
44 any::{LimitOrderAny, OrderAny, PassiveOrderAny, StopOrderAny},
45 builder::OrderTestBuilder,
46 limit::LimitOrder,
47 limit_if_touched::LimitIfTouchedOrder,
48 list::OrderList,
49 market::MarketOrder,
50 market_if_touched::MarketIfTouchedOrder,
51 market_to_limit::MarketToLimitOrder,
52 stop_limit::StopLimitOrder,
53 stop_market::StopMarketOrder,
54 trailing_stop_limit::TrailingStopLimitOrder,
55 trailing_stop_market::TrailingStopMarketOrder,
56};
57use crate::{
58 enums::{
59 ContingencyType, LiquiditySide, OrderSide, OrderSideSpecified, OrderStatus, OrderType,
60 PositionSide, TimeInForce, TrailingOffsetType, TriggerType,
61 },
62 events::{
63 OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied, OrderEmulated,
64 OrderEventAny, OrderExpired, OrderFilled, OrderInitialized, OrderModifyRejected,
65 OrderPendingCancel, OrderPendingUpdate, OrderRejected, OrderReleased, OrderSubmitted,
66 OrderTriggered, OrderUpdated,
67 },
68 identifiers::{
69 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
70 StrategyId, Symbol, TradeId, TraderId, Venue, VenueOrderId,
71 },
72 orderbook::OwnBookOrder,
73 types::{Currency, Money, Price, Quantity},
74};
75
76#[allow(dead_code)] const STOP_ORDER_TYPES: &[OrderType] = &[
78 OrderType::StopMarket,
79 OrderType::StopLimit,
80 OrderType::MarketIfTouched,
81 OrderType::LimitIfTouched,
82];
83
84#[allow(dead_code)] const LIMIT_ORDER_TYPES: &[OrderType] = &[
86 OrderType::Limit,
87 OrderType::StopLimit,
88 OrderType::LimitIfTouched,
89 OrderType::MarketIfTouched,
90];
91
92#[allow(dead_code)] const LOCAL_ACTIVE_ORDER_STATUS: &[OrderStatus] = &[
94 OrderStatus::Initialized,
95 OrderStatus::Emulated,
96 OrderStatus::Released,
97];
98
99#[derive(thiserror::Error, Debug)]
100pub enum OrderError {
101 #[error("Order not found: {0}")]
102 NotFound(ClientOrderId),
103 #[error("Order invariant failed: must have a side for this operation")]
104 NoOrderSide,
105 #[error("Invalid event for order type")]
106 InvalidOrderEvent,
107 #[error("Invalid order state transition")]
108 InvalidStateTransition,
109 #[error("Order was already initialized")]
110 AlreadyInitialized,
111 #[error("Order had no previous state")]
112 NoPreviousState,
113}
114
115#[must_use]
116pub fn ustr_indexmap_to_str(h: IndexMap<Ustr, Ustr>) -> IndexMap<String, String> {
117 h.into_iter()
118 .map(|(k, v)| (k.to_string(), v.to_string()))
119 .collect()
120}
121
122#[must_use]
123pub fn str_indexmap_to_ustr(h: IndexMap<String, String>) -> IndexMap<Ustr, Ustr> {
124 h.into_iter()
125 .map(|(k, v)| (Ustr::from(&k), Ustr::from(&v)))
126 .collect()
127}
128
129impl OrderStatus {
130 #[rustfmt::skip]
136 pub fn transition(&mut self, event: &OrderEventAny) -> Result<Self, OrderError> {
137 let new_state = match (self, event) {
138 (Self::Initialized, OrderEventAny::Denied(_)) => Self::Denied,
139 (Self::Initialized, OrderEventAny::Emulated(_)) => Self::Emulated, (Self::Initialized, OrderEventAny::Released(_)) => Self::Released, (Self::Initialized, OrderEventAny::Submitted(_)) => Self::Submitted,
142 (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,
154 (Self::Submitted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
155 (Self::Submitted, OrderEventAny::Rejected(_)) => Self::Rejected,
156 (Self::Submitted, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Submitted, OrderEventAny::Accepted(_)) => Self::Accepted,
158 (Self::Submitted, OrderEventAny::Filled(_)) => Self::Filled,
159 (Self::Submitted, OrderEventAny::Updated(_)) => Self::Submitted,
160 (Self::Accepted, OrderEventAny::Rejected(_)) => Self::Rejected, (Self::Accepted, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
162 (Self::Accepted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
163 (Self::Accepted, OrderEventAny::Canceled(_)) => Self::Canceled,
164 (Self::Accepted, OrderEventAny::Triggered(_)) => Self::Triggered,
165 (Self::Accepted, OrderEventAny::Expired(_)) => Self::Expired,
166 (Self::Accepted, OrderEventAny::Filled(_)) => Self::Filled,
167 (Self::Accepted, OrderEventAny::Updated(_)) => Self::Accepted, (Self::Canceled, OrderEventAny::Filled(_)) => Self::Filled, (Self::PendingUpdate, OrderEventAny::Rejected(_)) => Self::Rejected,
170 (Self::PendingUpdate, OrderEventAny::Accepted(_)) => Self::Accepted,
171 (Self::PendingUpdate, OrderEventAny::Canceled(_)) => Self::Canceled,
172 (Self::PendingUpdate, OrderEventAny::Expired(_)) => Self::Expired,
173 (Self::PendingUpdate, OrderEventAny::Triggered(_)) => Self::Triggered,
174 (Self::PendingUpdate, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate, (Self::PendingUpdate, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
176 (Self::PendingUpdate, OrderEventAny::Filled(_)) => Self::Filled,
177 (Self::PendingCancel, OrderEventAny::Rejected(_)) => Self::Rejected,
178 (Self::PendingCancel, OrderEventAny::PendingCancel(_)) => Self::PendingCancel, (Self::PendingCancel, OrderEventAny::Canceled(_)) => Self::Canceled,
180 (Self::PendingCancel, OrderEventAny::Expired(_)) => Self::Expired,
181 (Self::PendingCancel, OrderEventAny::Accepted(_)) => Self::Accepted, (Self::PendingCancel, OrderEventAny::Filled(_)) => Self::Filled,
183 (Self::Triggered, OrderEventAny::Rejected(_)) => Self::Rejected,
184 (Self::Triggered, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
185 (Self::Triggered, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
186 (Self::Triggered, OrderEventAny::Canceled(_)) => Self::Canceled,
187 (Self::Triggered, OrderEventAny::Expired(_)) => Self::Expired,
188 (Self::Triggered, OrderEventAny::Filled(_)) => Self::Filled,
189 (Self::PartiallyFilled, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
190 (Self::PartiallyFilled, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
191 (Self::PartiallyFilled, OrderEventAny::Canceled(_)) => Self::Canceled,
192 (Self::PartiallyFilled, OrderEventAny::Expired(_)) => Self::Expired,
193 (Self::PartiallyFilled, OrderEventAny::Filled(_)) => Self::Filled,
194 (Self::PartiallyFilled, OrderEventAny::Accepted(_)) => Self::Accepted,
195 _ => return Err(OrderError::InvalidStateTransition),
196 };
197 Ok(new_state)
198 }
199}
200
201#[enum_dispatch]
202pub trait Order: 'static + Send {
203 fn into_any(self) -> OrderAny;
204 fn status(&self) -> OrderStatus;
205 fn trader_id(&self) -> TraderId;
206 fn strategy_id(&self) -> StrategyId;
207 fn instrument_id(&self) -> InstrumentId;
208 fn symbol(&self) -> Symbol;
209 fn venue(&self) -> Venue;
210 fn client_order_id(&self) -> ClientOrderId;
211 fn venue_order_id(&self) -> Option<VenueOrderId>;
212 fn position_id(&self) -> Option<PositionId>;
213 fn account_id(&self) -> Option<AccountId>;
214 fn last_trade_id(&self) -> Option<TradeId>;
215 fn order_side(&self) -> OrderSide;
216 fn order_type(&self) -> OrderType;
217 fn quantity(&self) -> Quantity;
218 fn time_in_force(&self) -> TimeInForce;
219 fn expire_time(&self) -> Option<UnixNanos>;
220 fn price(&self) -> Option<Price>;
221 fn trigger_price(&self) -> Option<Price>;
222 fn trigger_type(&self) -> Option<TriggerType>;
223 fn liquidity_side(&self) -> Option<LiquiditySide>;
224 fn is_post_only(&self) -> bool;
225 fn is_reduce_only(&self) -> bool;
226 fn is_quote_quantity(&self) -> bool;
227 fn display_qty(&self) -> Option<Quantity>;
228 fn limit_offset(&self) -> Option<Decimal>;
229 fn trailing_offset(&self) -> Option<Decimal>;
230 fn trailing_offset_type(&self) -> Option<TrailingOffsetType>;
231 fn emulation_trigger(&self) -> Option<TriggerType>;
232 fn trigger_instrument_id(&self) -> Option<InstrumentId>;
233 fn contingency_type(&self) -> Option<ContingencyType>;
234 fn order_list_id(&self) -> Option<OrderListId>;
235 fn linked_order_ids(&self) -> Option<&[ClientOrderId]>;
236 fn parent_order_id(&self) -> Option<ClientOrderId>;
237 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId>;
238 fn exec_algorithm_params(&self) -> Option<&IndexMap<Ustr, Ustr>>;
239 fn exec_spawn_id(&self) -> Option<ClientOrderId>;
240 fn tags(&self) -> Option<&[Ustr]>;
241 fn filled_qty(&self) -> Quantity;
242 fn leaves_qty(&self) -> Quantity;
243 fn avg_px(&self) -> Option<f64>;
244 fn slippage(&self) -> Option<f64>;
245 fn init_id(&self) -> UUID4;
246 fn ts_init(&self) -> UnixNanos;
247 fn ts_submitted(&self) -> Option<UnixNanos>;
248 fn ts_accepted(&self) -> Option<UnixNanos>;
249 fn ts_closed(&self) -> Option<UnixNanos>;
250 fn ts_last(&self) -> UnixNanos;
251
252 fn order_side_specified(&self) -> OrderSideSpecified {
253 self.order_side().as_specified()
254 }
255 fn commissions(&self) -> &IndexMap<Currency, Money>;
256
257 fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError>;
263 fn update(&mut self, event: &OrderUpdated);
264
265 fn events(&self) -> Vec<&OrderEventAny>;
266
267 fn last_event(&self) -> &OrderEventAny {
268 self.events().last().unwrap()
270 }
271
272 fn event_count(&self) -> usize {
273 self.events().len()
274 }
275
276 fn venue_order_ids(&self) -> Vec<&VenueOrderId>;
277
278 fn trade_ids(&self) -> Vec<&TradeId>;
279
280 fn has_price(&self) -> bool;
281
282 fn is_buy(&self) -> bool {
283 self.order_side() == OrderSide::Buy
284 }
285
286 fn is_sell(&self) -> bool {
287 self.order_side() == OrderSide::Sell
288 }
289
290 fn is_passive(&self) -> bool {
291 self.order_type() != OrderType::Market
292 }
293
294 fn is_aggressive(&self) -> bool {
295 self.order_type() == OrderType::Market
296 }
297
298 fn is_emulated(&self) -> bool {
299 self.status() == OrderStatus::Emulated
300 }
301
302 fn is_active_local(&self) -> bool {
303 matches!(
304 self.status(),
305 OrderStatus::Initialized | OrderStatus::Emulated | OrderStatus::Released
306 )
307 }
308
309 fn is_primary(&self) -> bool {
310 self.exec_algorithm_id().is_some()
312 && self.client_order_id() == self.exec_spawn_id().unwrap()
313 }
314
315 fn is_secondary(&self) -> bool {
316 self.exec_algorithm_id().is_some()
318 && self.client_order_id() != self.exec_spawn_id().unwrap()
319 }
320
321 fn is_contingency(&self) -> bool {
322 self.contingency_type().is_some()
323 }
324
325 fn is_parent_order(&self) -> bool {
326 match self.contingency_type() {
327 Some(c) => c == ContingencyType::Oto,
328 None => false,
329 }
330 }
331
332 fn is_child_order(&self) -> bool {
333 self.parent_order_id().is_some()
334 }
335
336 fn is_open(&self) -> bool {
337 if let Some(emulation_trigger) = self.emulation_trigger() {
338 if emulation_trigger != TriggerType::NoTrigger {
339 return false;
340 }
341 }
342
343 matches!(
344 self.status(),
345 OrderStatus::Accepted
346 | OrderStatus::Triggered
347 | OrderStatus::PendingCancel
348 | OrderStatus::PendingUpdate
349 | OrderStatus::PartiallyFilled
350 )
351 }
352
353 fn is_canceled(&self) -> bool {
354 self.status() == OrderStatus::Canceled
355 }
356
357 fn is_closed(&self) -> bool {
358 matches!(
359 self.status(),
360 OrderStatus::Denied
361 | OrderStatus::Rejected
362 | OrderStatus::Canceled
363 | OrderStatus::Expired
364 | OrderStatus::Filled
365 )
366 }
367
368 fn is_inflight(&self) -> bool {
369 if let Some(emulation_trigger) = self.emulation_trigger() {
370 if emulation_trigger != TriggerType::NoTrigger {
371 return false;
372 }
373 }
374
375 matches!(
376 self.status(),
377 OrderStatus::Submitted | OrderStatus::PendingCancel | OrderStatus::PendingUpdate
378 )
379 }
380
381 fn is_pending_update(&self) -> bool {
382 self.status() == OrderStatus::PendingUpdate
383 }
384
385 fn is_pending_cancel(&self) -> bool {
386 self.status() == OrderStatus::PendingCancel
387 }
388
389 fn is_spawned(&self) -> bool {
390 self.exec_spawn_id()
391 .is_some_and(|exec_spawn_id| exec_spawn_id != self.client_order_id())
392 }
393
394 fn to_own_book_order(&self) -> OwnBookOrder {
395 OwnBookOrder::new(
396 self.trader_id(),
397 self.client_order_id(),
398 self.venue_order_id(),
399 self.order_side().as_specified(),
400 self.price().expect("`OwnBookOrder` must have a price"), self.quantity(),
402 self.order_type(),
403 self.time_in_force(),
404 self.status(),
405 self.ts_last(),
406 self.ts_submitted().unwrap_or_default(),
407 self.ts_accepted().unwrap_or_default(),
408 self.ts_init(),
409 )
410 }
411
412 fn is_triggered(&self) -> Option<bool>; fn set_position_id(&mut self, position_id: Option<PositionId>);
414 fn set_quantity(&mut self, quantity: Quantity);
415 fn set_leaves_qty(&mut self, leaves_qty: Quantity);
416 fn set_emulation_trigger(&mut self, emulation_trigger: Option<TriggerType>);
417 fn set_is_quote_quantity(&mut self, is_quote_quantity: bool);
418 fn set_liquidity_side(&mut self, liquidity_side: LiquiditySide);
419 fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool;
420 fn previous_status(&self) -> Option<OrderStatus>;
421}
422
423impl<T> From<&T> for OrderInitialized
424where
425 T: Order,
426{
427 fn from(order: &T) -> Self {
428 Self {
429 trader_id: order.trader_id(),
430 strategy_id: order.strategy_id(),
431 instrument_id: order.instrument_id(),
432 client_order_id: order.client_order_id(),
433 order_side: order.order_side(),
434 order_type: order.order_type(),
435 quantity: order.quantity(),
436 price: order.price(),
437 trigger_price: order.trigger_price(),
438 trigger_type: order.trigger_type(),
439 time_in_force: order.time_in_force(),
440 expire_time: order.expire_time(),
441 post_only: order.is_post_only(),
442 reduce_only: order.is_reduce_only(),
443 quote_quantity: order.is_quote_quantity(),
444 display_qty: order.display_qty(),
445 limit_offset: order.limit_offset(),
446 trailing_offset: order.trailing_offset(),
447 trailing_offset_type: order.trailing_offset_type(),
448 emulation_trigger: order.emulation_trigger(),
449 trigger_instrument_id: order.trigger_instrument_id(),
450 contingency_type: order.contingency_type(),
451 order_list_id: order.order_list_id(),
452 linked_order_ids: order.linked_order_ids().map(|x| x.to_vec()),
453 parent_order_id: order.parent_order_id(),
454 exec_algorithm_id: order.exec_algorithm_id(),
455 exec_algorithm_params: order.exec_algorithm_params().map(|x| x.to_owned()),
456 exec_spawn_id: order.exec_spawn_id(),
457 tags: order.tags().map(|x| x.to_vec()),
458 event_id: order.init_id(),
459 ts_event: order.ts_init(),
460 ts_init: order.ts_init(),
461 reconciliation: false,
462 }
463 }
464}
465
466#[derive(Clone, Debug, Serialize, Deserialize)]
467pub struct OrderCore {
468 pub events: Vec<OrderEventAny>,
469 pub commissions: IndexMap<Currency, Money>,
470 pub venue_order_ids: Vec<VenueOrderId>,
471 pub trade_ids: Vec<TradeId>,
472 pub previous_status: Option<OrderStatus>,
473 pub status: OrderStatus,
474 pub trader_id: TraderId,
475 pub strategy_id: StrategyId,
476 pub instrument_id: InstrumentId,
477 pub client_order_id: ClientOrderId,
478 pub venue_order_id: Option<VenueOrderId>,
479 pub position_id: Option<PositionId>,
480 pub account_id: Option<AccountId>,
481 pub last_trade_id: Option<TradeId>,
482 pub side: OrderSide,
483 pub order_type: OrderType,
484 pub quantity: Quantity,
485 pub time_in_force: TimeInForce,
486 pub liquidity_side: Option<LiquiditySide>,
487 pub is_reduce_only: bool,
488 pub is_quote_quantity: bool,
489 pub emulation_trigger: Option<TriggerType>,
490 pub contingency_type: Option<ContingencyType>,
491 pub order_list_id: Option<OrderListId>,
492 pub linked_order_ids: Option<Vec<ClientOrderId>>,
493 pub parent_order_id: Option<ClientOrderId>,
494 pub exec_algorithm_id: Option<ExecAlgorithmId>,
495 pub exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
496 pub exec_spawn_id: Option<ClientOrderId>,
497 pub tags: Option<Vec<Ustr>>,
498 pub filled_qty: Quantity,
499 pub leaves_qty: Quantity,
500 pub avg_px: Option<f64>,
501 pub slippage: Option<f64>,
502 pub init_id: UUID4,
503 pub ts_init: UnixNanos,
504 pub ts_submitted: Option<UnixNanos>,
505 pub ts_accepted: Option<UnixNanos>,
506 pub ts_closed: Option<UnixNanos>,
507 pub ts_last: UnixNanos,
508}
509
510impl OrderCore {
511 pub fn new(init: OrderInitialized) -> Self {
513 let events: Vec<OrderEventAny> = vec![OrderEventAny::Initialized(init.clone())];
514 Self {
515 events,
516 commissions: IndexMap::new(),
517 venue_order_ids: Vec::new(),
518 trade_ids: Vec::new(),
519 previous_status: None,
520 status: OrderStatus::Initialized,
521 trader_id: init.trader_id,
522 strategy_id: init.strategy_id,
523 instrument_id: init.instrument_id,
524 client_order_id: init.client_order_id,
525 venue_order_id: None,
526 position_id: None,
527 account_id: None,
528 last_trade_id: None,
529 side: init.order_side,
530 order_type: init.order_type,
531 quantity: init.quantity,
532 time_in_force: init.time_in_force,
533 liquidity_side: Some(LiquiditySide::NoLiquiditySide),
534 is_reduce_only: init.reduce_only,
535 is_quote_quantity: init.quote_quantity,
536 emulation_trigger: init.emulation_trigger.or(Some(TriggerType::NoTrigger)),
537 contingency_type: init
538 .contingency_type
539 .or(Some(ContingencyType::NoContingency)),
540 order_list_id: init.order_list_id,
541 linked_order_ids: init.linked_order_ids,
542 parent_order_id: init.parent_order_id,
543 exec_algorithm_id: init.exec_algorithm_id,
544 exec_algorithm_params: init.exec_algorithm_params,
545 exec_spawn_id: init.exec_spawn_id,
546 tags: init.tags,
547 filled_qty: Quantity::zero(init.quantity.precision),
548 leaves_qty: init.quantity,
549 avg_px: None,
550 slippage: None,
551 init_id: init.event_id,
552 ts_init: init.ts_event,
553 ts_submitted: None,
554 ts_accepted: None,
555 ts_closed: None,
556 ts_last: init.ts_event,
557 }
558 }
559
560 pub fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError> {
566 assert_eq!(self.client_order_id, event.client_order_id());
567 assert_eq!(self.strategy_id, event.strategy_id());
568
569 let new_status = self.status.transition(&event)?;
570 self.previous_status = Some(self.status);
571 self.status = new_status;
572
573 match &event {
574 OrderEventAny::Initialized(_) => return Err(OrderError::AlreadyInitialized),
575 OrderEventAny::Denied(event) => self.denied(event),
576 OrderEventAny::Emulated(event) => self.emulated(event),
577 OrderEventAny::Released(event) => self.released(event),
578 OrderEventAny::Submitted(event) => self.submitted(event),
579 OrderEventAny::Rejected(event) => self.rejected(event),
580 OrderEventAny::Accepted(event) => self.accepted(event),
581 OrderEventAny::PendingUpdate(event) => self.pending_update(event),
582 OrderEventAny::PendingCancel(event) => self.pending_cancel(event),
583 OrderEventAny::ModifyRejected(event) => self.modify_rejected(event),
584 OrderEventAny::CancelRejected(event) => self.cancel_rejected(event),
585 OrderEventAny::Updated(event) => self.updated(event),
586 OrderEventAny::Triggered(event) => self.triggered(event),
587 OrderEventAny::Canceled(event) => self.canceled(event),
588 OrderEventAny::Expired(event) => self.expired(event),
589 OrderEventAny::Filled(event) => self.filled(event),
590 }
591
592 self.ts_last = event.ts_event();
593 self.events.push(event);
594 Ok(())
595 }
596
597 fn denied(&mut self, event: &OrderDenied) {
598 self.ts_closed = Some(event.ts_event);
599 }
600
601 fn emulated(&self, _event: &OrderEmulated) {
602 }
604
605 fn released(&mut self, _event: &OrderReleased) {
606 self.emulation_trigger = None;
607 }
608
609 fn submitted(&mut self, event: &OrderSubmitted) {
610 self.account_id = Some(event.account_id);
611 self.ts_submitted = Some(event.ts_event);
612 }
613
614 fn accepted(&mut self, event: &OrderAccepted) {
615 self.venue_order_id = Some(event.venue_order_id);
616 self.ts_accepted = Some(event.ts_event);
617 }
618
619 fn rejected(&mut self, event: &OrderRejected) {
620 self.ts_closed = Some(event.ts_event);
621 }
622
623 fn pending_update(&self, _event: &OrderPendingUpdate) {
624 }
626
627 fn pending_cancel(&self, _event: &OrderPendingCancel) {
628 }
630
631 fn modify_rejected(&mut self, _event: &OrderModifyRejected) {
632 self.status = self
633 .previous_status
634 .unwrap_or_else(|| panic!("{}", OrderError::NoPreviousState));
635 }
636
637 fn cancel_rejected(&mut self, _event: &OrderCancelRejected) {
638 self.status = self
639 .previous_status
640 .unwrap_or_else(|| panic!("{}", OrderError::NoPreviousState));
641 }
642
643 fn triggered(&mut self, _event: &OrderTriggered) {}
644
645 fn canceled(&mut self, event: &OrderCanceled) {
646 self.ts_closed = Some(event.ts_event);
647 }
648
649 fn expired(&mut self, event: &OrderExpired) {
650 self.ts_closed = Some(event.ts_event);
651 }
652
653 fn updated(&mut self, event: &OrderUpdated) {
654 if let Some(venue_order_id) = &event.venue_order_id {
655 if self.venue_order_id.is_none()
656 || venue_order_id != self.venue_order_id.as_ref().unwrap()
657 {
658 self.venue_order_id = Some(*venue_order_id);
659 self.venue_order_ids.push(*venue_order_id);
660 }
661 }
662 }
663
664 fn filled(&mut self, event: &OrderFilled) {
665 if self.filled_qty + event.last_qty < self.quantity {
666 self.status = OrderStatus::PartiallyFilled;
667 } else {
668 self.status = OrderStatus::Filled;
669 self.ts_closed = Some(event.ts_event);
670 }
671
672 self.venue_order_id = Some(event.venue_order_id);
673 self.position_id = event.position_id;
674 self.trade_ids.push(event.trade_id);
675 self.last_trade_id = Some(event.trade_id);
676 self.liquidity_side = Some(event.liquidity_side);
677 self.filled_qty += event.last_qty;
678 self.leaves_qty -= event.last_qty;
679 self.ts_last = event.ts_event;
680 if self.ts_accepted.is_none() {
681 self.ts_accepted = Some(event.ts_event);
683 }
684
685 self.set_avg_px(event.last_qty, event.last_px);
686 }
687
688 fn set_avg_px(&mut self, last_qty: Quantity, last_px: Price) {
689 if self.avg_px.is_none() {
690 self.avg_px = Some(last_px.as_f64());
691 }
692
693 let filled_qty = self.filled_qty.as_f64();
694 let total_qty = filled_qty + last_qty.as_f64();
695
696 let avg_px = self
697 .avg_px
698 .unwrap()
699 .mul_add(filled_qty, last_px.as_f64() * last_qty.as_f64())
700 / total_qty;
701 self.avg_px = Some(avg_px);
702 }
703
704 pub fn set_slippage(&mut self, price: Price) {
705 self.slippage = self.avg_px.and_then(|avg_px| {
706 let current_price = price.as_f64();
707 match self.side {
708 OrderSide::Buy if avg_px > current_price => Some(avg_px - current_price),
709 OrderSide::Sell if avg_px < current_price => Some(current_price - avg_px),
710 _ => None,
711 }
712 });
713 }
714
715 #[must_use]
716 pub fn opposite_side(side: OrderSide) -> OrderSide {
717 match side {
718 OrderSide::Buy => OrderSide::Sell,
719 OrderSide::Sell => OrderSide::Buy,
720 OrderSide::NoOrderSide => OrderSide::NoOrderSide,
721 }
722 }
723
724 #[must_use]
725 pub fn closing_side(side: PositionSide) -> OrderSide {
726 match side {
727 PositionSide::Long => OrderSide::Sell,
728 PositionSide::Short => OrderSide::Buy,
729 PositionSide::Flat => OrderSide::NoOrderSide,
730 PositionSide::NoPositionSide => OrderSide::NoOrderSide,
731 }
732 }
733
734 #[must_use]
735 pub fn signed_decimal_qty(&self) -> Decimal {
736 match self.side {
737 OrderSide::Buy => self.quantity.as_decimal(),
738 OrderSide::Sell => -self.quantity.as_decimal(),
739 _ => panic!("Invalid order side"),
740 }
741 }
742
743 #[must_use]
744 pub fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool {
745 if side == PositionSide::Flat {
746 return false;
747 }
748
749 match (self.side, side) {
750 (OrderSide::Buy, PositionSide::Long) => false,
751 (OrderSide::Buy, PositionSide::Short) => self.leaves_qty <= position_qty,
752 (OrderSide::Sell, PositionSide::Short) => false,
753 (OrderSide::Sell, PositionSide::Long) => self.leaves_qty <= position_qty,
754 _ => true,
755 }
756 }
757
758 #[must_use]
759 pub fn commission(&self, currency: &Currency) -> Option<Money> {
760 self.commissions.get(currency).copied()
761 }
762
763 #[must_use]
764 pub fn commissions(&self) -> IndexMap<Currency, Money> {
765 self.commissions.clone()
766 }
767
768 #[must_use]
769 pub fn commissions_vec(&self) -> Vec<Money> {
770 self.commissions.values().cloned().collect()
771 }
772
773 #[must_use]
774 pub fn init_event(&self) -> Option<OrderEventAny> {
775 self.events.first().cloned()
776 }
777}
778
779#[cfg(test)]
783mod tests {
784 use rstest::rstest;
785 use rust_decimal_macros::dec;
786
787 use super::*;
788 use crate::{
789 enums::{OrderSide, OrderStatus, PositionSide},
790 events::order::{
791 accepted::OrderAcceptedBuilder, denied::OrderDeniedBuilder, filled::OrderFilledBuilder,
792 initialized::OrderInitializedBuilder, submitted::OrderSubmittedBuilder,
793 },
794 orders::MarketOrder,
795 };
796
797 #[rstest]
808 #[case(OrderSide::Buy, OrderSide::Sell)]
809 #[case(OrderSide::Sell, OrderSide::Buy)]
810 #[case(OrderSide::NoOrderSide, OrderSide::NoOrderSide)]
811 fn test_order_opposite_side(#[case] order_side: OrderSide, #[case] expected_side: OrderSide) {
812 let result = OrderCore::opposite_side(order_side);
813 assert_eq!(result, expected_side);
814 }
815
816 #[rstest]
817 #[case(PositionSide::Long, OrderSide::Sell)]
818 #[case(PositionSide::Short, OrderSide::Buy)]
819 #[case(PositionSide::NoPositionSide, OrderSide::NoOrderSide)]
820 fn test_closing_side(#[case] position_side: PositionSide, #[case] expected_side: OrderSide) {
821 let result = OrderCore::closing_side(position_side);
822 assert_eq!(result, expected_side);
823 }
824
825 #[rstest]
826 #[case(OrderSide::Buy, dec!(10_000))]
827 #[case(OrderSide::Sell, dec!(-10_000))]
828 fn test_signed_decimal_qty(#[case] order_side: OrderSide, #[case] expected: Decimal) {
829 let order: MarketOrder = OrderInitializedBuilder::default()
830 .order_side(order_side)
831 .quantity(Quantity::from(10_000))
832 .build()
833 .unwrap()
834 .into();
835
836 let result = order.signed_decimal_qty();
837 assert_eq!(result, expected);
838 }
839
840 #[rustfmt::skip]
841 #[rstest]
842 #[case(OrderSide::Buy, Quantity::from(100), PositionSide::Long, Quantity::from(50), false)]
843 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(50), true)]
844 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(100), true)]
845 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
846 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
847 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(50), true)]
848 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(100), true)]
849 #[case(OrderSide::Sell, Quantity::from(100), PositionSide::Short, Quantity::from(50), false)]
850 fn test_would_reduce_only(
851 #[case] order_side: OrderSide,
852 #[case] order_qty: Quantity,
853 #[case] position_side: PositionSide,
854 #[case] position_qty: Quantity,
855 #[case] expected: bool,
856 ) {
857 let order: MarketOrder = OrderInitializedBuilder::default()
858 .order_side(order_side)
859 .quantity(order_qty)
860 .build()
861 .unwrap()
862 .into();
863
864 assert_eq!(
865 order.would_reduce_only(position_side, position_qty),
866 expected
867 );
868 }
869
870 #[rstest]
871 fn test_order_state_transition_denied() {
872 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
873 let denied = OrderDeniedBuilder::default().build().unwrap();
874 let event = OrderEventAny::Denied(denied);
875
876 order.apply(event.clone()).unwrap();
877
878 assert_eq!(order.status, OrderStatus::Denied);
879 assert!(order.is_closed());
880 assert!(!order.is_open());
881 assert_eq!(order.event_count(), 2);
882 assert_eq!(order.last_event(), &event);
883 }
884
885 #[rstest]
886 fn test_order_life_cycle_to_filled() {
887 let init = OrderInitializedBuilder::default().build().unwrap();
888 let submitted = OrderSubmittedBuilder::default().build().unwrap();
889 let accepted = OrderAcceptedBuilder::default().build().unwrap();
890 let filled = OrderFilledBuilder::default().build().unwrap();
891
892 let mut order: MarketOrder = init.clone().into();
893 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
894 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
895 order.apply(OrderEventAny::Filled(filled)).unwrap();
896
897 assert_eq!(order.client_order_id, init.client_order_id);
898 assert_eq!(order.status(), OrderStatus::Filled);
899 assert_eq!(order.filled_qty(), Quantity::from(100_000));
900 assert_eq!(order.leaves_qty(), Quantity::from(0));
901 assert_eq!(order.avg_px(), Some(1.0));
902 assert!(!order.is_open());
903 assert!(order.is_closed());
904 assert_eq!(order.commission(&Currency::USD()), None);
905 assert_eq!(order.commissions(), &IndexMap::new());
906 }
907}