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