1use std::collections::HashMap;
17
18use arrow::{datatypes::Schema, error::ArrowError, record_batch::RecordBatch};
19use nautilus_model::events::{
20 OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied, OrderEmulated, OrderExpired,
21 OrderFillVoided, OrderFilled, OrderInitialized, OrderModifyRejected, OrderPendingCancel,
22 OrderPendingUpdate, OrderRejected, OrderReleased, OrderSubmitted, OrderTriggered, OrderUpdated,
23};
24
25use super::{
26 ArrowSchemaProvider, DecodeTypedFromRecordBatch, EncodeToRecordBatch, EncodingError,
27 KEY_INSTRUMENT_ID,
28 json::{JsonFieldSpec, decode_batch, encode_batch, metadata_for_type, schema_for_type},
29};
30
31const ORDER_INITIALIZED_FIELDS: &[JsonFieldSpec] = &[
32 JsonFieldSpec::utf8("trader_id", false),
33 JsonFieldSpec::utf8("strategy_id", false),
34 JsonFieldSpec::utf8("instrument_id", false),
35 JsonFieldSpec::utf8("client_order_id", false),
36 JsonFieldSpec::utf8("order_side", false),
37 JsonFieldSpec::utf8("order_type", false),
38 JsonFieldSpec::utf8("quantity", false),
39 JsonFieldSpec::utf8("time_in_force", false),
40 JsonFieldSpec::boolean("post_only", false),
41 JsonFieldSpec::boolean("reduce_only", false),
42 JsonFieldSpec::boolean("quote_quantity", false),
43 JsonFieldSpec::boolean("reconciliation", false),
44 JsonFieldSpec::utf8("event_id", false),
45 JsonFieldSpec::timestamp("ts_event", false),
46 JsonFieldSpec::timestamp("ts_init", false),
47 JsonFieldSpec::utf8("price", true),
48 JsonFieldSpec::utf8("trigger_price", true),
49 JsonFieldSpec::utf8("trigger_type", true),
50 JsonFieldSpec::utf8("limit_offset", true),
51 JsonFieldSpec::utf8("trailing_offset", true),
52 JsonFieldSpec::utf8("trailing_offset_type", true),
53 JsonFieldSpec::timestamp("expire_time", true),
54 JsonFieldSpec::utf8("display_qty", true),
55 JsonFieldSpec::utf8("emulation_trigger", true),
56 JsonFieldSpec::utf8("trigger_instrument_id", true),
57 JsonFieldSpec::utf8("contingency_type", true),
58 JsonFieldSpec::utf8("order_list_id", true),
59 JsonFieldSpec::utf8_json("linked_order_ids", true),
60 JsonFieldSpec::utf8("parent_order_id", true),
61 JsonFieldSpec::utf8("exec_algorithm_id", true),
62 JsonFieldSpec::utf8_json("exec_algorithm_params", true),
63 JsonFieldSpec::utf8("exec_spawn_id", true),
64 JsonFieldSpec::utf8_json("tags", true),
65 JsonFieldSpec::utf8("activation_price", true),
68];
69
70const ORDER_DENIED_FIELDS: &[JsonFieldSpec] = &[
71 JsonFieldSpec::utf8("trader_id", false),
72 JsonFieldSpec::utf8("strategy_id", false),
73 JsonFieldSpec::utf8("instrument_id", false),
74 JsonFieldSpec::utf8("client_order_id", false),
75 JsonFieldSpec::utf8("reason", false),
76 JsonFieldSpec::utf8("event_id", false),
77 JsonFieldSpec::timestamp("ts_event", false),
78 JsonFieldSpec::timestamp("ts_init", false),
79];
80
81const ORDER_EMULATED_FIELDS: &[JsonFieldSpec] = &[
82 JsonFieldSpec::utf8("trader_id", false),
83 JsonFieldSpec::utf8("strategy_id", false),
84 JsonFieldSpec::utf8("instrument_id", false),
85 JsonFieldSpec::utf8("client_order_id", false),
86 JsonFieldSpec::utf8("event_id", false),
87 JsonFieldSpec::timestamp("ts_event", false),
88 JsonFieldSpec::timestamp("ts_init", false),
89];
90
91const ORDER_SUBMITTED_FIELDS: &[JsonFieldSpec] = &[
92 JsonFieldSpec::utf8("trader_id", false),
93 JsonFieldSpec::utf8("strategy_id", false),
94 JsonFieldSpec::utf8("instrument_id", false),
95 JsonFieldSpec::utf8("client_order_id", false),
96 JsonFieldSpec::utf8("account_id", false),
97 JsonFieldSpec::utf8("event_id", false),
98 JsonFieldSpec::timestamp("ts_event", false),
99 JsonFieldSpec::timestamp("ts_init", false),
100];
101
102const ORDER_ACCEPTED_FIELDS: &[JsonFieldSpec] = &[
103 JsonFieldSpec::utf8("trader_id", false),
104 JsonFieldSpec::utf8("strategy_id", false),
105 JsonFieldSpec::utf8("instrument_id", false),
106 JsonFieldSpec::utf8("client_order_id", false),
107 JsonFieldSpec::utf8("venue_order_id", false),
108 JsonFieldSpec::utf8("account_id", false),
109 JsonFieldSpec::utf8("event_id", false),
110 JsonFieldSpec::timestamp("ts_event", false),
111 JsonFieldSpec::timestamp("ts_init", false),
112 JsonFieldSpec::boolean("reconciliation", false),
113];
114
115const ORDER_REJECTED_FIELDS: &[JsonFieldSpec] = &[
116 JsonFieldSpec::utf8("trader_id", false),
117 JsonFieldSpec::utf8("strategy_id", false),
118 JsonFieldSpec::utf8("instrument_id", false),
119 JsonFieldSpec::utf8("client_order_id", false),
120 JsonFieldSpec::utf8("account_id", false),
121 JsonFieldSpec::utf8("reason", false),
122 JsonFieldSpec::utf8("event_id", false),
123 JsonFieldSpec::timestamp("ts_event", false),
124 JsonFieldSpec::timestamp("ts_init", false),
125 JsonFieldSpec::boolean("reconciliation", false),
126 JsonFieldSpec::boolean("due_post_only", false),
127];
128
129const ORDER_PENDING_CANCEL_FIELDS: &[JsonFieldSpec] = &[
130 JsonFieldSpec::utf8("trader_id", false),
131 JsonFieldSpec::utf8("strategy_id", false),
132 JsonFieldSpec::utf8("instrument_id", false),
133 JsonFieldSpec::utf8("client_order_id", false),
134 JsonFieldSpec::utf8("account_id", true),
135 JsonFieldSpec::utf8("event_id", false),
136 JsonFieldSpec::timestamp("ts_event", false),
137 JsonFieldSpec::timestamp("ts_init", false),
138 JsonFieldSpec::boolean("reconciliation", false),
139 JsonFieldSpec::utf8("venue_order_id", true),
140];
141
142const ORDER_CANCELED_FIELDS: &[JsonFieldSpec] = &[
143 JsonFieldSpec::utf8("trader_id", false),
144 JsonFieldSpec::utf8("strategy_id", false),
145 JsonFieldSpec::utf8("instrument_id", false),
146 JsonFieldSpec::utf8("client_order_id", false),
147 JsonFieldSpec::utf8("event_id", false),
148 JsonFieldSpec::timestamp("ts_event", false),
149 JsonFieldSpec::timestamp("ts_init", false),
150 JsonFieldSpec::boolean("reconciliation", false),
151 JsonFieldSpec::utf8("venue_order_id", true),
152 JsonFieldSpec::utf8("account_id", true),
153];
154
155const ORDER_CANCEL_REJECTED_FIELDS: &[JsonFieldSpec] = &[
156 JsonFieldSpec::utf8("trader_id", false),
157 JsonFieldSpec::utf8("strategy_id", false),
158 JsonFieldSpec::utf8("instrument_id", false),
159 JsonFieldSpec::utf8("client_order_id", false),
160 JsonFieldSpec::utf8("reason", false),
161 JsonFieldSpec::utf8("event_id", false),
162 JsonFieldSpec::timestamp("ts_event", false),
163 JsonFieldSpec::timestamp("ts_init", false),
164 JsonFieldSpec::boolean("reconciliation", false),
165 JsonFieldSpec::utf8("venue_order_id", true),
166 JsonFieldSpec::utf8("account_id", true),
167];
168
169const ORDER_EXPIRED_FIELDS: &[JsonFieldSpec] = &[
170 JsonFieldSpec::utf8("trader_id", false),
171 JsonFieldSpec::utf8("strategy_id", false),
172 JsonFieldSpec::utf8("instrument_id", false),
173 JsonFieldSpec::utf8("client_order_id", false),
174 JsonFieldSpec::utf8("event_id", false),
175 JsonFieldSpec::timestamp("ts_event", false),
176 JsonFieldSpec::timestamp("ts_init", false),
177 JsonFieldSpec::boolean("reconciliation", false),
178 JsonFieldSpec::utf8("venue_order_id", true),
179 JsonFieldSpec::utf8("account_id", true),
180];
181
182const ORDER_TRIGGERED_FIELDS: &[JsonFieldSpec] = &[
183 JsonFieldSpec::utf8("trader_id", false),
184 JsonFieldSpec::utf8("strategy_id", false),
185 JsonFieldSpec::utf8("instrument_id", false),
186 JsonFieldSpec::utf8("client_order_id", false),
187 JsonFieldSpec::utf8("event_id", false),
188 JsonFieldSpec::timestamp("ts_event", false),
189 JsonFieldSpec::timestamp("ts_init", false),
190 JsonFieldSpec::boolean("reconciliation", false),
191 JsonFieldSpec::utf8("venue_order_id", true),
192 JsonFieldSpec::utf8("account_id", true),
193];
194
195const ORDER_PENDING_UPDATE_FIELDS: &[JsonFieldSpec] = &[
196 JsonFieldSpec::utf8("trader_id", false),
197 JsonFieldSpec::utf8("strategy_id", false),
198 JsonFieldSpec::utf8("instrument_id", false),
199 JsonFieldSpec::utf8("client_order_id", false),
200 JsonFieldSpec::utf8("account_id", true),
201 JsonFieldSpec::utf8("event_id", false),
202 JsonFieldSpec::timestamp("ts_event", false),
203 JsonFieldSpec::timestamp("ts_init", false),
204 JsonFieldSpec::boolean("reconciliation", false),
205 JsonFieldSpec::utf8("venue_order_id", true),
206];
207
208const ORDER_RELEASED_FIELDS: &[JsonFieldSpec] = &[
209 JsonFieldSpec::utf8("trader_id", false),
210 JsonFieldSpec::utf8("strategy_id", false),
211 JsonFieldSpec::utf8("instrument_id", false),
212 JsonFieldSpec::utf8("client_order_id", false),
213 JsonFieldSpec::utf8("released_price", false),
214 JsonFieldSpec::utf8("event_id", false),
215 JsonFieldSpec::timestamp("ts_event", false),
216 JsonFieldSpec::timestamp("ts_init", false),
217];
218
219const ORDER_MODIFY_REJECTED_FIELDS: &[JsonFieldSpec] = &[
220 JsonFieldSpec::utf8("trader_id", false),
221 JsonFieldSpec::utf8("strategy_id", false),
222 JsonFieldSpec::utf8("instrument_id", false),
223 JsonFieldSpec::utf8("client_order_id", false),
224 JsonFieldSpec::utf8("reason", false),
225 JsonFieldSpec::utf8("event_id", false),
226 JsonFieldSpec::timestamp("ts_event", false),
227 JsonFieldSpec::timestamp("ts_init", false),
228 JsonFieldSpec::boolean("reconciliation", false),
229 JsonFieldSpec::utf8("venue_order_id", true),
230 JsonFieldSpec::utf8("account_id", true),
231];
232
233const ORDER_UPDATED_FIELDS: &[JsonFieldSpec] = &[
234 JsonFieldSpec::utf8("trader_id", false),
235 JsonFieldSpec::utf8("strategy_id", false),
236 JsonFieldSpec::utf8("instrument_id", false),
237 JsonFieldSpec::utf8("client_order_id", false),
238 JsonFieldSpec::utf8("venue_order_id", true),
239 JsonFieldSpec::utf8("account_id", true),
240 JsonFieldSpec::utf8("quantity", false),
241 JsonFieldSpec::utf8("price", true),
242 JsonFieldSpec::utf8("trigger_price", true),
243 JsonFieldSpec::utf8("protection_price", true),
244 JsonFieldSpec::boolean("is_quote_quantity", false),
245 JsonFieldSpec::utf8("event_id", false),
246 JsonFieldSpec::timestamp("ts_event", false),
247 JsonFieldSpec::timestamp("ts_init", false),
248 JsonFieldSpec::boolean("reconciliation", false),
249];
250
251const ORDER_FILLED_FIELDS: &[JsonFieldSpec] = &[
252 JsonFieldSpec::utf8("trader_id", false),
253 JsonFieldSpec::utf8("strategy_id", false),
254 JsonFieldSpec::utf8("instrument_id", false),
255 JsonFieldSpec::utf8("client_order_id", false),
256 JsonFieldSpec::utf8("venue_order_id", false),
257 JsonFieldSpec::utf8("account_id", false),
258 JsonFieldSpec::utf8("trade_id", false),
259 JsonFieldSpec::utf8("order_side", false),
260 JsonFieldSpec::utf8("order_type", false),
261 JsonFieldSpec::utf8("last_qty", false),
262 JsonFieldSpec::utf8("last_px", false),
263 JsonFieldSpec::utf8("currency", false),
264 JsonFieldSpec::utf8("liquidity_side", false),
265 JsonFieldSpec::utf8("event_id", false),
266 JsonFieldSpec::timestamp("ts_event", false),
267 JsonFieldSpec::timestamp("ts_init", false),
268 JsonFieldSpec::boolean("reconciliation", false),
269 JsonFieldSpec::utf8("position_id", true),
270 JsonFieldSpec::utf8("commission", true),
271 JsonFieldSpec::utf8_json("info", true),
272];
273
274const ORDER_FILL_VOIDED_FIELDS: &[JsonFieldSpec] = &[
275 JsonFieldSpec::utf8("trader_id", false),
276 JsonFieldSpec::utf8("strategy_id", false),
277 JsonFieldSpec::utf8("instrument_id", false),
278 JsonFieldSpec::utf8("client_order_id", false),
279 JsonFieldSpec::utf8("venue_order_id", false),
280 JsonFieldSpec::utf8("account_id", false),
281 JsonFieldSpec::utf8("correction_id", false),
282 JsonFieldSpec::utf8("trade_id", false),
283 JsonFieldSpec::utf8("voided_qty", false),
284 JsonFieldSpec::utf8("commission_voided", true),
285 JsonFieldSpec::utf8("order_side", false),
286 JsonFieldSpec::utf8("order_type", false),
287 JsonFieldSpec::utf8("last_px", false),
288 JsonFieldSpec::utf8("currency", false),
289 JsonFieldSpec::utf8("liquidity_side", false),
290 JsonFieldSpec::utf8("position_id", true),
291 JsonFieldSpec::utf8("reason", true),
292 JsonFieldSpec::utf8_json("info", true),
293 JsonFieldSpec::utf8("event_id", false),
294 JsonFieldSpec::timestamp("ts_event", false),
295 JsonFieldSpec::timestamp("ts_init", false),
296 JsonFieldSpec::boolean("reconciliation", false),
297 JsonFieldSpec::boolean("is_reopened", false),
298 JsonFieldSpec::utf8("causation_id", true),
299];
300
301fn instrument_metadata(type_name: &'static str, instrument_id: &str) -> HashMap<String, String> {
302 let mut metadata = metadata_for_type(type_name);
303 metadata.insert(KEY_INSTRUMENT_ID.to_string(), instrument_id.to_string());
304 metadata
305}
306
307macro_rules! impl_order_event_arrow {
308 ($type:ty, $type_name:expr, $fields:expr) => {
309 impl ArrowSchemaProvider for $type {
310 fn get_schema(metadata: Option<HashMap<String, String>>) -> Schema {
311 schema_for_type($type_name, metadata, $fields)
312 }
313 }
314
315 impl EncodeToRecordBatch for $type {
316 fn encode_batch<T>(
317 metadata: &HashMap<String, String>,
318 data: &[T],
319 ) -> Result<RecordBatch, ArrowError>
320 where
321 T: std::borrow::Borrow<Self>,
322 {
323 encode_batch(
324 $type_name,
325 metadata,
326 data.iter().map(std::borrow::Borrow::borrow),
327 $fields,
328 )
329 }
330
331 fn metadata(&self) -> HashMap<String, String> {
332 instrument_metadata($type_name, &self.instrument_id.to_string())
333 }
334 }
335
336 impl DecodeTypedFromRecordBatch for $type {
337 fn decode_typed_batch(
338 metadata: &HashMap<String, String>,
339 record_batch: RecordBatch,
340 ) -> Result<Vec<Self>, EncodingError> {
341 decode_batch(metadata, &record_batch, $fields, Some($type_name))
342 }
343 }
344 };
345}
346
347impl_order_event_arrow!(
348 OrderInitialized,
349 "OrderInitialized",
350 ORDER_INITIALIZED_FIELDS
351);
352impl_order_event_arrow!(OrderDenied, "OrderDenied", ORDER_DENIED_FIELDS);
353impl_order_event_arrow!(OrderEmulated, "OrderEmulated", ORDER_EMULATED_FIELDS);
354impl_order_event_arrow!(OrderSubmitted, "OrderSubmitted", ORDER_SUBMITTED_FIELDS);
355impl_order_event_arrow!(OrderAccepted, "OrderAccepted", ORDER_ACCEPTED_FIELDS);
356impl_order_event_arrow!(OrderRejected, "OrderRejected", ORDER_REJECTED_FIELDS);
357impl_order_event_arrow!(
358 OrderPendingCancel,
359 "OrderPendingCancel",
360 ORDER_PENDING_CANCEL_FIELDS
361);
362impl_order_event_arrow!(OrderCanceled, "OrderCanceled", ORDER_CANCELED_FIELDS);
363impl_order_event_arrow!(
364 OrderCancelRejected,
365 "OrderCancelRejected",
366 ORDER_CANCEL_REJECTED_FIELDS
367);
368impl_order_event_arrow!(OrderExpired, "OrderExpired", ORDER_EXPIRED_FIELDS);
369impl_order_event_arrow!(OrderTriggered, "OrderTriggered", ORDER_TRIGGERED_FIELDS);
370impl_order_event_arrow!(
371 OrderPendingUpdate,
372 "OrderPendingUpdate",
373 ORDER_PENDING_UPDATE_FIELDS
374);
375impl_order_event_arrow!(OrderReleased, "OrderReleased", ORDER_RELEASED_FIELDS);
376impl_order_event_arrow!(
377 OrderModifyRejected,
378 "OrderModifyRejected",
379 ORDER_MODIFY_REJECTED_FIELDS
380);
381impl_order_event_arrow!(OrderUpdated, "OrderUpdated", ORDER_UPDATED_FIELDS);
382impl_order_event_arrow!(OrderFilled, "OrderFilled", ORDER_FILLED_FIELDS);
383impl_order_event_arrow!(OrderFillVoided, "OrderFillVoided", ORDER_FILL_VOIDED_FIELDS);
384
385#[cfg(test)]
386mod tests {
387 use std::str::FromStr;
388
389 use indexmap::IndexMap;
390 use nautilus_core::UUID4;
391 use nautilus_model::{
392 events::order::{
393 spec::OrderFillVoidedSpec,
394 stubs::{
395 order_accepted, order_cancel_rejected, order_denied_max_submitted_rate,
396 order_emulated, order_expired, order_filled, order_initialized_buy_limit,
397 order_modify_rejected, order_pending_cancel, order_pending_update,
398 order_rejected_insufficient_margin, order_released, order_submitted,
399 order_triggered, order_updated,
400 },
401 },
402 identifiers::PositionId,
403 types::{Money, Quantity},
404 };
405 use rstest::rstest;
406 use rust_decimal::Decimal;
407 use ustr::Ustr;
408
409 use super::*;
410
411 #[rstest]
412 fn test_order_initialized_round_trip(order_initialized_buy_limit: OrderInitialized) {
413 let event = OrderInitialized {
414 limit_offset: Some(Decimal::from_str("0.123456789123456789").unwrap()),
415 trailing_offset: Some(Decimal::from_str("0.987654321987654321").unwrap()),
416 ..order_initialized_buy_limit
417 };
418 let metadata = event.metadata();
419 let batch =
420 OrderInitialized::encode_batch(&metadata, std::slice::from_ref(&event)).unwrap();
421 let decoded =
422 OrderInitialized::decode_typed_batch(batch.schema().metadata(), batch).unwrap();
423
424 assert_eq!(decoded, vec![event]);
425 }
426
427 #[rstest]
428 fn test_order_filled_round_trip(order_filled: OrderFilled) {
429 let event = order_filled;
430 let metadata = event.metadata();
431 let batch = OrderFilled::encode_batch(&metadata, std::slice::from_ref(&event)).unwrap();
432 let decoded = OrderFilled::decode_typed_batch(batch.schema().metadata(), batch).unwrap();
433
434 assert_eq!(decoded, vec![event]);
435 }
436
437 #[rstest]
438 fn test_order_fill_voided_round_trip() {
439 roundtrip(OrderFillVoidedSpec::builder().is_reopened(true).build());
440 }
441
442 #[rstest]
443 fn test_order_fill_voided_populated_optionals_round_trip() {
444 let mut event = OrderFillVoidedSpec::builder()
445 .voided_qty(Quantity::from("0.561000"))
446 .commission_voided(Money::from("12.20000000 USDT"))
447 .position_id(PositionId::from("P-001"))
448 .reason(Ustr::from("VENUE_VOID"))
449 .info(IndexMap::from([(Ustr::from("source"), Ustr::from("test"))]))
450 .is_reopened(true)
451 .build();
452 event.causation_id = Some(UUID4::new());
453
454 roundtrip(event);
455 }
456
457 fn roundtrip<T>(event: T)
458 where
459 T: ArrowSchemaProvider
460 + EncodeToRecordBatch
461 + DecodeTypedFromRecordBatch
462 + Clone
463 + PartialEq
464 + std::fmt::Debug,
465 {
466 let metadata = event.metadata();
467 let batch = T::encode_batch(&metadata, std::slice::from_ref(&event)).unwrap();
468 let decoded = T::decode_typed_batch(batch.schema().metadata(), batch).unwrap();
469 assert_eq!(decoded, vec![event]);
470 }
471
472 #[rstest]
473 fn test_order_denied_round_trip(order_denied_max_submitted_rate: OrderDenied) {
474 roundtrip(order_denied_max_submitted_rate);
475 }
476
477 #[rstest]
478 fn test_order_submitted_round_trip(order_submitted: OrderSubmitted) {
479 roundtrip(order_submitted);
480 }
481
482 #[rstest]
483 fn test_order_accepted_round_trip(order_accepted: OrderAccepted) {
484 roundtrip(order_accepted);
485 }
486
487 #[rstest]
488 fn test_order_rejected_round_trip(order_rejected_insufficient_margin: OrderRejected) {
489 roundtrip(order_rejected_insufficient_margin);
490 }
491
492 #[rstest]
493 fn test_order_canceled_round_trip() {
494 use nautilus_model::events::OrderCanceled;
495 roundtrip(OrderCanceled::default());
496 }
497
498 #[rstest]
499 fn test_order_updated_round_trip(order_updated: OrderUpdated) {
500 roundtrip(order_updated);
501 }
502
503 #[rstest]
504 fn test_order_triggered_round_trip(order_triggered: OrderTriggered) {
505 roundtrip(order_triggered);
506 }
507
508 #[rstest]
509 fn test_order_expired_round_trip(order_expired: OrderExpired) {
510 roundtrip(order_expired);
511 }
512
513 #[rstest]
514 fn test_order_pending_update_round_trip(order_pending_update: OrderPendingUpdate) {
515 roundtrip(order_pending_update);
516 }
517
518 #[rstest]
519 fn test_order_pending_update_none_account_round_trip(order_pending_update: OrderPendingUpdate) {
520 roundtrip(OrderPendingUpdate {
521 account_id: None,
522 ..order_pending_update
523 });
524 }
525
526 #[rstest]
527 fn test_order_pending_cancel_round_trip(order_pending_cancel: OrderPendingCancel) {
528 roundtrip(order_pending_cancel);
529 }
530
531 #[rstest]
532 fn test_order_pending_cancel_none_account_round_trip(order_pending_cancel: OrderPendingCancel) {
533 roundtrip(OrderPendingCancel {
534 account_id: None,
535 ..order_pending_cancel
536 });
537 }
538
539 #[rstest]
540 fn test_order_cancel_rejected_round_trip(order_cancel_rejected: OrderCancelRejected) {
541 roundtrip(order_cancel_rejected);
542 }
543
544 #[rstest]
545 fn test_order_modify_rejected_round_trip(order_modify_rejected: OrderModifyRejected) {
546 roundtrip(order_modify_rejected);
547 }
548
549 #[rstest]
550 fn test_order_emulated_round_trip(order_emulated: OrderEmulated) {
551 roundtrip(order_emulated);
552 }
553
554 #[rstest]
555 fn test_order_released_round_trip(order_released: OrderReleased) {
556 roundtrip(order_released);
557 }
558}