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 info = Params::new([("source".into(), "unit-test".into())].into());
480
481 let event = factory.generate_account_state(
482 Vec::new(),
483 Vec::new(),
484 false,
485 UnixNanos::from(3),
486 UnixNanos::from(5),
487 Some(info.clone()),
488 );
489
490 assert!(!event.is_reported);
491 assert_eq!(event.info, Some(info));
492 }
493
494 #[rstest]
495 fn test_generate_order_denied_uses_ts_init_for_both_timestamps(
496 factory: OrderEventFactory,
497 order: OrderAny,
498 ) {
499 let OrderEventAny::Denied(event) =
500 factory.generate_order_denied(&order, "Risk limit exceeded", UnixNanos::from(11))
501 else {
502 panic!("expected a denied event");
503 };
504
505 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
506 assert_eq!(event.strategy_id, order.strategy_id());
507 assert_eq!(event.instrument_id, order.instrument_id());
508 assert_eq!(event.client_order_id, order.client_order_id());
509 assert_eq!(event.reason.as_str(), "Risk limit exceeded");
510 assert_eq!(event.ts_event, UnixNanos::from(11));
511 assert_eq!(event.ts_init, UnixNanos::from(11));
512 }
513
514 #[rstest]
515 fn test_generate_order_submitted_uses_ts_init_for_both_timestamps(
516 factory: OrderEventFactory,
517 order: OrderAny,
518 ) {
519 let OrderEventAny::Submitted(event) =
520 factory.generate_order_submitted(&order, UnixNanos::from(13))
521 else {
522 panic!("expected a submitted event");
523 };
524
525 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
526 assert_eq!(event.strategy_id, order.strategy_id());
527 assert_eq!(event.instrument_id, order.instrument_id());
528 assert_eq!(event.client_order_id, order.client_order_id());
529 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
530 assert_eq!(event.ts_event, UnixNanos::from(13));
531 assert_eq!(event.ts_init, UnixNanos::from(13));
532 }
533
534 #[rstest]
535 #[case(true)]
536 #[case(false)]
537 fn test_generate_order_rejected_carries_reason_and_post_only_flag(
538 factory: OrderEventFactory,
539 order: OrderAny,
540 #[case] due_post_only: bool,
541 ) {
542 let OrderEventAny::Rejected(event) = factory.generate_order_rejected(
543 &order,
544 "Insufficient margin",
545 UnixNanos::from(17),
546 UnixNanos::from(19),
547 due_post_only,
548 ) else {
549 panic!("expected a rejected event");
550 };
551
552 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
553 assert_eq!(event.strategy_id, order.strategy_id());
554 assert_eq!(event.instrument_id, order.instrument_id());
555 assert_eq!(event.client_order_id, order.client_order_id());
556 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
557 assert_eq!(event.reason.as_str(), "Insufficient margin");
558 assert_eq!(event.ts_event, UnixNanos::from(17));
559 assert_eq!(event.ts_init, UnixNanos::from(19));
560 assert!(!event.reconciliation);
561 assert_eq!(event.due_post_only, due_post_only);
562 }
563
564 #[rstest]
565 fn test_generate_order_accepted_carries_venue_order_id(
566 factory: OrderEventFactory,
567 order: OrderAny,
568 ) {
569 let OrderEventAny::Accepted(event) = factory.generate_order_accepted(
570 &order,
571 VenueOrderId::from("V-1"),
572 UnixNanos::from(23),
573 UnixNanos::from(29),
574 ) else {
575 panic!("expected an accepted event");
576 };
577
578 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
579 assert_eq!(event.client_order_id, order.client_order_id());
580 assert_eq!(event.venue_order_id, VenueOrderId::from("V-1"));
581 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
582 assert_eq!(event.ts_event, UnixNanos::from(23));
583 assert_eq!(event.ts_init, UnixNanos::from(29));
584 assert!(!event.reconciliation);
585 }
586
587 #[rstest]
588 fn test_generate_order_modify_rejected_carries_optional_venue_order_id(
589 factory: OrderEventFactory,
590 order: OrderAny,
591 ) {
592 let OrderEventAny::ModifyRejected(event) = factory.generate_order_modify_rejected(
593 &order,
594 Some(VenueOrderId::from("V-2")),
595 "Unknown order",
596 UnixNanos::from(31),
597 UnixNanos::from(37),
598 ) else {
599 panic!("expected a modify rejected event");
600 };
601
602 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
603 assert_eq!(event.client_order_id, order.client_order_id());
604 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-2")));
605 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
606 assert_eq!(event.reason.as_str(), "Unknown order");
607 assert_eq!(event.ts_event, UnixNanos::from(31));
608 assert_eq!(event.ts_init, UnixNanos::from(37));
609 }
610
611 #[rstest]
612 fn test_generate_order_cancel_rejected_allows_absent_venue_order_id(
613 factory: OrderEventFactory,
614 order: OrderAny,
615 ) {
616 let OrderEventAny::CancelRejected(event) = factory.generate_order_cancel_rejected(
617 &order,
618 None,
619 "Already closed",
620 UnixNanos::from(41),
621 UnixNanos::from(43),
622 ) else {
623 panic!("expected a cancel rejected event");
624 };
625
626 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
627 assert_eq!(event.client_order_id, order.client_order_id());
628 assert_eq!(event.venue_order_id, None);
629 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
630 assert_eq!(event.reason.as_str(), "Already closed");
631 assert_eq!(event.ts_event, UnixNanos::from(41));
632 assert_eq!(event.ts_init, UnixNanos::from(43));
633 }
634
635 #[rstest]
636 fn test_generate_order_updated_carries_every_revised_field(
637 factory: OrderEventFactory,
638 order: OrderAny,
639 ) {
640 let OrderEventAny::Updated(event) = factory.generate_order_updated(
641 &order,
642 VenueOrderId::from("V-3"),
643 Quantity::from("2.000"),
644 Some(Price::from("2400.00")),
645 Some(Price::from("2450.00")),
646 Some(Price::from("2350.00")),
647 UnixNanos::from(47),
648 UnixNanos::from(53),
649 ) else {
650 panic!("expected an updated event");
651 };
652
653 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
654 assert_eq!(event.client_order_id, order.client_order_id());
655 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-3")));
656 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
657 assert_eq!(event.quantity, Quantity::from("2.000"));
658 assert_eq!(event.price, Some(Price::from("2400.00")));
659 assert_eq!(event.trigger_price, Some(Price::from("2450.00")));
660 assert_eq!(event.protection_price, Some(Price::from("2350.00")));
661 assert!(!event.is_quote_quantity);
662 assert_eq!(event.ts_event, UnixNanos::from(47));
663 assert_eq!(event.ts_init, UnixNanos::from(53));
664 }
665
666 #[rstest]
667 fn test_generate_order_canceled_carries_identity(factory: OrderEventFactory, order: OrderAny) {
668 let OrderEventAny::Canceled(event) = factory.generate_order_canceled(
669 &order,
670 Some(VenueOrderId::from("V-4")),
671 UnixNanos::from(59),
672 UnixNanos::from(61),
673 ) else {
674 panic!("expected a canceled event");
675 };
676
677 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
678 assert_eq!(event.client_order_id, order.client_order_id());
679 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-4")));
680 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
681 assert_eq!(event.ts_event, UnixNanos::from(59));
682 assert_eq!(event.ts_init, UnixNanos::from(61));
683 }
684
685 #[rstest]
686 fn test_generate_order_triggered_carries_identity(factory: OrderEventFactory, order: OrderAny) {
687 let OrderEventAny::Triggered(event) = factory.generate_order_triggered(
688 &order,
689 Some(VenueOrderId::from("V-5")),
690 UnixNanos::from(67),
691 UnixNanos::from(71),
692 ) else {
693 panic!("expected a triggered event");
694 };
695
696 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
697 assert_eq!(event.client_order_id, order.client_order_id());
698 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-5")));
699 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
700 assert_eq!(event.ts_event, UnixNanos::from(67));
701 assert_eq!(event.ts_init, UnixNanos::from(71));
702 }
703
704 #[rstest]
705 fn test_generate_order_expired_carries_identity(factory: OrderEventFactory, order: OrderAny) {
706 let OrderEventAny::Expired(event) = factory.generate_order_expired(
707 &order,
708 Some(VenueOrderId::from("V-6")),
709 UnixNanos::from(73),
710 UnixNanos::from(79),
711 ) else {
712 panic!("expected an expired event");
713 };
714
715 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
716 assert_eq!(event.client_order_id, order.client_order_id());
717 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-6")));
718 assert_eq!(event.account_id, Some(AccountId::from(ACCOUNT_ID)));
719 assert_eq!(event.ts_event, UnixNanos::from(73));
720 assert_eq!(event.ts_init, UnixNanos::from(79));
721 }
722
723 #[rstest]
724 fn test_generate_order_filled_takes_side_and_type_from_the_order(
725 factory: OrderEventFactory,
726 order: OrderAny,
727 ) {
728 let commission = Money::new(1.25, Currency::USD());
729
730 let OrderEventAny::Filled(event) = factory.generate_order_filled(
731 &order,
732 VenueOrderId::from("V-7"),
733 Some(PositionId::from("P-1")),
734 TradeId::from("T-1"),
735 Quantity::from("1.500"),
736 Price::from("2499.50"),
737 Currency::USDT(),
738 Some(commission),
739 LiquiditySide::Maker,
740 UnixNanos::from(83),
741 UnixNanos::from(89),
742 ) else {
743 panic!("expected a filled event");
744 };
745
746 assert_eq!(event.trader_id, TraderId::from(TRADER_ID));
747 assert_eq!(event.strategy_id, order.strategy_id());
748 assert_eq!(event.instrument_id, order.instrument_id());
749 assert_eq!(event.client_order_id, order.client_order_id());
750 assert_eq!(event.venue_order_id, VenueOrderId::from("V-7"));
751 assert_eq!(event.account_id, AccountId::from(ACCOUNT_ID));
752 assert_eq!(event.trade_id, TradeId::from("T-1"));
753 assert_eq!(event.position_id, Some(PositionId::from("P-1")));
754 assert_eq!(event.order_side, OrderSide::Sell);
755 assert_eq!(event.order_type, OrderType::Limit);
756 assert_eq!(event.last_qty, Quantity::from("1.500"));
757 assert_eq!(event.last_px, Price::from("2499.50"));
758 assert_eq!(event.currency, Currency::USDT());
759 assert_eq!(event.liquidity_side, LiquiditySide::Maker);
760 assert_eq!(event.commission, Some(commission));
761 assert_eq!(event.ts_event, UnixNanos::from(83));
762 assert_eq!(event.ts_init, UnixNanos::from(89));
763 }
764
765 #[rstest]
766 fn test_generate_order_filled_allows_absent_position_and_commission(
767 factory: OrderEventFactory,
768 order: OrderAny,
769 ) {
770 let OrderEventAny::Filled(event) = factory.generate_order_filled(
771 &order,
772 VenueOrderId::from("V-8"),
773 None,
774 TradeId::from("T-2"),
775 Quantity::from("3.000"),
776 Price::from("2501.00"),
777 Currency::USDT(),
778 None,
779 LiquiditySide::Taker,
780 UnixNanos::from(97),
781 UnixNanos::from(101),
782 ) else {
783 panic!("expected a filled event");
784 };
785
786 assert_eq!(event.position_id, None);
787 assert_eq!(event.commission, None);
788 assert_eq!(event.liquidity_side, LiquiditySide::Taker);
789 }
790
791 #[rstest]
792 fn test_generated_events_carry_distinct_event_ids(factory: OrderEventFactory, order: OrderAny) {
793 let (OrderEventAny::Submitted(first), OrderEventAny::Submitted(second)) = (
794 factory.generate_order_submitted(&order, UnixNanos::from(1)),
795 factory.generate_order_submitted(&order, UnixNanos::from(1)),
796 ) else {
797 panic!("expected submitted events");
798 };
799
800 assert_ne!(first.event_id, second.event_id);
801 }
802
803 #[rstest]
804 fn test_factory_clone_preserves_identity(factory: OrderEventFactory) {
805 let mut clone = factory.clone();
806 clone.set_account_id(AccountId::from("SIM-003"));
807
808 assert_eq!(factory.account_id(), AccountId::from(ACCOUNT_ID));
809 assert_eq!(clone.account_id(), AccountId::from("SIM-003"));
810 assert_eq!(clone.trader_id(), factory.trader_id());
811 }
812}