1use nautilus_core::{Params, UUID4, UnixNanos};
19use nautilus_model::{
20 enums::{AccountType, LiquiditySide},
21 events::{
22 AccountState, OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied,
23 OrderEventAny, OrderExpired, OrderFilled, OrderModifyRejected, OrderRejected,
24 OrderSubmitted, OrderTriggered, OrderUpdated,
25 },
26 identifiers::{AccountId, PositionId, TradeId, TraderId, VenueOrderId},
27 orders::{Order, OrderAny},
28 types::{AccountBalance, Currency, MarginBalance, Money, Price, Quantity},
29};
30
31#[derive(Debug, Clone)]
37pub struct OrderEventFactory {
38 trader_id: TraderId,
39 account_id: AccountId,
40 account_type: AccountType,
41 base_currency: Option<Currency>,
42}
43
44impl OrderEventFactory {
45 #[must_use]
47 pub fn new(
48 trader_id: TraderId,
49 account_id: AccountId,
50 account_type: AccountType,
51 base_currency: Option<Currency>,
52 ) -> Self {
53 Self {
54 trader_id,
55 account_id,
56 account_type,
57 base_currency,
58 }
59 }
60
61 #[must_use]
63 pub fn trader_id(&self) -> TraderId {
64 self.trader_id
65 }
66
67 #[must_use]
69 pub fn account_id(&self) -> AccountId {
70 self.account_id
71 }
72
73 pub const fn set_account_id(&mut self, account_id: AccountId) {
75 self.account_id = account_id;
76 }
77
78 #[must_use]
80 pub fn generate_account_state(
81 &self,
82 balances: Vec<AccountBalance>,
83 margins: Vec<MarginBalance>,
84 reported: bool,
85 ts_event: UnixNanos,
86 ts_init: UnixNanos,
87 info: Option<Params>,
88 ) -> AccountState {
89 AccountState::new(
90 self.account_id,
91 self.account_type,
92 balances,
93 margins,
94 reported,
95 UUID4::new(),
96 ts_event,
97 ts_init,
98 self.base_currency,
99 )
100 .with_info(info)
101 }
102
103 #[must_use]
107 pub fn generate_order_denied(
108 &self,
109 order: &OrderAny,
110 reason: &str,
111 ts_init: UnixNanos,
112 ) -> OrderEventAny {
113 let event = OrderDenied::new(
114 self.trader_id,
115 order.strategy_id(),
116 order.instrument_id(),
117 order.client_order_id(),
118 reason.into(),
119 UUID4::new(),
120 ts_init,
121 ts_init,
122 );
123 OrderEventAny::Denied(event)
124 }
125
126 #[must_use]
130 pub fn generate_order_submitted(&self, order: &OrderAny, ts_init: UnixNanos) -> OrderEventAny {
131 let event = OrderSubmitted::new(
132 self.trader_id,
133 order.strategy_id(),
134 order.instrument_id(),
135 order.client_order_id(),
136 self.account_id,
137 UUID4::new(),
138 ts_init,
139 ts_init,
140 );
141 OrderEventAny::Submitted(event)
142 }
143
144 #[must_use]
146 pub fn generate_order_rejected(
147 &self,
148 order: &OrderAny,
149 reason: &str,
150 ts_event: UnixNanos,
151 ts_init: UnixNanos,
152 due_post_only: bool,
153 ) -> OrderEventAny {
154 let event = OrderRejected::new(
155 self.trader_id,
156 order.strategy_id(),
157 order.instrument_id(),
158 order.client_order_id(),
159 self.account_id,
160 reason.into(),
161 UUID4::new(),
162 ts_event,
163 ts_init,
164 false,
165 due_post_only,
166 );
167 OrderEventAny::Rejected(event)
168 }
169
170 #[must_use]
172 pub fn generate_order_accepted(
173 &self,
174 order: &OrderAny,
175 venue_order_id: VenueOrderId,
176 ts_event: UnixNanos,
177 ts_init: UnixNanos,
178 ) -> OrderEventAny {
179 let event = OrderAccepted::new(
180 self.trader_id,
181 order.strategy_id(),
182 order.instrument_id(),
183 order.client_order_id(),
184 venue_order_id,
185 self.account_id,
186 UUID4::new(),
187 ts_event,
188 ts_init,
189 false,
190 );
191 OrderEventAny::Accepted(event)
192 }
193
194 #[must_use]
196 pub fn generate_order_modify_rejected(
197 &self,
198 order: &OrderAny,
199 venue_order_id: Option<VenueOrderId>,
200 reason: &str,
201 ts_event: UnixNanos,
202 ts_init: UnixNanos,
203 ) -> OrderEventAny {
204 let event = OrderModifyRejected::new(
205 self.trader_id,
206 order.strategy_id(),
207 order.instrument_id(),
208 order.client_order_id(),
209 reason.into(),
210 UUID4::new(),
211 ts_event,
212 ts_init,
213 false,
214 venue_order_id,
215 Some(self.account_id),
216 );
217 OrderEventAny::ModifyRejected(event)
218 }
219
220 #[must_use]
222 pub fn generate_order_cancel_rejected(
223 &self,
224 order: &OrderAny,
225 venue_order_id: Option<VenueOrderId>,
226 reason: &str,
227 ts_event: UnixNanos,
228 ts_init: UnixNanos,
229 ) -> OrderEventAny {
230 let event = OrderCancelRejected::new(
231 self.trader_id,
232 order.strategy_id(),
233 order.instrument_id(),
234 order.client_order_id(),
235 reason.into(),
236 UUID4::new(),
237 ts_event,
238 ts_init,
239 false,
240 venue_order_id,
241 Some(self.account_id),
242 );
243 OrderEventAny::CancelRejected(event)
244 }
245
246 #[expect(clippy::too_many_arguments)]
248 #[must_use]
249 pub fn generate_order_updated(
250 &self,
251 order: &OrderAny,
252 venue_order_id: VenueOrderId,
253 quantity: Quantity,
254 price: Option<Price>,
255 trigger_price: Option<Price>,
256 protection_price: Option<Price>,
257 ts_event: UnixNanos,
258 ts_init: UnixNanos,
259 ) -> OrderEventAny {
260 let event = OrderUpdated::new(
261 self.trader_id,
262 order.strategy_id(),
263 order.instrument_id(),
264 order.client_order_id(),
265 quantity,
266 UUID4::new(),
267 ts_event,
268 ts_init,
269 false,
270 Some(venue_order_id),
271 Some(self.account_id),
272 price,
273 trigger_price,
274 protection_price,
275 false, );
277 OrderEventAny::Updated(event)
278 }
279
280 #[must_use]
282 pub fn generate_order_canceled(
283 &self,
284 order: &OrderAny,
285 venue_order_id: Option<VenueOrderId>,
286 ts_event: UnixNanos,
287 ts_init: UnixNanos,
288 ) -> OrderEventAny {
289 let event = OrderCanceled::new(
290 self.trader_id,
291 order.strategy_id(),
292 order.instrument_id(),
293 order.client_order_id(),
294 UUID4::new(),
295 ts_event,
296 ts_init,
297 false,
298 venue_order_id,
299 Some(self.account_id),
300 None,
301 );
302 OrderEventAny::Canceled(event)
303 }
304
305 #[must_use]
307 pub fn generate_order_triggered(
308 &self,
309 order: &OrderAny,
310 venue_order_id: Option<VenueOrderId>,
311 ts_event: UnixNanos,
312 ts_init: UnixNanos,
313 ) -> OrderEventAny {
314 let event = OrderTriggered::new(
315 self.trader_id,
316 order.strategy_id(),
317 order.instrument_id(),
318 order.client_order_id(),
319 UUID4::new(),
320 ts_event,
321 ts_init,
322 false,
323 venue_order_id,
324 Some(self.account_id),
325 );
326 OrderEventAny::Triggered(event)
327 }
328
329 #[must_use]
331 pub fn generate_order_expired(
332 &self,
333 order: &OrderAny,
334 venue_order_id: Option<VenueOrderId>,
335 ts_event: UnixNanos,
336 ts_init: UnixNanos,
337 ) -> OrderEventAny {
338 let event = OrderExpired::new(
339 self.trader_id,
340 order.strategy_id(),
341 order.instrument_id(),
342 order.client_order_id(),
343 UUID4::new(),
344 ts_event,
345 ts_init,
346 false,
347 venue_order_id,
348 Some(self.account_id),
349 );
350 OrderEventAny::Expired(event)
351 }
352
353 #[expect(clippy::too_many_arguments)]
355 #[must_use]
356 pub fn generate_order_filled(
357 &self,
358 order: &OrderAny,
359 venue_order_id: VenueOrderId,
360 venue_position_id: Option<PositionId>,
361 trade_id: TradeId,
362 last_qty: Quantity,
363 last_px: Price,
364 quote_currency: Currency,
365 commission: Option<Money>,
366 liquidity_side: LiquiditySide,
367 ts_event: UnixNanos,
368 ts_init: UnixNanos,
369 ) -> OrderEventAny {
370 let event = OrderFilled::new(
371 self.trader_id,
372 order.strategy_id(),
373 order.instrument_id(),
374 order.client_order_id(),
375 venue_order_id,
376 self.account_id,
377 trade_id,
378 order.order_side(),
379 order.order_type(),
380 last_qty,
381 last_px,
382 quote_currency,
383 liquidity_side,
384 UUID4::new(),
385 ts_event,
386 ts_init,
387 false,
388 venue_position_id,
389 commission,
390 None,
391 );
392 OrderEventAny::Filled(event)
393 }
394}
395
396#[cfg(test)]
397mod tests {
398 use nautilus_model::{
399 enums::{OrderSide, OrderType},
400 identifiers::{ClientOrderId, InstrumentId},
401 orders::OrderTestBuilder,
402 types::Quantity,
403 };
404 use rstest::{fixture, rstest};
405
406 use super::*;
407
408 const TRADER_ID: &str = "TESTER-001";
409 const ACCOUNT_ID: &str = "SIM-002";
410
411 #[fixture]
412 fn factory() -> OrderEventFactory {
413 OrderEventFactory::new(
414 TraderId::from(TRADER_ID),
415 AccountId::from(ACCOUNT_ID),
416 AccountType::Margin,
417 Some(Currency::USD()),
418 )
419 }
420
421 #[fixture]
422 fn order() -> OrderAny {
423 OrderTestBuilder::new(OrderType::Limit)
424 .instrument_id(InstrumentId::from("ETHUSDT.BINANCE"))
425 .client_order_id(ClientOrderId::from("O-19700101-000000-001-001-1"))
426 .side(OrderSide::Sell)
427 .quantity(Quantity::from("3.000"))
428 .price(Price::from("2500.00"))
429 .build()
430 }
431
432 #[rstest]
433 fn test_identity_accessors_and_account_id_override(mut factory: OrderEventFactory) {
434 assert_eq!(factory.trader_id(), TraderId::from(TRADER_ID));
435 assert_eq!(factory.account_id(), AccountId::from(ACCOUNT_ID));
436
437 factory.set_account_id(AccountId::from("SIM-999"));
438
439 assert_eq!(factory.account_id(), AccountId::from("SIM-999"));
440 assert_eq!(factory.trader_id(), TraderId::from(TRADER_ID));
441 }
442
443 #[rstest]
444 fn test_generate_account_state_carries_factory_identity(factory: OrderEventFactory) {
445 let balance = AccountBalance::new(
446 Money::new(1_500.0, Currency::USD()),
447 Money::new(500.0, Currency::USD()),
448 Money::new(1_000.0, Currency::USD()),
449 );
450
451 let margin = MarginBalance::new(
452 Money::new(10.0, Currency::USD()),
453 Money::new(20.0, Currency::USD()),
454 Some(InstrumentId::from("ETHUSDT.BINANCE")),
455 );
456
457 let event = factory.generate_account_state(
458 vec![balance],
459 vec![margin],
460 true,
461 UnixNanos::from(3),
462 UnixNanos::from(5),
463 None,
464 );
465
466 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
467 assert_eq!(event.account_type, AccountType::Margin);
468 assert_eq!(event.base_currency, Some(Currency::USD()));
469 assert_eq!(event.balances, vec![balance]);
470 assert_eq!(event.margins, vec![margin]);
471 assert!(event.is_reported);
472 assert_eq!(event.ts_event, UnixNanos::from(3));
473 assert_eq!(event.ts_init, UnixNanos::from(5));
474 assert_eq!(event.info, None);
475 }
476
477 #[rstest]
478 fn test_generate_account_state_attaches_info(factory: OrderEventFactory) {
479 let mut info = Params::new();
480 info.insert("source".into(), "unit-test".into());
481
482 let event = factory.generate_account_state(
483 Vec::new(),
484 Vec::new(),
485 false,
486 UnixNanos::from(3),
487 UnixNanos::from(5),
488 Some(info.clone()),
489 );
490
491 assert!(!event.is_reported);
492 assert_eq!(event.info, Some(info));
493 }
494
495 #[rstest]
496 fn test_generate_order_denied_uses_ts_init_for_both_timestamps(
497 factory: OrderEventFactory,
498 order: OrderAny,
499 ) {
500 let OrderEventAny::Denied(event) =
501 factory.generate_order_denied(&order, "Risk limit exceeded", UnixNanos::from(11))
502 else {
503 panic!("expected a denied event");
504 };
505
506 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
507 assert_eq!(event.strategy_id, order.strategy_id());
508 assert_eq!(event.instrument_id, order.instrument_id());
509 assert_eq!(event.client_order_id, order.client_order_id());
510 assert_eq!(event.reason.as_str(), "Risk limit exceeded");
511 assert_eq!(event.ts_event, UnixNanos::from(11));
512 assert_eq!(event.ts_init, UnixNanos::from(11));
513 }
514
515 #[rstest]
516 fn test_generate_order_submitted_uses_ts_init_for_both_timestamps(
517 factory: OrderEventFactory,
518 order: OrderAny,
519 ) {
520 let OrderEventAny::Submitted(event) =
521 factory.generate_order_submitted(&order, UnixNanos::from(13))
522 else {
523 panic!("expected a submitted event");
524 };
525
526 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
527 assert_eq!(event.strategy_id, order.strategy_id());
528 assert_eq!(event.instrument_id, order.instrument_id());
529 assert_eq!(event.client_order_id, order.client_order_id());
530 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
531 assert_eq!(event.ts_event, UnixNanos::from(13));
532 assert_eq!(event.ts_init, UnixNanos::from(13));
533 }
534
535 #[rstest]
536 #[case(true)]
537 #[case(false)]
538 fn test_generate_order_rejected_carries_reason_and_post_only_flag(
539 factory: OrderEventFactory,
540 order: OrderAny,
541 #[case] due_post_only: bool,
542 ) {
543 let OrderEventAny::Rejected(event) = factory.generate_order_rejected(
544 &order,
545 "Insufficient margin",
546 UnixNanos::from(17),
547 UnixNanos::from(19),
548 due_post_only,
549 ) else {
550 panic!("expected a rejected event");
551 };
552
553 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
554 assert_eq!(event.strategy_id, order.strategy_id());
555 assert_eq!(event.instrument_id, order.instrument_id());
556 assert_eq!(event.client_order_id, order.client_order_id());
557 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
558 assert_eq!(event.reason.as_str(), "Insufficient margin");
559 assert_eq!(event.ts_event, UnixNanos::from(17));
560 assert_eq!(event.ts_init, UnixNanos::from(19));
561 assert!(!event.reconciliation);
562 assert_eq!(event.due_post_only, due_post_only);
563 }
564
565 #[rstest]
566 fn test_generate_order_accepted_carries_venue_order_id(
567 factory: OrderEventFactory,
568 order: OrderAny,
569 ) {
570 let OrderEventAny::Accepted(event) = factory.generate_order_accepted(
571 &order,
572 VenueOrderId::from("V-1"),
573 UnixNanos::from(23),
574 UnixNanos::from(29),
575 ) else {
576 panic!("expected an accepted event");
577 };
578
579 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
580 assert_eq!(event.client_order_id, order.client_order_id());
581 assert_eq!(event.venue_order_id, VenueOrderId::from("V-1"));
582 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
583 assert_eq!(event.ts_event, UnixNanos::from(23));
584 assert_eq!(event.ts_init, UnixNanos::from(29));
585 assert!(!event.reconciliation);
586 }
587
588 #[rstest]
589 fn test_generate_order_modify_rejected_carries_optional_venue_order_id(
590 factory: OrderEventFactory,
591 order: OrderAny,
592 ) {
593 let OrderEventAny::ModifyRejected(event) = factory.generate_order_modify_rejected(
594 &order,
595 Some(VenueOrderId::from("V-2")),
596 "Unknown order",
597 UnixNanos::from(31),
598 UnixNanos::from(37),
599 ) else {
600 panic!("expected a modify rejected event");
601 };
602
603 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
604 assert_eq!(event.client_order_id, order.client_order_id());
605 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-2")));
606 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
607 assert_eq!(event.reason.as_str(), "Unknown order");
608 assert_eq!(event.ts_event, UnixNanos::from(31));
609 assert_eq!(event.ts_init, UnixNanos::from(37));
610 }
611
612 #[rstest]
613 fn test_generate_order_cancel_rejected_allows_absent_venue_order_id(
614 factory: OrderEventFactory,
615 order: OrderAny,
616 ) {
617 let OrderEventAny::CancelRejected(event) = factory.generate_order_cancel_rejected(
618 &order,
619 None,
620 "Already closed",
621 UnixNanos::from(41),
622 UnixNanos::from(43),
623 ) else {
624 panic!("expected a cancel rejected event");
625 };
626
627 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
628 assert_eq!(event.client_order_id, order.client_order_id());
629 assert_eq!(event.venue_order_id, None);
630 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
631 assert_eq!(event.reason.as_str(), "Already closed");
632 assert_eq!(event.ts_event, UnixNanos::from(41));
633 assert_eq!(event.ts_init, UnixNanos::from(43));
634 }
635
636 #[rstest]
637 fn test_generate_order_updated_carries_every_revised_field(
638 factory: OrderEventFactory,
639 order: OrderAny,
640 ) {
641 let OrderEventAny::Updated(event) = factory.generate_order_updated(
642 &order,
643 VenueOrderId::from("V-3"),
644 Quantity::from("2.000"),
645 Some(Price::from("2400.00")),
646 Some(Price::from("2450.00")),
647 Some(Price::from("2350.00")),
648 UnixNanos::from(47),
649 UnixNanos::from(53),
650 ) else {
651 panic!("expected an updated event");
652 };
653
654 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
655 assert_eq!(event.client_order_id, order.client_order_id());
656 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-3")));
657 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
658 assert_eq!(event.quantity, Quantity::from("2.000"));
659 assert_eq!(event.price, Some(Price::from("2400.00")));
660 assert_eq!(event.trigger_price, Some(Price::from("2450.00")));
661 assert_eq!(event.protection_price, Some(Price::from("2350.00")));
662 assert!(!event.is_quote_quantity);
663 assert_eq!(event.ts_event, UnixNanos::from(47));
664 assert_eq!(event.ts_init, UnixNanos::from(53));
665 }
666
667 #[rstest]
668 fn test_generate_order_canceled_carries_identity(factory: OrderEventFactory, order: OrderAny) {
669 let OrderEventAny::Canceled(event) = factory.generate_order_canceled(
670 &order,
671 Some(VenueOrderId::from("V-4")),
672 UnixNanos::from(59),
673 UnixNanos::from(61),
674 ) else {
675 panic!("expected a canceled event");
676 };
677
678 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
679 assert_eq!(event.client_order_id, order.client_order_id());
680 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-4")));
681 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
682 assert_eq!(event.ts_event, UnixNanos::from(59));
683 assert_eq!(event.ts_init, UnixNanos::from(61));
684 }
685
686 #[rstest]
687 fn test_generate_order_triggered_carries_identity(factory: OrderEventFactory, order: OrderAny) {
688 let OrderEventAny::Triggered(event) = factory.generate_order_triggered(
689 &order,
690 Some(VenueOrderId::from("V-5")),
691 UnixNanos::from(67),
692 UnixNanos::from(71),
693 ) else {
694 panic!("expected a triggered event");
695 };
696
697 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
698 assert_eq!(event.client_order_id, order.client_order_id());
699 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-5")));
700 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
701 assert_eq!(event.ts_event, UnixNanos::from(67));
702 assert_eq!(event.ts_init, UnixNanos::from(71));
703 }
704
705 #[rstest]
706 fn test_generate_order_expired_carries_identity(factory: OrderEventFactory, order: OrderAny) {
707 let OrderEventAny::Expired(event) = factory.generate_order_expired(
708 &order,
709 Some(VenueOrderId::from("V-6")),
710 UnixNanos::from(73),
711 UnixNanos::from(79),
712 ) else {
713 panic!("expected an expired event");
714 };
715
716 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
717 assert_eq!(event.client_order_id, order.client_order_id());
718 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-6")));
719 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
720 assert_eq!(event.ts_event, UnixNanos::from(73));
721 assert_eq!(event.ts_init, UnixNanos::from(79));
722 }
723
724 #[rstest]
725 fn test_generate_order_filled_takes_side_and_type_from_the_order(
726 factory: OrderEventFactory,
727 order: OrderAny,
728 ) {
729 let commission = Money::new(1.25, Currency::USD());
730
731 let OrderEventAny::Filled(event) = factory.generate_order_filled(
732 &order,
733 VenueOrderId::from("V-7"),
734 Some(PositionId::from("P-1")),
735 TradeId::from("T-1"),
736 Quantity::from("1.500"),
737 Price::from("2499.50"),
738 Currency::USDT(),
739 Some(commission),
740 LiquiditySide::Maker,
741 UnixNanos::from(83),
742 UnixNanos::from(89),
743 ) else {
744 panic!("expected a filled event");
745 };
746
747 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
748 assert_eq!(event.strategy_id, order.strategy_id());
749 assert_eq!(event.instrument_id, order.instrument_id());
750 assert_eq!(event.client_order_id, order.client_order_id());
751 assert_eq!(event.venue_order_id, VenueOrderId::from("V-7"));
752 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
753 assert_eq!(event.trade_id, TradeId::from("T-1"));
754 assert_eq!(event.position_id, Some(PositionId::from("P-1")));
755 assert_eq!(event.order_side, OrderSide::Sell);
756 assert_eq!(event.order_type, OrderType::Limit);
757 assert_eq!(event.last_qty, Quantity::from("1.500"));
758 assert_eq!(event.last_px, Price::from("2499.50"));
759 assert_eq!(event.currency, Currency::USDT());
760 assert_eq!(event.liquidity_side, LiquiditySide::Maker);
761 assert_eq!(event.commission, Some(commission));
762 assert_eq!(event.ts_event, UnixNanos::from(83));
763 assert_eq!(event.ts_init, UnixNanos::from(89));
764 }
765
766 #[rstest]
767 fn test_generate_order_filled_allows_absent_position_and_commission(
768 factory: OrderEventFactory,
769 order: OrderAny,
770 ) {
771 let OrderEventAny::Filled(event) = factory.generate_order_filled(
772 &order,
773 VenueOrderId::from("V-8"),
774 None,
775 TradeId::from("T-2"),
776 Quantity::from("3.000"),
777 Price::from("2501.00"),
778 Currency::USDT(),
779 None,
780 LiquiditySide::Taker,
781 UnixNanos::from(97),
782 UnixNanos::from(101),
783 ) else {
784 panic!("expected a filled event");
785 };
786
787 assert_eq!(event.position_id, None);
788 assert_eq!(event.commission, None);
789 assert_eq!(event.liquidity_side, LiquiditySide::Taker);
790 }
791
792 #[rstest]
793 fn test_generated_events_carry_distinct_event_ids(factory: OrderEventFactory, order: OrderAny) {
794 let (OrderEventAny::Submitted(first), OrderEventAny::Submitted(second)) = (
795 factory.generate_order_submitted(&order, UnixNanos::from(1)),
796 factory.generate_order_submitted(&order, UnixNanos::from(1)),
797 ) else {
798 panic!("expected submitted events");
799 };
800
801 assert_ne!(first.event_id, second.event_id);
802 }
803
804 #[rstest]
805 fn test_factory_clone_preserves_identity(factory: OrderEventFactory) {
806 let mut clone = factory.clone();
807 clone.set_account_id(AccountId::from("SIM-003"));
808
809 assert_eq!(factory.account_id(), AccountId::from(ACCOUNT_ID));
810 assert_eq!(clone.account_id(), AccountId::from("SIM-003"));
811 assert_eq!(clone.trader_id(), factory.trader_id());
812 }
813}