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]
131 pub fn transition(&mut self, event: &OrderEventAny) -> Result<Self, OrderError> {
132 let new_state = match (self, event) {
133 (Self::Initialized, OrderEventAny::Denied(_)) => Self::Denied,
134 (Self::Initialized, OrderEventAny::Emulated(_)) => Self::Emulated, (Self::Initialized, OrderEventAny::Released(_)) => Self::Released, (Self::Initialized, OrderEventAny::Submitted(_)) => Self::Submitted,
137 (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,
149 (Self::Submitted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
150 (Self::Submitted, OrderEventAny::Rejected(_)) => Self::Rejected,
151 (Self::Submitted, OrderEventAny::Canceled(_)) => Self::Canceled, (Self::Submitted, OrderEventAny::Accepted(_)) => Self::Accepted,
153 (Self::Submitted, OrderEventAny::Filled(_)) => Self::Filled,
154 (Self::Submitted, OrderEventAny::Updated(_)) => Self::Submitted,
155 (Self::Accepted, OrderEventAny::Rejected(_)) => Self::Rejected, (Self::Accepted, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
157 (Self::Accepted, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
158 (Self::Accepted, OrderEventAny::Canceled(_)) => Self::Canceled,
159 (Self::Accepted, OrderEventAny::Triggered(_)) => Self::Triggered,
160 (Self::Accepted, OrderEventAny::Expired(_)) => Self::Expired,
161 (Self::Accepted, OrderEventAny::Filled(_)) => Self::Filled,
162 (Self::Accepted, OrderEventAny::Updated(_)) => Self::Accepted, (Self::Canceled, OrderEventAny::Filled(_)) => Self::Filled, (Self::PendingUpdate, OrderEventAny::Rejected(_)) => Self::Rejected,
165 (Self::PendingUpdate, OrderEventAny::Accepted(_)) => Self::Accepted,
166 (Self::PendingUpdate, OrderEventAny::Canceled(_)) => Self::Canceled,
167 (Self::PendingUpdate, OrderEventAny::Expired(_)) => Self::Expired,
168 (Self::PendingUpdate, OrderEventAny::Triggered(_)) => Self::Triggered,
169 (Self::PendingUpdate, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate, (Self::PendingUpdate, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
171 (Self::PendingUpdate, OrderEventAny::Filled(_)) => Self::Filled,
172 (Self::PendingCancel, OrderEventAny::Rejected(_)) => Self::Rejected,
173 (Self::PendingCancel, OrderEventAny::PendingCancel(_)) => Self::PendingCancel, (Self::PendingCancel, OrderEventAny::Canceled(_)) => Self::Canceled,
175 (Self::PendingCancel, OrderEventAny::Expired(_)) => Self::Expired,
176 (Self::PendingCancel, OrderEventAny::Accepted(_)) => Self::Accepted, (Self::PendingCancel, OrderEventAny::Filled(_)) => Self::Filled,
178 (Self::Triggered, OrderEventAny::Rejected(_)) => Self::Rejected,
179 (Self::Triggered, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
180 (Self::Triggered, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
181 (Self::Triggered, OrderEventAny::Canceled(_)) => Self::Canceled,
182 (Self::Triggered, OrderEventAny::Expired(_)) => Self::Expired,
183 (Self::Triggered, OrderEventAny::Filled(_)) => Self::Filled,
184 (Self::PartiallyFilled, OrderEventAny::PendingUpdate(_)) => Self::PendingUpdate,
185 (Self::PartiallyFilled, OrderEventAny::PendingCancel(_)) => Self::PendingCancel,
186 (Self::PartiallyFilled, OrderEventAny::Canceled(_)) => Self::Canceled,
187 (Self::PartiallyFilled, OrderEventAny::Expired(_)) => Self::Expired,
188 (Self::PartiallyFilled, OrderEventAny::Filled(_)) => Self::Filled,
189 (Self::PartiallyFilled, OrderEventAny::Accepted(_)) => Self::Accepted,
190 _ => return Err(OrderError::InvalidStateTransition),
191 };
192 Ok(new_state)
193 }
194}
195
196#[enum_dispatch]
197pub trait Order: 'static + Send {
198 fn into_any(self) -> OrderAny;
199 fn status(&self) -> OrderStatus;
200 fn trader_id(&self) -> TraderId;
201 fn strategy_id(&self) -> StrategyId;
202 fn instrument_id(&self) -> InstrumentId;
203 fn symbol(&self) -> Symbol;
204 fn venue(&self) -> Venue;
205 fn client_order_id(&self) -> ClientOrderId;
206 fn venue_order_id(&self) -> Option<VenueOrderId>;
207 fn position_id(&self) -> Option<PositionId>;
208 fn account_id(&self) -> Option<AccountId>;
209 fn last_trade_id(&self) -> Option<TradeId>;
210 fn order_side(&self) -> OrderSide;
211 fn order_type(&self) -> OrderType;
212 fn quantity(&self) -> Quantity;
213 fn time_in_force(&self) -> TimeInForce;
214 fn expire_time(&self) -> Option<UnixNanos>;
215 fn price(&self) -> Option<Price>;
216 fn trigger_price(&self) -> Option<Price>;
217 fn trigger_type(&self) -> Option<TriggerType>;
218 fn liquidity_side(&self) -> Option<LiquiditySide>;
219 fn is_post_only(&self) -> bool;
220 fn is_reduce_only(&self) -> bool;
221 fn is_quote_quantity(&self) -> bool;
222 fn display_qty(&self) -> Option<Quantity>;
223 fn limit_offset(&self) -> Option<Decimal>;
224 fn trailing_offset(&self) -> Option<Decimal>;
225 fn trailing_offset_type(&self) -> Option<TrailingOffsetType>;
226 fn emulation_trigger(&self) -> Option<TriggerType>;
227 fn trigger_instrument_id(&self) -> Option<InstrumentId>;
228 fn contingency_type(&self) -> Option<ContingencyType>;
229 fn order_list_id(&self) -> Option<OrderListId>;
230 fn linked_order_ids(&self) -> Option<&[ClientOrderId]>;
231 fn parent_order_id(&self) -> Option<ClientOrderId>;
232 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId>;
233 fn exec_algorithm_params(&self) -> Option<&IndexMap<Ustr, Ustr>>;
234 fn exec_spawn_id(&self) -> Option<ClientOrderId>;
235 fn tags(&self) -> Option<&[Ustr]>;
236 fn filled_qty(&self) -> Quantity;
237 fn leaves_qty(&self) -> Quantity;
238 fn avg_px(&self) -> Option<f64>;
239 fn slippage(&self) -> Option<f64>;
240 fn init_id(&self) -> UUID4;
241 fn ts_init(&self) -> UnixNanos;
242 fn ts_submitted(&self) -> Option<UnixNanos>;
243 fn ts_accepted(&self) -> Option<UnixNanos>;
244 fn ts_closed(&self) -> Option<UnixNanos>;
245 fn ts_last(&self) -> UnixNanos;
246
247 fn order_side_specified(&self) -> OrderSideSpecified {
248 self.order_side().as_specified()
249 }
250 fn commissions(&self) -> &IndexMap<Currency, Money>;
251
252 fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError>;
253 fn update(&mut self, event: &OrderUpdated);
254
255 fn events(&self) -> Vec<&OrderEventAny>;
256
257 fn last_event(&self) -> &OrderEventAny {
258 self.events().last().unwrap()
260 }
261
262 fn event_count(&self) -> usize {
263 self.events().len()
264 }
265
266 fn venue_order_ids(&self) -> Vec<&VenueOrderId>;
267
268 fn trade_ids(&self) -> Vec<&TradeId>;
269
270 fn has_price(&self) -> bool;
271
272 fn is_buy(&self) -> bool {
273 self.order_side() == OrderSide::Buy
274 }
275
276 fn is_sell(&self) -> bool {
277 self.order_side() == OrderSide::Sell
278 }
279
280 fn is_passive(&self) -> bool {
281 self.order_type() != OrderType::Market
282 }
283
284 fn is_aggressive(&self) -> bool {
285 self.order_type() == OrderType::Market
286 }
287
288 fn is_emulated(&self) -> bool {
289 self.status() == OrderStatus::Emulated
290 }
291
292 fn is_active_local(&self) -> bool {
293 matches!(
294 self.status(),
295 OrderStatus::Initialized | OrderStatus::Emulated | OrderStatus::Released
296 )
297 }
298
299 fn is_primary(&self) -> bool {
300 self.exec_algorithm_id().is_some()
302 && self.client_order_id() == self.exec_spawn_id().unwrap()
303 }
304
305 fn is_secondary(&self) -> bool {
306 self.exec_algorithm_id().is_some()
308 && self.client_order_id() != self.exec_spawn_id().unwrap()
309 }
310
311 fn is_contingency(&self) -> bool {
312 self.contingency_type().is_some()
313 }
314
315 fn is_parent_order(&self) -> bool {
316 match self.contingency_type() {
317 Some(c) => c == ContingencyType::Oto,
318 None => false,
319 }
320 }
321
322 fn is_child_order(&self) -> bool {
323 self.parent_order_id().is_some()
324 }
325
326 fn is_open(&self) -> bool {
327 if let Some(emulation_trigger) = self.emulation_trigger() {
328 if emulation_trigger != TriggerType::NoTrigger {
329 return false;
330 }
331 }
332
333 matches!(
334 self.status(),
335 OrderStatus::Accepted
336 | OrderStatus::Triggered
337 | OrderStatus::PendingCancel
338 | OrderStatus::PendingUpdate
339 | OrderStatus::PartiallyFilled
340 )
341 }
342
343 fn is_canceled(&self) -> bool {
344 self.status() == OrderStatus::Canceled
345 }
346
347 fn is_closed(&self) -> bool {
348 matches!(
349 self.status(),
350 OrderStatus::Denied
351 | OrderStatus::Rejected
352 | OrderStatus::Canceled
353 | OrderStatus::Expired
354 | OrderStatus::Filled
355 )
356 }
357
358 fn is_inflight(&self) -> bool {
359 if let Some(emulation_trigger) = self.emulation_trigger() {
360 if emulation_trigger != TriggerType::NoTrigger {
361 return false;
362 }
363 }
364
365 matches!(
366 self.status(),
367 OrderStatus::Submitted | OrderStatus::PendingCancel | OrderStatus::PendingUpdate
368 )
369 }
370
371 fn is_pending_update(&self) -> bool {
372 self.status() == OrderStatus::PendingUpdate
373 }
374
375 fn is_pending_cancel(&self) -> bool {
376 self.status() == OrderStatus::PendingCancel
377 }
378
379 fn is_spawned(&self) -> bool {
380 self.exec_spawn_id()
381 .is_some_and(|exec_spawn_id| exec_spawn_id != self.client_order_id())
382 }
383
384 fn to_own_book_order(&self) -> OwnBookOrder {
385 OwnBookOrder::new(
386 self.trader_id(),
387 self.client_order_id(),
388 self.venue_order_id(),
389 self.order_side().as_specified(),
390 self.price().expect("`OwnBookOrder` must have a price"), self.quantity(),
392 self.order_type(),
393 self.time_in_force(),
394 self.status(),
395 self.ts_last(),
396 self.ts_submitted().unwrap_or_default(),
397 self.ts_accepted().unwrap_or_default(),
398 self.ts_init(),
399 )
400 }
401
402 fn is_triggered(&self) -> Option<bool>; fn set_position_id(&mut self, position_id: Option<PositionId>);
404 fn set_quantity(&mut self, quantity: Quantity);
405 fn set_leaves_qty(&mut self, leaves_qty: Quantity);
406 fn set_emulation_trigger(&mut self, emulation_trigger: Option<TriggerType>);
407 fn set_is_quote_quantity(&mut self, is_quote_quantity: bool);
408 fn set_liquidity_side(&mut self, liquidity_side: LiquiditySide);
409 fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool;
410 fn previous_status(&self) -> Option<OrderStatus>;
411}
412
413impl<T> From<&T> for OrderInitialized
414where
415 T: Order,
416{
417 fn from(order: &T) -> Self {
418 Self {
419 trader_id: order.trader_id(),
420 strategy_id: order.strategy_id(),
421 instrument_id: order.instrument_id(),
422 client_order_id: order.client_order_id(),
423 order_side: order.order_side(),
424 order_type: order.order_type(),
425 quantity: order.quantity(),
426 price: order.price(),
427 trigger_price: order.trigger_price(),
428 trigger_type: order.trigger_type(),
429 time_in_force: order.time_in_force(),
430 expire_time: order.expire_time(),
431 post_only: order.is_post_only(),
432 reduce_only: order.is_reduce_only(),
433 quote_quantity: order.is_quote_quantity(),
434 display_qty: order.display_qty(),
435 limit_offset: order.limit_offset(),
436 trailing_offset: order.trailing_offset(),
437 trailing_offset_type: order.trailing_offset_type(),
438 emulation_trigger: order.emulation_trigger(),
439 trigger_instrument_id: order.trigger_instrument_id(),
440 contingency_type: order.contingency_type(),
441 order_list_id: order.order_list_id(),
442 linked_order_ids: order.linked_order_ids().map(|x| x.to_vec()),
443 parent_order_id: order.parent_order_id(),
444 exec_algorithm_id: order.exec_algorithm_id(),
445 exec_algorithm_params: order.exec_algorithm_params().map(|x| x.to_owned()),
446 exec_spawn_id: order.exec_spawn_id(),
447 tags: order.tags().map(|x| x.to_vec()),
448 event_id: order.init_id(),
449 ts_event: order.ts_init(),
450 ts_init: order.ts_init(),
451 reconciliation: false,
452 }
453 }
454}
455
456#[derive(Clone, Debug, Serialize, Deserialize)]
457pub struct OrderCore {
458 pub events: Vec<OrderEventAny>,
459 pub commissions: IndexMap<Currency, Money>,
460 pub venue_order_ids: Vec<VenueOrderId>,
461 pub trade_ids: Vec<TradeId>,
462 pub previous_status: Option<OrderStatus>,
463 pub status: OrderStatus,
464 pub trader_id: TraderId,
465 pub strategy_id: StrategyId,
466 pub instrument_id: InstrumentId,
467 pub client_order_id: ClientOrderId,
468 pub venue_order_id: Option<VenueOrderId>,
469 pub position_id: Option<PositionId>,
470 pub account_id: Option<AccountId>,
471 pub last_trade_id: Option<TradeId>,
472 pub side: OrderSide,
473 pub order_type: OrderType,
474 pub quantity: Quantity,
475 pub time_in_force: TimeInForce,
476 pub liquidity_side: Option<LiquiditySide>,
477 pub is_reduce_only: bool,
478 pub is_quote_quantity: bool,
479 pub emulation_trigger: Option<TriggerType>,
480 pub contingency_type: Option<ContingencyType>,
481 pub order_list_id: Option<OrderListId>,
482 pub linked_order_ids: Option<Vec<ClientOrderId>>,
483 pub parent_order_id: Option<ClientOrderId>,
484 pub exec_algorithm_id: Option<ExecAlgorithmId>,
485 pub exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
486 pub exec_spawn_id: Option<ClientOrderId>,
487 pub tags: Option<Vec<Ustr>>,
488 pub filled_qty: Quantity,
489 pub leaves_qty: Quantity,
490 pub avg_px: Option<f64>,
491 pub slippage: Option<f64>,
492 pub init_id: UUID4,
493 pub ts_init: UnixNanos,
494 pub ts_submitted: Option<UnixNanos>,
495 pub ts_accepted: Option<UnixNanos>,
496 pub ts_closed: Option<UnixNanos>,
497 pub ts_last: UnixNanos,
498}
499
500impl OrderCore {
501 pub fn new(init: OrderInitialized) -> Self {
503 let events: Vec<OrderEventAny> = vec![OrderEventAny::Initialized(init.clone())];
504 Self {
505 events,
506 commissions: IndexMap::new(),
507 venue_order_ids: Vec::new(),
508 trade_ids: Vec::new(),
509 previous_status: None,
510 status: OrderStatus::Initialized,
511 trader_id: init.trader_id,
512 strategy_id: init.strategy_id,
513 instrument_id: init.instrument_id,
514 client_order_id: init.client_order_id,
515 venue_order_id: None,
516 position_id: None,
517 account_id: None,
518 last_trade_id: None,
519 side: init.order_side,
520 order_type: init.order_type,
521 quantity: init.quantity,
522 time_in_force: init.time_in_force,
523 liquidity_side: Some(LiquiditySide::NoLiquiditySide),
524 is_reduce_only: init.reduce_only,
525 is_quote_quantity: init.quote_quantity,
526 emulation_trigger: init.emulation_trigger.or(Some(TriggerType::NoTrigger)),
527 contingency_type: init
528 .contingency_type
529 .or(Some(ContingencyType::NoContingency)),
530 order_list_id: init.order_list_id,
531 linked_order_ids: init.linked_order_ids,
532 parent_order_id: init.parent_order_id,
533 exec_algorithm_id: init.exec_algorithm_id,
534 exec_algorithm_params: init.exec_algorithm_params,
535 exec_spawn_id: init.exec_spawn_id,
536 tags: init.tags,
537 filled_qty: Quantity::zero(init.quantity.precision),
538 leaves_qty: init.quantity,
539 avg_px: None,
540 slippage: None,
541 init_id: init.event_id,
542 ts_init: init.ts_event,
543 ts_submitted: None,
544 ts_accepted: None,
545 ts_closed: None,
546 ts_last: init.ts_event,
547 }
548 }
549
550 pub fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError> {
551 assert_eq!(self.client_order_id, event.client_order_id());
552 assert_eq!(self.strategy_id, event.strategy_id());
553
554 let new_status = self.status.transition(&event)?;
555 self.previous_status = Some(self.status);
556 self.status = new_status;
557
558 match &event {
559 OrderEventAny::Initialized(_) => return Err(OrderError::AlreadyInitialized),
560 OrderEventAny::Denied(event) => self.denied(event),
561 OrderEventAny::Emulated(event) => self.emulated(event),
562 OrderEventAny::Released(event) => self.released(event),
563 OrderEventAny::Submitted(event) => self.submitted(event),
564 OrderEventAny::Rejected(event) => self.rejected(event),
565 OrderEventAny::Accepted(event) => self.accepted(event),
566 OrderEventAny::PendingUpdate(event) => self.pending_update(event),
567 OrderEventAny::PendingCancel(event) => self.pending_cancel(event),
568 OrderEventAny::ModifyRejected(event) => self.modify_rejected(event),
569 OrderEventAny::CancelRejected(event) => self.cancel_rejected(event),
570 OrderEventAny::Updated(event) => self.updated(event),
571 OrderEventAny::Triggered(event) => self.triggered(event),
572 OrderEventAny::Canceled(event) => self.canceled(event),
573 OrderEventAny::Expired(event) => self.expired(event),
574 OrderEventAny::Filled(event) => self.filled(event),
575 }
576
577 self.ts_last = event.ts_event();
578 self.events.push(event);
579 Ok(())
580 }
581
582 fn denied(&mut self, event: &OrderDenied) {
583 self.ts_closed = Some(event.ts_event);
584 }
585
586 fn emulated(&self, _event: &OrderEmulated) {
587 }
589
590 fn released(&mut self, _event: &OrderReleased) {
591 self.emulation_trigger = None;
592 }
593
594 fn submitted(&mut self, event: &OrderSubmitted) {
595 self.account_id = Some(event.account_id);
596 self.ts_submitted = Some(event.ts_event);
597 }
598
599 fn accepted(&mut self, event: &OrderAccepted) {
600 self.venue_order_id = Some(event.venue_order_id);
601 self.ts_accepted = Some(event.ts_event);
602 }
603
604 fn rejected(&mut self, event: &OrderRejected) {
605 self.ts_closed = Some(event.ts_event);
606 }
607
608 fn pending_update(&self, _event: &OrderPendingUpdate) {
609 }
611
612 fn pending_cancel(&self, _event: &OrderPendingCancel) {
613 }
615
616 fn modify_rejected(&mut self, _event: &OrderModifyRejected) {
617 self.status = self
618 .previous_status
619 .unwrap_or_else(|| panic!("{}", OrderError::NoPreviousState));
620 }
621
622 fn cancel_rejected(&mut self, _event: &OrderCancelRejected) {
623 self.status = self
624 .previous_status
625 .unwrap_or_else(|| panic!("{}", OrderError::NoPreviousState));
626 }
627
628 fn triggered(&mut self, _event: &OrderTriggered) {}
629
630 fn canceled(&mut self, event: &OrderCanceled) {
631 self.ts_closed = Some(event.ts_event);
632 }
633
634 fn expired(&mut self, event: &OrderExpired) {
635 self.ts_closed = Some(event.ts_event);
636 }
637
638 fn updated(&mut self, event: &OrderUpdated) {
639 if let Some(venue_order_id) = &event.venue_order_id {
640 if self.venue_order_id.is_none()
641 || venue_order_id != self.venue_order_id.as_ref().unwrap()
642 {
643 self.venue_order_id = Some(*venue_order_id);
644 self.venue_order_ids.push(*venue_order_id);
645 }
646 }
647 }
648
649 fn filled(&mut self, event: &OrderFilled) {
650 if self.filled_qty + event.last_qty < self.quantity {
651 self.status = OrderStatus::PartiallyFilled;
652 } else {
653 self.status = OrderStatus::Filled;
654 self.ts_closed = Some(event.ts_event);
655 }
656
657 self.venue_order_id = Some(event.venue_order_id);
658 self.position_id = event.position_id;
659 self.trade_ids.push(event.trade_id);
660 self.last_trade_id = Some(event.trade_id);
661 self.liquidity_side = Some(event.liquidity_side);
662 self.filled_qty += event.last_qty;
663 self.leaves_qty -= event.last_qty;
664 self.ts_last = event.ts_event;
665 if self.ts_accepted.is_none() {
666 self.ts_accepted = Some(event.ts_event);
668 }
669
670 self.set_avg_px(event.last_qty, event.last_px);
671 }
672
673 fn set_avg_px(&mut self, last_qty: Quantity, last_px: Price) {
674 if self.avg_px.is_none() {
675 self.avg_px = Some(last_px.as_f64());
676 }
677
678 let filled_qty = self.filled_qty.as_f64();
679 let total_qty = filled_qty + last_qty.as_f64();
680
681 let avg_px = self
682 .avg_px
683 .unwrap()
684 .mul_add(filled_qty, last_px.as_f64() * last_qty.as_f64())
685 / total_qty;
686 self.avg_px = Some(avg_px);
687 }
688
689 pub fn set_slippage(&mut self, price: Price) {
690 self.slippage = self.avg_px.and_then(|avg_px| {
691 let current_price = price.as_f64();
692 match self.side {
693 OrderSide::Buy if avg_px > current_price => Some(avg_px - current_price),
694 OrderSide::Sell if avg_px < current_price => Some(current_price - avg_px),
695 _ => None,
696 }
697 });
698 }
699
700 #[must_use]
701 pub fn opposite_side(side: OrderSide) -> OrderSide {
702 match side {
703 OrderSide::Buy => OrderSide::Sell,
704 OrderSide::Sell => OrderSide::Buy,
705 OrderSide::NoOrderSide => OrderSide::NoOrderSide,
706 }
707 }
708
709 #[must_use]
710 pub fn closing_side(side: PositionSide) -> OrderSide {
711 match side {
712 PositionSide::Long => OrderSide::Sell,
713 PositionSide::Short => OrderSide::Buy,
714 PositionSide::Flat => OrderSide::NoOrderSide,
715 PositionSide::NoPositionSide => OrderSide::NoOrderSide,
716 }
717 }
718
719 #[must_use]
720 pub fn signed_decimal_qty(&self) -> Decimal {
721 match self.side {
722 OrderSide::Buy => self.quantity.as_decimal(),
723 OrderSide::Sell => -self.quantity.as_decimal(),
724 _ => panic!("Invalid order side"),
725 }
726 }
727
728 #[must_use]
729 pub fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool {
730 if side == PositionSide::Flat {
731 return false;
732 }
733
734 match (self.side, side) {
735 (OrderSide::Buy, PositionSide::Long) => false,
736 (OrderSide::Buy, PositionSide::Short) => self.leaves_qty <= position_qty,
737 (OrderSide::Sell, PositionSide::Short) => false,
738 (OrderSide::Sell, PositionSide::Long) => self.leaves_qty <= position_qty,
739 _ => true,
740 }
741 }
742
743 #[must_use]
744 pub fn commission(&self, currency: &Currency) -> Option<Money> {
745 self.commissions.get(currency).copied()
746 }
747
748 #[must_use]
749 pub fn commissions(&self) -> IndexMap<Currency, Money> {
750 self.commissions.clone()
751 }
752
753 #[must_use]
754 pub fn commissions_vec(&self) -> Vec<Money> {
755 self.commissions.values().cloned().collect()
756 }
757
758 #[must_use]
759 pub fn init_event(&self) -> Option<OrderEventAny> {
760 self.events.first().cloned()
761 }
762}
763
764#[cfg(test)]
768mod tests {
769 use rstest::rstest;
770 use rust_decimal_macros::dec;
771
772 use super::*;
773 use crate::{
774 enums::{OrderSide, OrderStatus, PositionSide},
775 events::order::{
776 accepted::OrderAcceptedBuilder, denied::OrderDeniedBuilder, filled::OrderFilledBuilder,
777 initialized::OrderInitializedBuilder, submitted::OrderSubmittedBuilder,
778 },
779 orders::MarketOrder,
780 };
781
782 #[rstest]
793 #[case(OrderSide::Buy, OrderSide::Sell)]
794 #[case(OrderSide::Sell, OrderSide::Buy)]
795 #[case(OrderSide::NoOrderSide, OrderSide::NoOrderSide)]
796 fn test_order_opposite_side(#[case] order_side: OrderSide, #[case] expected_side: OrderSide) {
797 let result = OrderCore::opposite_side(order_side);
798 assert_eq!(result, expected_side);
799 }
800
801 #[rstest]
802 #[case(PositionSide::Long, OrderSide::Sell)]
803 #[case(PositionSide::Short, OrderSide::Buy)]
804 #[case(PositionSide::NoPositionSide, OrderSide::NoOrderSide)]
805 fn test_closing_side(#[case] position_side: PositionSide, #[case] expected_side: OrderSide) {
806 let result = OrderCore::closing_side(position_side);
807 assert_eq!(result, expected_side);
808 }
809
810 #[rstest]
811 #[case(OrderSide::Buy, dec!(10_000))]
812 #[case(OrderSide::Sell, dec!(-10_000))]
813 fn test_signed_decimal_qty(#[case] order_side: OrderSide, #[case] expected: Decimal) {
814 let order: MarketOrder = OrderInitializedBuilder::default()
815 .order_side(order_side)
816 .quantity(Quantity::from(10_000))
817 .build()
818 .unwrap()
819 .into();
820
821 let result = order.signed_decimal_qty();
822 assert_eq!(result, expected);
823 }
824
825 #[rustfmt::skip]
826 #[rstest]
827 #[case(OrderSide::Buy, Quantity::from(100), PositionSide::Long, Quantity::from(50), false)]
828 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(50), true)]
829 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Short, Quantity::from(100), true)]
830 #[case(OrderSide::Buy, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
831 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Flat, Quantity::from(0), false)]
832 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(50), true)]
833 #[case(OrderSide::Sell, Quantity::from(50), PositionSide::Long, Quantity::from(100), true)]
834 #[case(OrderSide::Sell, Quantity::from(100), PositionSide::Short, Quantity::from(50), false)]
835 fn test_would_reduce_only(
836 #[case] order_side: OrderSide,
837 #[case] order_qty: Quantity,
838 #[case] position_side: PositionSide,
839 #[case] position_qty: Quantity,
840 #[case] expected: bool,
841 ) {
842 let order: MarketOrder = OrderInitializedBuilder::default()
843 .order_side(order_side)
844 .quantity(order_qty)
845 .build()
846 .unwrap()
847 .into();
848
849 assert_eq!(
850 order.would_reduce_only(position_side, position_qty),
851 expected
852 );
853 }
854
855 #[rstest]
856 fn test_order_state_transition_denied() {
857 let mut order: MarketOrder = OrderInitializedBuilder::default().build().unwrap().into();
858 let denied = OrderDeniedBuilder::default().build().unwrap();
859 let event = OrderEventAny::Denied(denied);
860
861 order.apply(event.clone()).unwrap();
862
863 assert_eq!(order.status, OrderStatus::Denied);
864 assert!(order.is_closed());
865 assert!(!order.is_open());
866 assert_eq!(order.event_count(), 2);
867 assert_eq!(order.last_event(), &event);
868 }
869
870 #[rstest]
871 fn test_order_life_cycle_to_filled() {
872 let init = OrderInitializedBuilder::default().build().unwrap();
873 let submitted = OrderSubmittedBuilder::default().build().unwrap();
874 let accepted = OrderAcceptedBuilder::default().build().unwrap();
875 let filled = OrderFilledBuilder::default().build().unwrap();
876
877 let mut order: MarketOrder = init.clone().into();
878 order.apply(OrderEventAny::Submitted(submitted)).unwrap();
879 order.apply(OrderEventAny::Accepted(accepted)).unwrap();
880 order.apply(OrderEventAny::Filled(filled)).unwrap();
881
882 assert_eq!(order.client_order_id, init.client_order_id);
883 assert_eq!(order.status(), OrderStatus::Filled);
884 assert_eq!(order.filled_qty(), Quantity::from(100_000));
885 assert_eq!(order.leaves_qty(), Quantity::from(0));
886 assert_eq!(order.avg_px(), Some(1.0));
887 assert!(!order.is_open());
888 assert!(order.is_closed());
889 assert_eq!(order.commission(&Currency::USD()), None);
890 assert_eq!(order.commissions(), &IndexMap::new());
891 }
892}