1use std::fmt::{Debug, Display};
17
18use derive_builder::Builder;
19use nautilus_core::{UUID4, UnixNanos};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24use crate::{
25 enums::{
26 ContingencyType, LiquiditySide, OrderSide, OrderSideSpecified, OrderType, TimeInForce,
27 TrailingOffsetType, TriggerType,
28 },
29 events::OrderEvent,
30 identifiers::{
31 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
32 StrategyId, TradeId, TraderId, VenueOrderId,
33 },
34 types::{Currency, Money, Price, Quantity},
35};
36
37#[repr(C)]
38#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Builder)]
39#[builder(default)]
40#[serde(tag = "type")]
41#[cfg_attr(
42 feature = "python",
43 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
44)]
45pub struct OrderFilled {
46 pub trader_id: TraderId,
48 pub strategy_id: StrategyId,
50 pub instrument_id: InstrumentId,
52 pub client_order_id: ClientOrderId,
54 pub venue_order_id: VenueOrderId,
55 pub account_id: AccountId,
57 pub trade_id: TradeId,
59 pub order_side: OrderSide,
61 pub order_type: OrderType,
63 pub last_qty: Quantity,
65 pub last_px: Price,
67 pub currency: Currency,
69 pub liquidity_side: LiquiditySide,
71 pub event_id: UUID4,
73 pub ts_event: UnixNanos,
75 pub ts_init: UnixNanos,
77 pub reconciliation: bool,
79 pub position_id: Option<PositionId>,
81 pub commission: Option<Money>,
83}
84
85impl OrderFilled {
86 #[allow(clippy::too_many_arguments)]
88 pub fn new(
89 trader_id: TraderId,
90 strategy_id: StrategyId,
91 instrument_id: InstrumentId,
92 client_order_id: ClientOrderId,
93 venue_order_id: VenueOrderId,
94 account_id: AccountId,
95 trade_id: TradeId,
96 order_side: OrderSide,
97 order_type: OrderType,
98 last_qty: Quantity,
99 last_px: Price,
100 currency: Currency,
101 liquidity_side: LiquiditySide,
102 event_id: UUID4,
103 ts_event: UnixNanos,
104 ts_init: UnixNanos,
105 reconciliation: bool,
106 position_id: Option<PositionId>,
107 commission: Option<Money>,
108 ) -> Self {
109 Self {
110 trader_id,
111 strategy_id,
112 instrument_id,
113 client_order_id,
114 venue_order_id,
115 account_id,
116 trade_id,
117 order_side,
118 order_type,
119 last_qty,
120 last_px,
121 currency,
122 liquidity_side,
123 event_id,
124 ts_event,
125 ts_init,
126 reconciliation,
127 position_id,
128 commission,
129 }
130 }
131
132 #[must_use]
133 pub fn specified_side(&self) -> OrderSideSpecified {
134 self.order_side.as_specified()
135 }
136
137 #[must_use]
138 pub fn is_buy(&self) -> bool {
139 self.order_side == OrderSide::Buy
140 }
141
142 #[must_use]
143 pub fn is_sell(&self) -> bool {
144 self.order_side == OrderSide::Sell
145 }
146}
147
148impl Default for OrderFilled {
149 fn default() -> Self {
151 Self {
152 trader_id: TraderId::default(),
153 strategy_id: StrategyId::default(),
154 instrument_id: InstrumentId::default(),
155 client_order_id: ClientOrderId::default(),
156 venue_order_id: VenueOrderId::default(),
157 account_id: AccountId::default(),
158 trade_id: TradeId::default(),
159 position_id: None,
160 order_side: OrderSide::Buy,
161 order_type: OrderType::Market,
162 last_qty: Quantity::new(100_000.0, 0),
163 last_px: Price::from("1.00000"),
164 currency: Currency::USD(),
165 commission: None,
166 liquidity_side: LiquiditySide::Taker,
167 event_id: Default::default(),
168 ts_event: Default::default(),
169 ts_init: Default::default(),
170 reconciliation: Default::default(),
171 }
172 }
173}
174
175impl Debug for OrderFilled {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 let position_id_str = match self.position_id {
178 Some(position_id) => position_id.to_string(),
179 None => "None".to_string(),
180 };
181 let commission_str = match self.commission {
182 Some(commission) => commission.to_string(),
183 None => "None".to_string(),
184 };
185 write!(
186 f,
187 "{}(\
188 trader_id={}, \
189 strategy_id={}, \
190 instrument_id={}, \
191 client_order_id={}, \
192 venue_order_id={}, \
193 account_id={}, \
194 trade_id={}, \
195 position_id={}, \
196 order_side={}, \
197 order_type={}, \
198 last_qty={}, \
199 last_px={} {}, \
200 commission={}, \
201 liquidity_side={}, \
202 event_id={}, \
203 ts_event={}, \
204 ts_init={})",
205 stringify!(OrderFilled),
206 self.trader_id,
207 self.strategy_id,
208 self.instrument_id,
209 self.client_order_id,
210 self.venue_order_id,
211 self.account_id,
212 self.trade_id,
213 position_id_str,
214 self.order_side,
215 self.order_type,
216 self.last_qty.to_formatted_string(),
217 self.last_px.to_formatted_string(),
218 self.currency,
219 commission_str,
220 self.liquidity_side,
221 self.event_id,
222 self.ts_event,
223 self.ts_init
224 )
225 }
226}
227
228impl Display for OrderFilled {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 write!(
231 f,
232 "{}(\
233 instrument_id={}, \
234 client_order_id={}, \
235 venue_order_id={}, \
236 account_id={}, \
237 trade_id={}, \
238 position_id={}, \
239 order_side={}, \
240 order_type={}, \
241 last_qty={}, \
242 last_px={} {}, \
243 commission={}, \
244 liquidity_side={}, \
245 ts_event={})",
246 stringify!(OrderFilled),
247 self.instrument_id,
248 self.client_order_id,
249 self.venue_order_id,
250 self.account_id,
251 self.trade_id,
252 self.position_id.unwrap_or_default(),
253 self.order_side,
254 self.order_type,
255 self.last_qty.to_formatted_string(),
256 self.last_px.to_formatted_string(),
257 self.currency,
258 self.commission.unwrap_or(Money::from("0.0 USD")),
259 self.liquidity_side,
260 self.ts_event
261 )
262 }
263}
264
265impl OrderEvent for OrderFilled {
266 fn id(&self) -> UUID4 {
267 self.event_id
268 }
269
270 fn kind(&self) -> &str {
271 stringify!(OrderFilled)
272 }
273
274 fn order_type(&self) -> Option<OrderType> {
275 Some(self.order_type)
276 }
277
278 fn order_side(&self) -> Option<OrderSide> {
279 Some(self.order_side)
280 }
281
282 fn trader_id(&self) -> TraderId {
283 self.trader_id
284 }
285
286 fn strategy_id(&self) -> StrategyId {
287 self.strategy_id
288 }
289
290 fn instrument_id(&self) -> InstrumentId {
291 self.instrument_id
292 }
293
294 fn trade_id(&self) -> Option<TradeId> {
295 Some(self.trade_id)
296 }
297
298 fn currency(&self) -> Option<Currency> {
299 Some(self.currency)
300 }
301
302 fn client_order_id(&self) -> ClientOrderId {
303 self.client_order_id
304 }
305
306 fn reason(&self) -> Option<Ustr> {
307 None
308 }
309
310 fn quantity(&self) -> Option<Quantity> {
311 Some(self.last_qty)
312 }
313
314 fn time_in_force(&self) -> Option<TimeInForce> {
315 None
316 }
317
318 fn liquidity_side(&self) -> Option<LiquiditySide> {
319 Some(self.liquidity_side)
320 }
321
322 fn post_only(&self) -> Option<bool> {
323 None
324 }
325
326 fn reduce_only(&self) -> Option<bool> {
327 None
328 }
329
330 fn quote_quantity(&self) -> Option<bool> {
331 None
332 }
333
334 fn reconciliation(&self) -> bool {
335 self.reconciliation
336 }
337
338 fn price(&self) -> Option<Price> {
339 None
340 }
341
342 fn last_px(&self) -> Option<Price> {
343 Some(self.last_px)
344 }
345
346 fn last_qty(&self) -> Option<Quantity> {
347 Some(self.last_qty)
348 }
349
350 fn trigger_price(&self) -> Option<Price> {
351 None
352 }
353
354 fn trigger_type(&self) -> Option<TriggerType> {
355 None
356 }
357
358 fn limit_offset(&self) -> Option<Decimal> {
359 None
360 }
361
362 fn trailing_offset(&self) -> Option<Decimal> {
363 None
364 }
365
366 fn trailing_offset_type(&self) -> Option<TrailingOffsetType> {
367 None
368 }
369
370 fn expire_time(&self) -> Option<UnixNanos> {
371 None
372 }
373
374 fn display_qty(&self) -> Option<Quantity> {
375 None
376 }
377
378 fn emulation_trigger(&self) -> Option<TriggerType> {
379 None
380 }
381
382 fn trigger_instrument_id(&self) -> Option<InstrumentId> {
383 None
384 }
385
386 fn contingency_type(&self) -> Option<ContingencyType> {
387 None
388 }
389
390 fn order_list_id(&self) -> Option<OrderListId> {
391 None
392 }
393
394 fn linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
395 None
396 }
397
398 fn parent_order_id(&self) -> Option<ClientOrderId> {
399 None
400 }
401
402 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
403 None
404 }
405
406 fn exec_spawn_id(&self) -> Option<ClientOrderId> {
407 None
408 }
409
410 fn venue_order_id(&self) -> Option<VenueOrderId> {
411 Some(self.venue_order_id)
412 }
413
414 fn account_id(&self) -> Option<AccountId> {
415 Some(self.account_id)
416 }
417
418 fn position_id(&self) -> Option<PositionId> {
419 self.position_id
420 }
421
422 fn commission(&self) -> Option<Money> {
423 self.commission
424 }
425
426 fn ts_event(&self) -> UnixNanos {
427 self.ts_event
428 }
429
430 fn ts_init(&self) -> UnixNanos {
431 self.ts_init
432 }
433}
434
435#[cfg(test)]
439mod tests {
440 use nautilus_core::UnixNanos;
441 use rstest::rstest;
442
443 use super::*;
444 use crate::{
445 enums::{LiquiditySide, OrderSide, OrderSideSpecified, OrderType},
446 events::order::stubs::*,
447 identifiers::{
448 AccountId, ClientOrderId, InstrumentId, PositionId, StrategyId, TradeId, TraderId,
449 VenueOrderId,
450 },
451 types::{Currency, Money, Price, Quantity},
452 };
453
454 fn create_test_order_filled() -> OrderFilled {
455 OrderFilled::new(
456 TraderId::from("TRADER-001"),
457 StrategyId::from("EMA-CROSS"),
458 InstrumentId::from("EURUSD.SIM"),
459 ClientOrderId::from("O-19700101-000000-001-001-1"),
460 VenueOrderId::from("V-001"),
461 AccountId::from("SIM-001"),
462 TradeId::from("T-001"),
463 OrderSide::Buy,
464 OrderType::Market,
465 Quantity::from("100"),
466 Price::from("1.0500"),
467 Currency::USD(),
468 LiquiditySide::Taker,
469 Default::default(),
470 UnixNanos::from(1_000_000_000),
471 UnixNanos::from(2_000_000_000),
472 false,
473 Some(PositionId::from("P-001")),
474 Some(Money::new(2.5, Currency::USD())),
475 )
476 }
477
478 #[rstest]
479 fn test_order_filled_new() {
480 let order_filled = create_test_order_filled();
481
482 assert_eq!(order_filled.trader_id, TraderId::from("TRADER-001"));
483 assert_eq!(order_filled.strategy_id, StrategyId::from("EMA-CROSS"));
484 assert_eq!(order_filled.instrument_id, InstrumentId::from("EURUSD.SIM"));
485 assert_eq!(
486 order_filled.client_order_id,
487 ClientOrderId::from("O-19700101-000000-001-001-1")
488 );
489 assert_eq!(order_filled.venue_order_id, VenueOrderId::from("V-001"));
490 assert_eq!(order_filled.account_id, AccountId::from("SIM-001"));
491 assert_eq!(order_filled.trade_id, TradeId::from("T-001"));
492 assert_eq!(order_filled.order_side, OrderSide::Buy);
493 assert_eq!(order_filled.order_type, OrderType::Market);
494 assert_eq!(order_filled.last_qty, Quantity::from("100"));
495 assert_eq!(order_filled.last_px, Price::from("1.0500"));
496 assert_eq!(order_filled.currency, Currency::USD());
497 assert_eq!(order_filled.liquidity_side, LiquiditySide::Taker);
498 assert_eq!(order_filled.position_id, Some(PositionId::from("P-001")));
499 assert_eq!(
500 order_filled.commission,
501 Some(Money::new(2.5, Currency::USD()))
502 );
503 assert!(!order_filled.reconciliation);
504 }
505
506 #[rstest]
507 fn test_order_filled_display(order_filled: OrderFilled) {
508 let display = format!("{order_filled}");
509 assert_eq!(
510 display,
511 "OrderFilled(instrument_id=BTCUSDT.COINBASE, client_order_id=O-19700101-000000-001-001-1, \
512 venue_order_id=123456, account_id=SIM-001, trade_id=1, position_id=P-001, \
513 order_side=BUY, order_type=LIMIT, last_qty=0.561, last_px=22_000 USDT, \
514 commission=12.20000000 USDT, liquidity_side=TAKER, ts_event=0)"
515 );
516 }
517
518 #[rstest]
519 fn test_order_filled_is_buy(order_filled: OrderFilled) {
520 assert!(order_filled.is_buy());
521 assert!(!order_filled.is_sell());
522 }
523
524 #[rstest]
525 fn test_order_filled_is_sell() {
526 let mut order_filled = create_test_order_filled();
527 order_filled.order_side = OrderSide::Sell;
528
529 assert!(order_filled.is_sell());
530 assert!(!order_filled.is_buy());
531 }
532
533 #[rstest]
534 fn test_order_filled_specified_side() {
535 let buy_order = create_test_order_filled();
536 assert_eq!(buy_order.specified_side(), OrderSideSpecified::Buy);
537
538 let mut sell_order = create_test_order_filled();
539 sell_order.order_side = OrderSide::Sell;
540 assert_eq!(sell_order.specified_side(), OrderSideSpecified::Sell);
541 }
542
543 #[rstest]
544 fn test_order_filled_default() {
545 let order_filled = OrderFilled::default();
546
547 assert_eq!(order_filled.trader_id, TraderId::default());
548 assert_eq!(order_filled.strategy_id, StrategyId::default());
549 assert_eq!(order_filled.instrument_id, InstrumentId::default());
550 assert_eq!(order_filled.client_order_id, ClientOrderId::default());
551 assert_eq!(order_filled.venue_order_id, VenueOrderId::default());
552 assert_eq!(order_filled.account_id, AccountId::default());
553 assert_eq!(order_filled.trade_id, TradeId::default());
554 assert_eq!(order_filled.order_side, OrderSide::Buy);
555 assert_eq!(order_filled.order_type, OrderType::Market);
556 assert_eq!(order_filled.currency, Currency::USD());
557 assert_eq!(order_filled.liquidity_side, LiquiditySide::Taker);
558 assert_eq!(order_filled.position_id, None);
559 assert_eq!(order_filled.commission, None);
560 assert!(!order_filled.reconciliation);
561 }
562
563 #[rstest]
564 fn test_order_filled_order_event_trait() {
565 let order_filled = create_test_order_filled();
566
567 assert_eq!(order_filled.id(), order_filled.event_id);
568 assert_eq!(order_filled.kind(), "OrderFilled");
569 assert_eq!(order_filled.order_type(), Some(OrderType::Market));
570 assert_eq!(order_filled.order_side(), Some(OrderSide::Buy));
571 assert_eq!(order_filled.trader_id(), TraderId::from("TRADER-001"));
572 assert_eq!(order_filled.strategy_id(), StrategyId::from("EMA-CROSS"));
573 assert_eq!(
574 order_filled.instrument_id(),
575 InstrumentId::from("EURUSD.SIM")
576 );
577 assert_eq!(order_filled.trade_id(), Some(TradeId::from("T-001")));
578 assert_eq!(order_filled.currency(), Some(Currency::USD()));
579 assert_eq!(
580 order_filled.client_order_id(),
581 ClientOrderId::from("O-19700101-000000-001-001-1")
582 );
583 assert_eq!(order_filled.reason(), None);
584 assert_eq!(order_filled.quantity(), Some(Quantity::from("100")));
585 assert_eq!(order_filled.liquidity_side(), Some(LiquiditySide::Taker));
586 assert!(!order_filled.reconciliation());
587 assert_eq!(
588 order_filled.venue_order_id(),
589 Some(VenueOrderId::from("V-001"))
590 );
591 assert_eq!(order_filled.account_id(), Some(AccountId::from("SIM-001")));
592 assert_eq!(order_filled.position_id(), Some(PositionId::from("P-001")));
593 assert_eq!(
594 order_filled.commission(),
595 Some(Money::new(2.5, Currency::USD()))
596 );
597 assert_eq!(order_filled.last_px(), Some(Price::from("1.0500")));
598 assert_eq!(order_filled.last_qty(), Some(Quantity::from("100")));
599 }
600
601 #[rstest]
602 fn test_order_filled_different_order_types() {
603 let mut market_order = create_test_order_filled();
604 market_order.order_type = OrderType::Market;
605
606 let mut limit_order = create_test_order_filled();
607 limit_order.order_type = OrderType::Limit;
608
609 let mut stop_order = create_test_order_filled();
610 stop_order.order_type = OrderType::StopMarket;
611
612 assert_ne!(market_order, limit_order);
613 assert_ne!(limit_order, stop_order);
614 assert_eq!(market_order.order_type, OrderType::Market);
615 assert_eq!(limit_order.order_type, OrderType::Limit);
616 assert_eq!(stop_order.order_type, OrderType::StopMarket);
617 }
618
619 #[rstest]
620 fn test_order_filled_different_liquidity_sides() {
621 let mut taker = create_test_order_filled();
622 taker.liquidity_side = LiquiditySide::Taker;
623
624 let mut maker = create_test_order_filled();
625 maker.liquidity_side = LiquiditySide::Maker;
626
627 assert_ne!(taker, maker);
628 assert_eq!(taker.liquidity_side, LiquiditySide::Taker);
629 assert_eq!(maker.liquidity_side, LiquiditySide::Maker);
630 }
631
632 #[rstest]
633 fn test_order_filled_without_position_id() {
634 let mut order_filled = create_test_order_filled();
635 order_filled.position_id = None;
636
637 assert!(order_filled.position_id.is_none());
638 }
639
640 #[rstest]
641 fn test_order_filled_without_commission() {
642 let mut order_filled = create_test_order_filled();
643 order_filled.commission = None;
644
645 assert!(order_filled.commission.is_none());
646 }
647
648 #[rstest]
649 fn test_order_filled_with_reconciliation() {
650 let mut order_filled = create_test_order_filled();
651 order_filled.reconciliation = true;
652
653 assert!(order_filled.reconciliation);
654 }
655
656 #[rstest]
657 fn test_order_filled_clone() {
658 let order_filled1 = create_test_order_filled();
659 let order_filled2 = order_filled1;
660
661 assert_eq!(order_filled1, order_filled2);
662 }
663
664 #[rstest]
665 fn test_order_filled_debug() {
666 let order_filled = create_test_order_filled();
667 let debug_str = format!("{order_filled:?}");
668
669 assert!(debug_str.contains("OrderFilled"));
670 assert!(debug_str.contains("TRADER-001"));
671 assert!(debug_str.contains("EMA-CROSS"));
672 assert!(debug_str.contains("EURUSD.SIM"));
673 assert!(debug_str.contains("P-001"));
674 }
675
676 #[rstest]
677 fn test_order_filled_partial_eq() {
678 let order_filled1 = create_test_order_filled();
679 let mut order_filled2 = create_test_order_filled();
680 order_filled2.event_id = order_filled1.event_id; let mut order_filled3 = create_test_order_filled();
682 order_filled3.trade_id = TradeId::from("T-002");
683
684 assert_eq!(order_filled1, order_filled2);
685 assert_ne!(order_filled1, order_filled3);
686 }
687
688 #[rstest]
689 fn test_order_filled_timestamps() {
690 let order_filled = create_test_order_filled();
691
692 assert_eq!(order_filled.ts_event, UnixNanos::from(1_000_000_000));
693 assert_eq!(order_filled.ts_init, UnixNanos::from(2_000_000_000));
694 assert!(order_filled.ts_event < order_filled.ts_init);
695 }
696
697 #[rstest]
698 fn test_order_filled_different_currencies() {
699 let mut usd_fill = create_test_order_filled();
700 usd_fill.currency = Currency::USD();
701
702 let mut eur_fill = create_test_order_filled();
703 eur_fill.currency = Currency::EUR();
704
705 assert_ne!(usd_fill, eur_fill);
706 assert_eq!(usd_fill.currency, Currency::USD());
707 assert_eq!(eur_fill.currency, Currency::EUR());
708 }
709
710 #[rstest]
711 fn test_order_filled_different_prices_and_quantities() {
712 let mut large_fill = create_test_order_filled();
713 large_fill.last_qty = Quantity::from("1000");
714 large_fill.last_px = Price::from("1.1000");
715
716 let mut small_fill = create_test_order_filled();
717 small_fill.last_qty = Quantity::from("100");
718 small_fill.last_px = Price::from("1.0500");
719
720 assert_ne!(large_fill, small_fill);
721 assert_eq!(large_fill.last_qty, Quantity::from("1000"));
722 assert_eq!(large_fill.last_px, Price::from("1.1000"));
723 assert_eq!(small_fill.last_qty, Quantity::from("100"));
724 assert_eq!(small_fill.last_px, Price::from("1.0500"));
725 }
726
727 #[rstest]
728 fn test_order_filled_serialization() {
729 let original = create_test_order_filled();
730
731 let json = serde_json::to_string(&original).unwrap();
732 let deserialized: OrderFilled = serde_json::from_str(&json).unwrap();
733
734 assert_eq!(original, deserialized);
735 }
736}