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)]
436mod tests {
437 use nautilus_core::UnixNanos;
438 use rstest::rstest;
439
440 use super::*;
441 use crate::{
442 enums::{LiquiditySide, OrderSide, OrderSideSpecified, OrderType},
443 events::order::stubs::*,
444 identifiers::{
445 AccountId, ClientOrderId, InstrumentId, PositionId, StrategyId, TradeId, TraderId,
446 VenueOrderId,
447 },
448 types::{Currency, Money, Price, Quantity},
449 };
450
451 fn create_test_order_filled() -> OrderFilled {
452 OrderFilled::new(
453 TraderId::from("TRADER-001"),
454 StrategyId::from("EMA-CROSS"),
455 InstrumentId::from("EURUSD.SIM"),
456 ClientOrderId::from("O-19700101-000000-001-001-1"),
457 VenueOrderId::from("V-001"),
458 AccountId::from("SIM-001"),
459 TradeId::from("T-001"),
460 OrderSide::Buy,
461 OrderType::Market,
462 Quantity::from("100"),
463 Price::from("1.0500"),
464 Currency::USD(),
465 LiquiditySide::Taker,
466 Default::default(),
467 UnixNanos::from(1_000_000_000),
468 UnixNanos::from(2_000_000_000),
469 false,
470 Some(PositionId::from("P-001")),
471 Some(Money::new(2.5, Currency::USD())),
472 )
473 }
474
475 #[rstest]
476 fn test_order_filled_new() {
477 let order_filled = create_test_order_filled();
478
479 assert_eq!(order_filled.trader_id, TraderId::from("TRADER-001"));
480 assert_eq!(order_filled.strategy_id, StrategyId::from("EMA-CROSS"));
481 assert_eq!(order_filled.instrument_id, InstrumentId::from("EURUSD.SIM"));
482 assert_eq!(
483 order_filled.client_order_id,
484 ClientOrderId::from("O-19700101-000000-001-001-1")
485 );
486 assert_eq!(order_filled.venue_order_id, VenueOrderId::from("V-001"));
487 assert_eq!(order_filled.account_id, AccountId::from("SIM-001"));
488 assert_eq!(order_filled.trade_id, TradeId::from("T-001"));
489 assert_eq!(order_filled.order_side, OrderSide::Buy);
490 assert_eq!(order_filled.order_type, OrderType::Market);
491 assert_eq!(order_filled.last_qty, Quantity::from("100"));
492 assert_eq!(order_filled.last_px, Price::from("1.0500"));
493 assert_eq!(order_filled.currency, Currency::USD());
494 assert_eq!(order_filled.liquidity_side, LiquiditySide::Taker);
495 assert_eq!(order_filled.position_id, Some(PositionId::from("P-001")));
496 assert_eq!(
497 order_filled.commission,
498 Some(Money::new(2.5, Currency::USD()))
499 );
500 assert!(!order_filled.reconciliation);
501 }
502
503 #[rstest]
504 fn test_order_filled_display(order_filled: OrderFilled) {
505 let display = format!("{order_filled}");
506 assert_eq!(
507 display,
508 "OrderFilled(instrument_id=BTCUSDT.COINBASE, client_order_id=O-19700101-000000-001-001-1, \
509 venue_order_id=123456, account_id=SIM-001, trade_id=1, position_id=P-001, \
510 order_side=BUY, order_type=LIMIT, last_qty=0.561, last_px=22_000 USDT, \
511 commission=12.20000000 USDT, liquidity_side=TAKER, ts_event=0)"
512 );
513 }
514
515 #[rstest]
516 fn test_order_filled_is_buy(order_filled: OrderFilled) {
517 assert!(order_filled.is_buy());
518 assert!(!order_filled.is_sell());
519 }
520
521 #[rstest]
522 fn test_order_filled_is_sell() {
523 let mut order_filled = create_test_order_filled();
524 order_filled.order_side = OrderSide::Sell;
525
526 assert!(order_filled.is_sell());
527 assert!(!order_filled.is_buy());
528 }
529
530 #[rstest]
531 fn test_order_filled_specified_side() {
532 let buy_order = create_test_order_filled();
533 assert_eq!(buy_order.specified_side(), OrderSideSpecified::Buy);
534
535 let mut sell_order = create_test_order_filled();
536 sell_order.order_side = OrderSide::Sell;
537 assert_eq!(sell_order.specified_side(), OrderSideSpecified::Sell);
538 }
539
540 #[rstest]
541 fn test_order_filled_default() {
542 let order_filled = OrderFilled::default();
543
544 assert_eq!(order_filled.trader_id, TraderId::default());
545 assert_eq!(order_filled.strategy_id, StrategyId::default());
546 assert_eq!(order_filled.instrument_id, InstrumentId::default());
547 assert_eq!(order_filled.client_order_id, ClientOrderId::default());
548 assert_eq!(order_filled.venue_order_id, VenueOrderId::default());
549 assert_eq!(order_filled.account_id, AccountId::default());
550 assert_eq!(order_filled.trade_id, TradeId::default());
551 assert_eq!(order_filled.order_side, OrderSide::Buy);
552 assert_eq!(order_filled.order_type, OrderType::Market);
553 assert_eq!(order_filled.currency, Currency::USD());
554 assert_eq!(order_filled.liquidity_side, LiquiditySide::Taker);
555 assert_eq!(order_filled.position_id, None);
556 assert_eq!(order_filled.commission, None);
557 assert!(!order_filled.reconciliation);
558 }
559
560 #[rstest]
561 fn test_order_filled_order_event_trait() {
562 let order_filled = create_test_order_filled();
563
564 assert_eq!(order_filled.id(), order_filled.event_id);
565 assert_eq!(order_filled.kind(), "OrderFilled");
566 assert_eq!(order_filled.order_type(), Some(OrderType::Market));
567 assert_eq!(order_filled.order_side(), Some(OrderSide::Buy));
568 assert_eq!(order_filled.trader_id(), TraderId::from("TRADER-001"));
569 assert_eq!(order_filled.strategy_id(), StrategyId::from("EMA-CROSS"));
570 assert_eq!(
571 order_filled.instrument_id(),
572 InstrumentId::from("EURUSD.SIM")
573 );
574 assert_eq!(order_filled.trade_id(), Some(TradeId::from("T-001")));
575 assert_eq!(order_filled.currency(), Some(Currency::USD()));
576 assert_eq!(
577 order_filled.client_order_id(),
578 ClientOrderId::from("O-19700101-000000-001-001-1")
579 );
580 assert_eq!(order_filled.reason(), None);
581 assert_eq!(order_filled.quantity(), Some(Quantity::from("100")));
582 assert_eq!(order_filled.liquidity_side(), Some(LiquiditySide::Taker));
583 assert!(!order_filled.reconciliation());
584 assert_eq!(
585 order_filled.venue_order_id(),
586 Some(VenueOrderId::from("V-001"))
587 );
588 assert_eq!(order_filled.account_id(), Some(AccountId::from("SIM-001")));
589 assert_eq!(order_filled.position_id(), Some(PositionId::from("P-001")));
590 assert_eq!(
591 order_filled.commission(),
592 Some(Money::new(2.5, Currency::USD()))
593 );
594 assert_eq!(order_filled.last_px(), Some(Price::from("1.0500")));
595 assert_eq!(order_filled.last_qty(), Some(Quantity::from("100")));
596 }
597
598 #[rstest]
599 fn test_order_filled_different_order_types() {
600 let mut market_order = create_test_order_filled();
601 market_order.order_type = OrderType::Market;
602
603 let mut limit_order = create_test_order_filled();
604 limit_order.order_type = OrderType::Limit;
605
606 let mut stop_order = create_test_order_filled();
607 stop_order.order_type = OrderType::StopMarket;
608
609 assert_ne!(market_order, limit_order);
610 assert_ne!(limit_order, stop_order);
611 assert_eq!(market_order.order_type, OrderType::Market);
612 assert_eq!(limit_order.order_type, OrderType::Limit);
613 assert_eq!(stop_order.order_type, OrderType::StopMarket);
614 }
615
616 #[rstest]
617 fn test_order_filled_different_liquidity_sides() {
618 let mut taker = create_test_order_filled();
619 taker.liquidity_side = LiquiditySide::Taker;
620
621 let mut maker = create_test_order_filled();
622 maker.liquidity_side = LiquiditySide::Maker;
623
624 assert_ne!(taker, maker);
625 assert_eq!(taker.liquidity_side, LiquiditySide::Taker);
626 assert_eq!(maker.liquidity_side, LiquiditySide::Maker);
627 }
628
629 #[rstest]
630 fn test_order_filled_without_position_id() {
631 let mut order_filled = create_test_order_filled();
632 order_filled.position_id = None;
633
634 assert!(order_filled.position_id.is_none());
635 }
636
637 #[rstest]
638 fn test_order_filled_without_commission() {
639 let mut order_filled = create_test_order_filled();
640 order_filled.commission = None;
641
642 assert!(order_filled.commission.is_none());
643 }
644
645 #[rstest]
646 fn test_order_filled_with_reconciliation() {
647 let mut order_filled = create_test_order_filled();
648 order_filled.reconciliation = true;
649
650 assert!(order_filled.reconciliation);
651 }
652
653 #[rstest]
654 fn test_order_filled_clone() {
655 let order_filled1 = create_test_order_filled();
656 let order_filled2 = order_filled1;
657
658 assert_eq!(order_filled1, order_filled2);
659 }
660
661 #[rstest]
662 fn test_order_filled_debug() {
663 let order_filled = create_test_order_filled();
664 let debug_str = format!("{order_filled:?}");
665
666 assert!(debug_str.contains("OrderFilled"));
667 assert!(debug_str.contains("TRADER-001"));
668 assert!(debug_str.contains("EMA-CROSS"));
669 assert!(debug_str.contains("EURUSD.SIM"));
670 assert!(debug_str.contains("P-001"));
671 }
672
673 #[rstest]
674 fn test_order_filled_partial_eq() {
675 let order_filled1 = create_test_order_filled();
676 let mut order_filled2 = create_test_order_filled();
677 order_filled2.event_id = order_filled1.event_id; let mut order_filled3 = create_test_order_filled();
679 order_filled3.trade_id = TradeId::from("T-002");
680
681 assert_eq!(order_filled1, order_filled2);
682 assert_ne!(order_filled1, order_filled3);
683 }
684
685 #[rstest]
686 fn test_order_filled_timestamps() {
687 let order_filled = create_test_order_filled();
688
689 assert_eq!(order_filled.ts_event, UnixNanos::from(1_000_000_000));
690 assert_eq!(order_filled.ts_init, UnixNanos::from(2_000_000_000));
691 assert!(order_filled.ts_event < order_filled.ts_init);
692 }
693
694 #[rstest]
695 fn test_order_filled_different_currencies() {
696 let mut usd_fill = create_test_order_filled();
697 usd_fill.currency = Currency::USD();
698
699 let mut eur_fill = create_test_order_filled();
700 eur_fill.currency = Currency::EUR();
701
702 assert_ne!(usd_fill, eur_fill);
703 assert_eq!(usd_fill.currency, Currency::USD());
704 assert_eq!(eur_fill.currency, Currency::EUR());
705 }
706
707 #[rstest]
708 fn test_order_filled_different_prices_and_quantities() {
709 let mut large_fill = create_test_order_filled();
710 large_fill.last_qty = Quantity::from("1000");
711 large_fill.last_px = Price::from("1.1000");
712
713 let mut small_fill = create_test_order_filled();
714 small_fill.last_qty = Quantity::from("100");
715 small_fill.last_px = Price::from("1.0500");
716
717 assert_ne!(large_fill, small_fill);
718 assert_eq!(large_fill.last_qty, Quantity::from("1000"));
719 assert_eq!(large_fill.last_px, Price::from("1.1000"));
720 assert_eq!(small_fill.last_qty, Quantity::from("100"));
721 assert_eq!(small_fill.last_px, Price::from("1.0500"));
722 }
723
724 #[rstest]
725 fn test_order_filled_serialization() {
726 let original = create_test_order_filled();
727
728 let json = serde_json::to_string(&original).unwrap();
729 let deserialized: OrderFilled = serde_json::from_str(&json).unwrap();
730
731 assert_eq!(original, deserialized);
732 }
733}