nautilus_model/events/order/spec/
updated.rs1use nautilus_core::{UUID4, UnixNanos};
17
18use crate::{
19 events::OrderUpdated,
20 identifiers::{AccountId, ClientOrderId, InstrumentId, StrategyId, TraderId, VenueOrderId},
21 stubs::{TestDefault, test_uuid},
22 types::{Price, Quantity},
23};
24
25#[derive(Debug, Clone, bon::Builder)]
31#[builder(finish_fn = into_spec)]
32pub struct OrderUpdatedSpec {
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 = Quantity::new(100_000.0, 0))]
42 pub quantity: Quantity,
43 #[builder(default = test_uuid())]
44 pub event_id: UUID4,
45 #[builder(default = UnixNanos::default())]
46 pub ts_event: UnixNanos,
47 #[builder(default = UnixNanos::default())]
48 pub ts_init: UnixNanos,
49 #[builder(default = false)]
50 pub reconciliation: bool,
51 pub venue_order_id: Option<VenueOrderId>,
52 pub account_id: Option<AccountId>,
53 pub price: Option<Price>,
54 pub trigger_price: Option<Price>,
55 pub protection_price: Option<Price>,
56 #[builder(default = false)]
57 pub is_quote_quantity: bool,
58}
59
60impl<S: order_updated_spec_builder::IsComplete> OrderUpdatedSpecBuilder<S> {
61 #[must_use]
63 pub fn build(self) -> OrderUpdated {
64 let spec = self.into_spec();
65 OrderUpdated::new(
66 spec.trader_id,
67 spec.strategy_id,
68 spec.instrument_id,
69 spec.client_order_id,
70 spec.quantity,
71 spec.event_id,
72 spec.ts_event,
73 spec.ts_init,
74 spec.reconciliation,
75 spec.venue_order_id,
76 spec.account_id,
77 spec.price,
78 spec.trigger_price,
79 spec.protection_price,
80 spec.is_quote_quantity,
81 )
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use rstest::rstest;
88
89 use super::*;
90 use crate::stubs::reset_test_uuid_rng;
91
92 #[rstest]
93 fn defaults_are_sensible() {
94 let event = OrderUpdatedSpec::builder().build();
97 assert_eq!(event.trader_id, TraderId::test_default());
98 assert_eq!(event.strategy_id, StrategyId::test_default());
99 assert_eq!(event.instrument_id, InstrumentId::test_default());
100 assert_eq!(event.client_order_id, ClientOrderId::test_default());
101 assert_eq!(event.quantity, Quantity::new(100_000.0, 0));
102 assert_eq!(event.ts_event, UnixNanos::default());
103 assert_eq!(event.ts_init, UnixNanos::default());
104 assert_eq!(event.reconciliation, 0);
105 assert_eq!(event.venue_order_id, None);
106 assert_eq!(event.account_id, None);
107 assert_eq!(event.price, None);
108 assert_eq!(event.trigger_price, None);
109 assert_eq!(event.protection_price, None);
110 assert!(!event.is_quote_quantity);
111 }
112
113 #[rstest]
114 fn overrides_apply_through_constructor() {
115 let event = OrderUpdatedSpec::builder()
116 .quantity(Quantity::from(50_000))
117 .price(Price::from("22000"))
118 .is_quote_quantity(true)
119 .build();
120
121 assert_eq!(event.quantity, Quantity::from(50_000));
122 assert_eq!(event.price, Some(Price::from("22000")));
123 assert!(event.is_quote_quantity);
124 assert_eq!(event.trader_id, TraderId::test_default());
125 }
126
127 #[rstest]
128 fn event_ids_are_unique_within_a_run() {
129 reset_test_uuid_rng();
130 let a = OrderUpdatedSpec::builder().build();
131 let b = OrderUpdatedSpec::builder().build();
132 let c = OrderUpdatedSpec::builder().build();
133 assert_ne!(a.event_id, b.event_id);
134 assert_ne!(b.event_id, c.event_id);
135 assert_ne!(a.event_id, c.event_id);
136 }
137
138 #[rstest]
139 fn event_id_sequence_is_reproducible() {
140 reset_test_uuid_rng();
142 let first_run: Vec<_> = (0..3)
143 .map(|_| OrderUpdatedSpec::builder().build().event_id)
144 .collect();
145
146 reset_test_uuid_rng();
147 let second_run: Vec<_> = (0..3)
148 .map(|_| OrderUpdatedSpec::builder().build().event_id)
149 .collect();
150
151 assert_eq!(first_run, second_run);
152 }
153}