nautilus_model/events/order/spec/
canceled.rs1use nautilus_core::{UUID4, UnixNanos};
17use ustr::Ustr;
18
19use crate::{
20 events::OrderCanceled,
21 identifiers::{AccountId, ClientOrderId, InstrumentId, StrategyId, TraderId, VenueOrderId},
22 stubs::{TestDefault, test_uuid},
23};
24
25#[derive(Debug, Clone, bon::Builder)]
31#[builder(finish_fn = into_spec)]
32pub struct OrderCanceledSpec {
33 #[builder(default = TraderId::test_default())]
34 pub trader_id: TraderId,
35 #[builder(default = StrategyId::test_default())]
36 pub strategy_id: StrategyId,
37 #[builder(default = InstrumentId::test_default())]
38 pub instrument_id: InstrumentId,
39 #[builder(default = ClientOrderId::test_default())]
40 pub client_order_id: ClientOrderId,
41 #[builder(default = test_uuid())]
42 pub event_id: UUID4,
43 #[builder(default = UnixNanos::default())]
44 pub ts_event: UnixNanos,
45 #[builder(default = UnixNanos::default())]
46 pub ts_init: UnixNanos,
47 #[builder(default = false)]
48 pub reconciliation: bool,
49 pub venue_order_id: Option<VenueOrderId>,
50 pub account_id: Option<AccountId>,
51 pub reason: Option<Ustr>,
52}
53
54impl<S: order_canceled_spec_builder::IsComplete> OrderCanceledSpecBuilder<S> {
55 #[must_use]
57 pub fn build(self) -> OrderCanceled {
58 let spec = self.into_spec();
59 OrderCanceled::new(
60 spec.trader_id,
61 spec.strategy_id,
62 spec.instrument_id,
63 spec.client_order_id,
64 spec.event_id,
65 spec.ts_event,
66 spec.ts_init,
67 spec.reconciliation,
68 spec.venue_order_id,
69 spec.account_id,
70 spec.reason,
71 )
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use rstest::rstest;
78
79 use super::*;
80 use crate::stubs::reset_test_uuid_rng;
81
82 #[rstest]
83 fn defaults_are_sensible() {
84 let event = OrderCanceledSpec::builder().build();
87 assert_eq!(event.trader_id, TraderId::test_default());
88 assert_eq!(event.strategy_id, StrategyId::test_default());
89 assert_eq!(event.instrument_id, InstrumentId::test_default());
90 assert_eq!(event.client_order_id, ClientOrderId::test_default());
91 assert_eq!(event.ts_event, UnixNanos::default());
92 assert_eq!(event.ts_init, UnixNanos::default());
93 assert!(!event.reconciliation);
94 assert_eq!(event.venue_order_id, None);
95 assert_eq!(event.account_id, None);
96 assert_eq!(event.reason, None);
97 }
98
99 #[rstest]
100 fn overrides_apply_through_constructor() {
101 let event = OrderCanceledSpec::builder()
102 .venue_order_id(VenueOrderId::from("V-1"))
103 .account_id(AccountId::from("SIM-002"))
104 .reconciliation(true)
105 .reason(Ustr::from("not-enough-liquidity"))
106 .build();
107
108 assert_eq!(event.venue_order_id, Some(VenueOrderId::from("V-1")));
109 assert_eq!(event.account_id, Some(AccountId::from("SIM-002")));
110 assert!(event.reconciliation);
111 assert_eq!(event.reason, Some(Ustr::from("not-enough-liquidity")));
112 assert_eq!(event.trader_id, TraderId::test_default());
113 }
114
115 #[rstest]
116 fn event_ids_are_unique_within_a_run() {
117 reset_test_uuid_rng();
118 let a = OrderCanceledSpec::builder().build();
119 let b = OrderCanceledSpec::builder().build();
120 let c = OrderCanceledSpec::builder().build();
121 assert_ne!(a.event_id, b.event_id);
122 assert_ne!(b.event_id, c.event_id);
123 assert_ne!(a.event_id, c.event_id);
124 }
125
126 #[rstest]
127 fn event_id_sequence_is_reproducible() {
128 reset_test_uuid_rng();
130 let first_run: Vec<_> = (0..3)
131 .map(|_| OrderCanceledSpec::builder().build().event_id)
132 .collect();
133
134 reset_test_uuid_rng();
135 let second_run: Vec<_> = (0..3)
136 .map(|_| OrderCanceledSpec::builder().build().event_id)
137 .collect();
138
139 assert_eq!(first_run, second_run);
140 }
141}