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