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