nautilus_model/orders/
base.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use 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,  // Emulated orders
97            (Self::Initialized, OrderEventAny::Released(_)) => Self::Released,  // Emulated orders
98            (Self::Initialized, OrderEventAny::Submitted(_)) => Self::Submitted,
99            (Self::Initialized, OrderEventAny::Rejected(_)) => Self::Rejected,  // External orders
100            (Self::Initialized, OrderEventAny::Accepted(_)) => Self::Accepted,  // External orders
101            (Self::Initialized, OrderEventAny::Canceled(_)) => Self::Canceled,  // External orders
102            (Self::Initialized, OrderEventAny::Expired(_)) => Self::Expired,  // External orders
103            (Self::Initialized, OrderEventAny::Triggered(_)) => Self::Triggered, // External orders
104            (Self::Emulated, OrderEventAny::Canceled(_)) => Self::Canceled,  // Emulated orders
105            (Self::Emulated, OrderEventAny::Expired(_)) => Self::Expired,  // Emulated orders
106            (Self::Emulated, OrderEventAny::Released(_)) => Self::Released,  // Emulated orders
107            (Self::Released, OrderEventAny::Submitted(_)) => Self::Submitted,  // Emulated orders
108            (Self::Released, OrderEventAny::Denied(_)) => Self::Denied,  // Emulated orders
109            (Self::Released, OrderEventAny::Canceled(_)) => Self::Canceled,  // Execution algo
110            (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,  // FOK and IOC cases
114            (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,  // StopLimit order
118            (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,  // Real world possibility
126            (Self::Canceled, OrderEventAny::Filled(_)) => Self::Filled,  // Real world possibility
127            (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,  // Allow multiple requests
133            (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,  // Allow multiple requests
138            (Self::PendingCancel, OrderEventAny::Canceled(_)) => Self::Canceled,
139            (Self::PendingCancel, OrderEventAny::Expired(_)) => Self::Expired,
140            (Self::PendingCancel, OrderEventAny::Accepted(_)) => Self::Accepted,  // Allow failed cancel requests
141            (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        // SAFETY: Unwrap safe as `Order` specification guarantees at least one event (`OrderInitialized`)
215        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        // TODO: Guarantee `exec_spawn_id` is some if `exec_algorithm_id` is some
255        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        // TODO: Guarantee `exec_spawn_id` is some if `exec_algorithm_id` is some
261        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    /// Creates a new [`OrderCore`] instance.
441    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        // Do nothing else
521    }
522
523    fn emulated(&self, _event: &OrderEmulated) {
524        // Do nothing else
525    }
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        // Do nothing else
541    }
542
543    fn pending_update(&self, _event: &OrderPendingUpdate) {
544        // Do nothing else
545    }
546
547    fn pending_cancel(&self, _event: &OrderPendingCancel) {
548        // Do nothing else
549    }
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////////////////////////////////////////////////////////////////////////////////
684// Tests
685////////////////////////////////////////////////////////////////////////////////
686#[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}