nautilus_model/events/order/spec/
filled.rs1use nautilus_core::{UUID4, UnixNanos};
17
18use crate::{
19 enums::{LiquiditySide, OrderSide, OrderType},
20 events::OrderFilled,
21 identifiers::{
22 AccountId, ClientOrderId, InstrumentId, PositionId, StrategyId, TradeId, TraderId,
23 VenueOrderId,
24 },
25 stubs::{TestDefault, test_uuid},
26 types::{Currency, Money, Price, Quantity},
27};
28
29#[derive(Debug, Clone, bon::Builder)]
35#[builder(finish_fn = into_spec)]
36pub struct OrderFilledSpec {
37 #[builder(default = TraderId::test_default())]
38 pub trader_id: TraderId,
39 #[builder(default = StrategyId::test_default())]
40 pub strategy_id: StrategyId,
41 #[builder(default = InstrumentId::test_default())]
42 pub instrument_id: InstrumentId,
43 #[builder(default = ClientOrderId::test_default())]
44 pub client_order_id: ClientOrderId,
45 #[builder(default = VenueOrderId::test_default())]
46 pub venue_order_id: VenueOrderId,
47 #[builder(default = AccountId::test_default())]
48 pub account_id: AccountId,
49 #[builder(default = TradeId::test_default())]
50 pub trade_id: TradeId,
51 #[builder(default = OrderSide::Buy)]
52 pub order_side: OrderSide,
53 #[builder(default = OrderType::Market)]
54 pub order_type: OrderType,
55 #[builder(default = Quantity::new(100_000.0, 0))]
56 pub last_qty: Quantity,
57 #[builder(default = Price::from("1.00000"))]
58 pub last_px: Price,
59 #[builder(default = Currency::USD())]
60 pub currency: Currency,
61 #[builder(default = LiquiditySide::Taker)]
62 pub liquidity_side: LiquiditySide,
63 #[builder(default = test_uuid())]
64 pub event_id: UUID4,
65 #[builder(default = UnixNanos::default())]
66 pub ts_event: UnixNanos,
67 #[builder(default = UnixNanos::default())]
68 pub ts_init: UnixNanos,
69 #[builder(default = false)]
70 pub reconciliation: bool,
71 pub position_id: Option<PositionId>,
72 pub commission: Option<Money>,
73}
74
75impl<S: order_filled_spec_builder::IsComplete> OrderFilledSpecBuilder<S> {
76 #[must_use]
78 pub fn build(self) -> OrderFilled {
79 let spec = self.into_spec();
80 OrderFilled::new(
81 spec.trader_id,
82 spec.strategy_id,
83 spec.instrument_id,
84 spec.client_order_id,
85 spec.venue_order_id,
86 spec.account_id,
87 spec.trade_id,
88 spec.order_side,
89 spec.order_type,
90 spec.last_qty,
91 spec.last_px,
92 spec.currency,
93 spec.liquidity_side,
94 spec.event_id,
95 spec.ts_event,
96 spec.ts_init,
97 spec.reconciliation,
98 spec.position_id,
99 spec.commission,
100 )
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use rstest::rstest;
107
108 use super::*;
109 use crate::stubs::reset_test_uuid_rng;
110
111 #[rstest]
112 fn defaults_are_sensible() {
113 let order = OrderFilledSpec::builder().build();
116 assert_eq!(order.trader_id, TraderId::test_default());
117 assert_eq!(order.strategy_id, StrategyId::test_default());
118 assert_eq!(order.instrument_id, InstrumentId::test_default());
119 assert_eq!(order.client_order_id, ClientOrderId::test_default());
120 assert_eq!(order.venue_order_id, VenueOrderId::test_default());
121 assert_eq!(order.account_id, AccountId::test_default());
122 assert_eq!(order.trade_id, TradeId::test_default());
123 assert_eq!(order.order_side, OrderSide::Buy);
124 assert_eq!(order.order_type, OrderType::Market);
125 assert_eq!(order.last_qty, Quantity::new(100_000.0, 0));
126 assert_eq!(order.last_px, Price::from("1.00000"));
127 assert_eq!(order.currency, Currency::USD());
128 assert_eq!(order.liquidity_side, LiquiditySide::Taker);
129 assert_eq!(order.ts_event, UnixNanos::default());
130 assert_eq!(order.ts_init, UnixNanos::default());
131 assert!(!order.reconciliation);
132 assert_eq!(order.position_id, None);
133 assert_eq!(order.commission, None);
134 }
135
136 #[rstest]
137 fn overrides_apply_through_constructor() {
138 let order = OrderFilledSpec::builder()
139 .order_side(OrderSide::Sell)
140 .last_qty(Quantity::from("50"))
141 .last_px(Price::from("1.25000"))
142 .commission(Money::from("0.5 USD"))
143 .build();
144
145 assert_eq!(order.order_side, OrderSide::Sell);
146 assert_eq!(order.last_qty, Quantity::from("50"));
147 assert_eq!(order.last_px, Price::from("1.25000"));
148 assert_eq!(order.commission, Some(Money::from("0.5 USD")));
149 assert_eq!(order.trader_id, TraderId::test_default());
150 }
151
152 #[rstest]
153 fn event_ids_are_unique_within_a_run() {
154 reset_test_uuid_rng();
155 let a = OrderFilledSpec::builder().build();
156 let b = OrderFilledSpec::builder().build();
157 let c = OrderFilledSpec::builder().build();
158 assert_ne!(a.event_id, b.event_id);
159 assert_ne!(b.event_id, c.event_id);
160 assert_ne!(a.event_id, c.event_id);
161 }
162
163 #[rstest]
164 fn event_id_sequence_is_reproducible() {
165 reset_test_uuid_rng();
167 let first_run: Vec<_> = (0..3)
168 .map(|_| OrderFilledSpec::builder().build().event_id)
169 .collect();
170
171 reset_test_uuid_rng();
172 let second_run: Vec<_> = (0..3)
173 .map(|_| OrderFilledSpec::builder().build().event_id)
174 .collect();
175
176 assert_eq!(first_run, second_run);
177 }
178}