nautilus_model/events/order/
updated.rs1use std::fmt::{Debug, Display};
17
18use nautilus_core::{UUID4, UnixNanos};
19use rust_decimal::Decimal;
20use serde::{Deserialize, Serialize};
21use ustr::Ustr;
22
23use crate::{
24 enums::{
25 ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType,
26 TriggerType,
27 },
28 events::OrderEvent,
29 identifiers::{
30 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
31 StrategyId, TradeId, TraderId, VenueOrderId,
32 },
33 types::{Currency, Money, Price, Quantity},
34};
35
36#[repr(C)]
37#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(tag = "type")]
39#[cfg_attr(
40 feature = "python",
41 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model", from_py_object)
42)]
43#[cfg_attr(
44 feature = "python",
45 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
46)]
47pub struct OrderUpdated {
48 pub trader_id: TraderId,
50 pub strategy_id: StrategyId,
52 pub instrument_id: InstrumentId,
54 pub client_order_id: ClientOrderId,
56 pub venue_order_id: Option<VenueOrderId>,
58 pub account_id: Option<AccountId>,
60 pub quantity: Quantity,
62 pub price: Option<Price>,
64 pub trigger_price: Option<Price>,
66 pub protection_price: Option<Price>,
68 #[serde(default)]
70 pub is_quote_quantity: bool,
71 pub event_id: UUID4,
73 pub ts_event: UnixNanos,
75 pub ts_init: UnixNanos,
77 pub reconciliation: bool,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub causation_id: Option<UUID4>,
82}
83
84impl OrderUpdated {
85 #[expect(clippy::too_many_arguments)]
87 #[must_use]
88 pub fn new(
89 trader_id: TraderId,
90 strategy_id: StrategyId,
91 instrument_id: InstrumentId,
92 client_order_id: ClientOrderId,
93 quantity: Quantity,
94 event_id: UUID4,
95 ts_event: UnixNanos,
96 ts_init: UnixNanos,
97 reconciliation: bool,
98 venue_order_id: Option<VenueOrderId>,
99 account_id: Option<AccountId>,
100 price: Option<Price>,
101 trigger_price: Option<Price>,
102 protection_price: Option<Price>,
103 is_quote_quantity: bool,
104 ) -> Self {
105 Self {
106 trader_id,
107 strategy_id,
108 instrument_id,
109 client_order_id,
110 quantity,
111 event_id,
112 ts_event,
113 ts_init,
114 reconciliation,
115 venue_order_id,
116 account_id,
117 price,
118 trigger_price,
119 protection_price,
120 is_quote_quantity,
121 causation_id: None,
122 }
123 }
124}
125
126impl Debug for OrderUpdated {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 write!(
129 f,
130 "{}(trader_id={}, strategy_id={}, instrument_id={}, client_order_id={}, \
131 venue_order_id={}, account_id={}, quantity={}, price={}, trigger_price={}, protection_price={}, event_id={}, ts_event={}, ts_init={})",
132 stringify!(OrderUpdated),
133 self.trader_id,
134 self.strategy_id,
135 self.instrument_id,
136 self.client_order_id,
137 self.venue_order_id
138 .map_or("None".to_string(), |venue_order_id| format!(
139 "{venue_order_id}"
140 )),
141 self.account_id
142 .map_or("None".to_string(), |account_id| format!("{account_id}")),
143 self.quantity,
144 self.price
145 .map_or("None".to_string(), |price| price.to_formatted_string()),
146 self.trigger_price
147 .map_or("None".to_string(), |trigger_price| trigger_price
148 .to_formatted_string()),
149 self.protection_price
150 .map_or("None".to_string(), |protection_price| protection_price
151 .to_formatted_string()),
152 self.event_id,
153 self.ts_event,
154 self.ts_init
155 )
156 }
157}
158
159impl Display for OrderUpdated {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(
162 f,
163 "{}(instrument_id={}, client_order_id={}, venue_order_id={}, account_id={}, quantity={}, price={}, trigger_price={}, protection_price={}, ts_event={})",
164 stringify!(OrderUpdated),
165 self.instrument_id,
166 self.client_order_id,
167 self.venue_order_id
168 .map_or("None".to_string(), |venue_order_id| format!(
169 "{venue_order_id}"
170 )),
171 self.account_id
172 .map_or("None".to_string(), |account_id| format!("{account_id}")),
173 self.quantity.to_formatted_string(),
174 self.price
175 .map_or("None".to_string(), |price| price.to_formatted_string()),
176 self.trigger_price
177 .map_or("None".to_string(), |trigger_price| trigger_price
178 .to_formatted_string()),
179 self.protection_price
180 .map_or("None".to_string(), |protection_price| protection_price
181 .to_formatted_string()),
182 self.ts_event
183 )
184 }
185}
186
187impl OrderEvent for OrderUpdated {
188 fn id(&self) -> UUID4 {
189 self.event_id
190 }
191
192 fn type_name(&self) -> &'static str {
193 stringify!(OrderUpdated)
194 }
195
196 fn order_type(&self) -> Option<OrderType> {
197 None
198 }
199
200 fn order_side(&self) -> Option<OrderSide> {
201 None
202 }
203
204 fn trader_id(&self) -> TraderId {
205 self.trader_id
206 }
207
208 fn strategy_id(&self) -> StrategyId {
209 self.strategy_id
210 }
211
212 fn instrument_id(&self) -> InstrumentId {
213 self.instrument_id
214 }
215
216 fn trade_id(&self) -> Option<TradeId> {
217 None
218 }
219
220 fn currency(&self) -> Option<Currency> {
221 None
222 }
223
224 fn client_order_id(&self) -> ClientOrderId {
225 self.client_order_id
226 }
227
228 fn reason(&self) -> Option<Ustr> {
229 None
230 }
231
232 fn quantity(&self) -> Option<Quantity> {
233 Some(self.quantity)
234 }
235
236 fn time_in_force(&self) -> Option<TimeInForce> {
237 None
238 }
239
240 fn liquidity_side(&self) -> Option<LiquiditySide> {
241 None
242 }
243
244 fn post_only(&self) -> Option<bool> {
245 None
246 }
247
248 fn reduce_only(&self) -> Option<bool> {
249 None
250 }
251
252 fn quote_quantity(&self) -> Option<bool> {
253 Some(self.is_quote_quantity)
254 }
255
256 fn reconciliation(&self) -> bool {
257 self.reconciliation
258 }
259
260 fn price(&self) -> Option<Price> {
261 self.price
262 }
263
264 fn last_px(&self) -> Option<Price> {
265 None
266 }
267
268 fn last_qty(&self) -> Option<Quantity> {
269 None
270 }
271
272 fn trigger_price(&self) -> Option<Price> {
273 self.trigger_price
274 }
275
276 fn trigger_type(&self) -> Option<TriggerType> {
277 None
278 }
279
280 fn limit_offset(&self) -> Option<Decimal> {
281 None
282 }
283
284 fn trailing_offset(&self) -> Option<Decimal> {
285 None
286 }
287
288 fn trailing_offset_type(&self) -> Option<TrailingOffsetType> {
289 None
290 }
291
292 fn expire_time(&self) -> Option<UnixNanos> {
293 None
294 }
295
296 fn display_qty(&self) -> Option<Quantity> {
297 None
298 }
299
300 fn emulation_trigger(&self) -> Option<TriggerType> {
301 None
302 }
303
304 fn trigger_instrument_id(&self) -> Option<InstrumentId> {
305 None
306 }
307
308 fn contingency_type(&self) -> Option<ContingencyType> {
309 None
310 }
311
312 fn order_list_id(&self) -> Option<OrderListId> {
313 None
314 }
315
316 fn linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
317 None
318 }
319
320 fn parent_order_id(&self) -> Option<ClientOrderId> {
321 None
322 }
323
324 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
325 None
326 }
327
328 fn exec_spawn_id(&self) -> Option<ClientOrderId> {
329 None
330 }
331
332 fn venue_order_id(&self) -> Option<VenueOrderId> {
333 self.venue_order_id
334 }
335
336 fn account_id(&self) -> Option<AccountId> {
337 self.account_id
338 }
339
340 fn position_id(&self) -> Option<PositionId> {
341 None
342 }
343
344 fn commission(&self) -> Option<Money> {
345 None
346 }
347
348 fn ts_event(&self) -> UnixNanos {
349 self.ts_event
350 }
351
352 fn ts_init(&self) -> UnixNanos {
353 self.ts_init
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use rstest::rstest;
360
361 use crate::events::order::{stubs::*, updated::OrderUpdated};
362
363 #[rstest]
364 fn test_order_updated_display(order_updated: OrderUpdated) {
365 let display = format!("{order_updated}");
366 assert_eq!(
367 display,
368 "OrderUpdated(instrument_id=BTCUSDT.COINBASE, client_order_id=O-19700101-000000-001-001-1, venue_order_id=001, account_id=SIM-001, quantity=100, price=22_000, trigger_price=None, protection_price=None, ts_event=0)"
369 );
370 }
371
372 #[rstest]
373 fn test_order_updated_serialization() {
374 let original = OrderUpdated::default();
375 let json = serde_json::to_string(&original).unwrap();
376 let deserialized: OrderUpdated = serde_json::from_str(&json).unwrap();
377 assert_eq!(original, deserialized);
378 }
379}