1use borsh::{BorshDeserialize as Deserialize, BorshSerialize as Serialize};
4
5use crate::{
6 quantities::{BaseLots, QuoteLots, Ticks, WrapperU64},
7 state::{SelfTradeBehavior, Side},
8};
9
10pub trait OrderPacketMetadata {
11 fn is_take_only(&self) -> bool {
12 self.is_ioc() || self.is_fok()
13 }
14 fn is_ioc(&self) -> bool;
15 fn is_fok(&self) -> bool;
16 fn is_post_only(&self) -> bool;
17 fn no_deposit_or_withdrawal(&self) -> bool;
18}
19
20#[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Eq, Debug)]
21pub enum OrderPacket {
22 PostOnly {
25 side: Side,
26
27 price_in_ticks: Ticks,
29
30 num_base_lots: BaseLots,
32
33 client_order_id: u128,
35
36 reject_post_only: bool,
39
40 use_only_deposited_funds: bool,
44
45 last_valid_slot: Option<u64>,
47
48 last_valid_unix_timestamp_in_seconds: Option<u64>,
50
51 fail_silently_on_insufficient_funds: bool,
53 },
54
55 Limit {
59 side: Side,
60
61 price_in_ticks: Ticks,
63
64 num_base_lots: BaseLots,
66
67 self_trade_behavior: SelfTradeBehavior,
69
70 match_limit: Option<u64>,
72
73 client_order_id: u128,
75
76 use_only_deposited_funds: bool,
80
81 last_valid_slot: Option<u64>,
83
84 last_valid_unix_timestamp_in_seconds: Option<u64>,
86
87 fail_silently_on_insufficient_funds: bool,
89 },
90
91 ImmediateOrCancel {
98 side: Side,
99
100 price_in_ticks: Option<Ticks>,
105
106 num_base_lots: BaseLots,
109
110 num_quote_lots: QuoteLots,
113
114 min_base_lots_to_fill: BaseLots,
117
118 min_quote_lots_to_fill: QuoteLots,
121
122 self_trade_behavior: SelfTradeBehavior,
124
125 match_limit: Option<u64>,
127
128 client_order_id: u128,
130
131 use_only_deposited_funds: bool,
135
136 last_valid_slot: Option<u64>,
138
139 last_valid_unix_timestamp_in_seconds: Option<u64>,
141 },
142}
143
144impl OrderPacketMetadata for OrderPacket {
145 fn is_ioc(&self) -> bool {
146 matches!(self, OrderPacket::ImmediateOrCancel { .. })
147 }
148
149 fn is_fok(&self) -> bool {
150 match self {
151 &Self::ImmediateOrCancel {
152 num_base_lots,
153 num_quote_lots,
154 min_base_lots_to_fill,
155 min_quote_lots_to_fill,
156 ..
157 } => {
158 num_base_lots > BaseLots::ZERO && num_base_lots == min_base_lots_to_fill
159 || num_quote_lots > QuoteLots::ZERO && num_quote_lots == min_quote_lots_to_fill
160 }
161 _ => false,
162 }
163 }
164
165 fn is_post_only(&self) -> bool {
166 matches!(self, OrderPacket::PostOnly { .. })
167 }
168
169 fn no_deposit_or_withdrawal(&self) -> bool {
170 match *self {
171 Self::PostOnly {
172 use_only_deposited_funds,
173 ..
174 } => use_only_deposited_funds,
175 Self::Limit {
176 use_only_deposited_funds,
177 ..
178 } => use_only_deposited_funds,
179 Self::ImmediateOrCancel {
180 use_only_deposited_funds,
181 ..
182 } => use_only_deposited_funds,
183 }
184 }
185}
186
187impl OrderPacket {
188 pub fn new_post_only_default(side: Side, price_in_ticks: u64, num_base_lots: u64) -> Self {
189 Self::PostOnly {
190 side,
191 price_in_ticks: Ticks::new(price_in_ticks),
192 num_base_lots: BaseLots::new(num_base_lots),
193 client_order_id: 0,
194 reject_post_only: true,
195 use_only_deposited_funds: false,
196 last_valid_slot: None,
197 last_valid_unix_timestamp_in_seconds: None,
198 fail_silently_on_insufficient_funds: false,
199 }
200 }
201
202 pub fn new_post_only_default_with_client_order_id(
203 side: Side,
204 price_in_ticks: u64,
205 num_base_lots: u64,
206 client_order_id: u128,
207 ) -> Self {
208 Self::PostOnly {
209 side,
210 price_in_ticks: Ticks::new(price_in_ticks),
211 num_base_lots: BaseLots::new(num_base_lots),
212 client_order_id,
213 reject_post_only: true,
214 use_only_deposited_funds: false,
215 last_valid_slot: None,
216 last_valid_unix_timestamp_in_seconds: None,
217 fail_silently_on_insufficient_funds: false,
218 }
219 }
220
221 pub fn new_adjustable_post_only_default_with_client_order_id(
222 side: Side,
223 price_in_ticks: u64,
224 num_base_lots: u64,
225 client_order_id: u128,
226 ) -> Self {
227 Self::PostOnly {
228 side,
229 price_in_ticks: Ticks::new(price_in_ticks),
230 num_base_lots: BaseLots::new(num_base_lots),
231 client_order_id,
232 reject_post_only: false,
233 use_only_deposited_funds: false,
234 last_valid_slot: None,
235 last_valid_unix_timestamp_in_seconds: None,
236 fail_silently_on_insufficient_funds: false,
237 }
238 }
239
240 pub fn new_post_only(
241 side: Side,
242 price_in_ticks: u64,
243 num_base_lots: u64,
244 client_order_id: u128,
245 reject_post_only: bool,
246 use_only_deposited_funds: bool,
247 ) -> Self {
248 Self::PostOnly {
249 side,
250 price_in_ticks: Ticks::new(price_in_ticks),
251 num_base_lots: BaseLots::new(num_base_lots),
252 client_order_id,
253 reject_post_only,
254 use_only_deposited_funds,
255 last_valid_slot: None,
256 last_valid_unix_timestamp_in_seconds: None,
257 fail_silently_on_insufficient_funds: false,
258 }
259 }
260
261 pub fn new_limit_order_default(side: Side, price_in_ticks: u64, num_base_lots: u64) -> Self {
262 Self::new_limit_order(
263 side,
264 price_in_ticks,
265 num_base_lots,
266 SelfTradeBehavior::CancelProvide,
267 None,
268 0,
269 false,
270 )
271 }
272
273 pub fn new_limit_order_default_with_client_order_id(
274 side: Side,
275 price_in_ticks: u64,
276 num_base_lots: u64,
277 client_order_id: u128,
278 ) -> Self {
279 Self::new_limit_order(
280 side,
281 price_in_ticks,
282 num_base_lots,
283 SelfTradeBehavior::CancelProvide,
284 None,
285 client_order_id,
286 false,
287 )
288 }
289
290 pub fn new_limit_order(
291 side: Side,
292 price_in_ticks: u64,
293 num_base_lots: u64,
294 self_trade_behavior: SelfTradeBehavior,
295 match_limit: Option<u64>,
296 client_order_id: u128,
297 use_only_deposited_funds: bool,
298 ) -> Self {
299 Self::Limit {
300 side,
301 price_in_ticks: Ticks::new(price_in_ticks),
302 num_base_lots: BaseLots::new(num_base_lots),
303 self_trade_behavior,
304 match_limit,
305 client_order_id,
306 use_only_deposited_funds,
307 last_valid_slot: None,
308 last_valid_unix_timestamp_in_seconds: None,
309 fail_silently_on_insufficient_funds: false,
310 }
311 }
312
313 pub fn new_fok_sell_with_limit_price(
314 target_price_in_ticks: u64,
315 base_lot_budget: u64,
316 self_trade_behavior: SelfTradeBehavior,
317 match_limit: Option<u64>,
318 client_order_id: u128,
319 use_only_deposited_funds: bool,
320 ) -> Self {
321 Self::new_ioc(
322 Side::Ask,
323 Some(target_price_in_ticks),
324 base_lot_budget,
325 0,
326 base_lot_budget,
327 0,
328 self_trade_behavior,
329 match_limit,
330 client_order_id,
331 use_only_deposited_funds,
332 None,
333 None,
334 )
335 }
336
337 pub fn new_fok_buy_with_limit_price(
338 target_price_in_ticks: u64,
339 base_lot_budget: u64,
340 self_trade_behavior: SelfTradeBehavior,
341 match_limit: Option<u64>,
342 client_order_id: u128,
343 use_only_deposited_funds: bool,
344 ) -> Self {
345 Self::new_ioc(
346 Side::Bid,
347 Some(target_price_in_ticks),
348 base_lot_budget,
349 0,
350 base_lot_budget,
351 0,
352 self_trade_behavior,
353 match_limit,
354 client_order_id,
355 use_only_deposited_funds,
356 None,
357 None,
358 )
359 }
360
361 pub fn new_ioc_sell_with_limit_price(
362 price_in_ticks: u64,
363 num_base_lots: u64,
364 self_trade_behavior: SelfTradeBehavior,
365 match_limit: Option<u64>,
366 client_order_id: u128,
367 use_only_deposited_funds: bool,
368 ) -> Self {
369 Self::new_ioc(
370 Side::Ask,
371 Some(price_in_ticks),
372 num_base_lots,
373 0,
374 0,
375 0,
376 self_trade_behavior,
377 match_limit,
378 client_order_id,
379 use_only_deposited_funds,
380 None,
381 None,
382 )
383 }
384
385 pub fn new_ioc_buy_with_limit_price(
386 price_in_ticks: u64,
387 num_quote_lots: u64,
388 self_trade_behavior: SelfTradeBehavior,
389 match_limit: Option<u64>,
390 client_order_id: u128,
391 use_only_deposited_funds: bool,
392 ) -> Self {
393 Self::new_ioc(
394 Side::Bid,
395 Some(price_in_ticks),
396 0,
397 num_quote_lots,
398 0,
399 0,
400 self_trade_behavior,
401 match_limit,
402 client_order_id,
403 use_only_deposited_funds,
404 None,
405 None,
406 )
407 }
408
409 pub fn new_ioc_by_lots(
410 side: Side,
411 price_in_ticks: u64,
412 base_lot_budget: u64,
413 self_trade_behavior: SelfTradeBehavior,
414 match_limit: Option<u64>,
415 client_order_id: u128,
416 use_only_deposited_funds: bool,
417 ) -> Self {
418 Self::new_ioc(
419 side,
420 Some(price_in_ticks),
421 base_lot_budget,
422 0,
423 0,
424 0,
425 self_trade_behavior,
426 match_limit,
427 client_order_id,
428 use_only_deposited_funds,
429 None,
430 None,
431 )
432 }
433
434 pub fn new_ioc_buy_with_slippage(quote_lots_in: u64, min_base_lots_out: u64) -> Self {
435 Self::new_ioc(
436 Side::Bid,
437 None,
438 0,
439 quote_lots_in,
440 min_base_lots_out,
441 0,
442 SelfTradeBehavior::CancelProvide,
443 None,
444 0,
445 false,
446 None,
447 None,
448 )
449 }
450
451 pub fn new_ioc_sell_with_slippage(base_lots_in: u64, min_quote_lots_out: u64) -> Self {
452 Self::new_ioc(
453 Side::Ask,
454 None,
455 base_lots_in,
456 0,
457 0,
458 min_quote_lots_out,
459 SelfTradeBehavior::CancelProvide,
460 None,
461 0,
462 false,
463 None,
464 None,
465 )
466 }
467
468 #[allow(clippy::too_many_arguments)]
469 pub fn new_ioc(
470 side: Side,
471 price_in_ticks: Option<u64>,
472 num_base_lots: u64,
473 num_quote_lots: u64,
474 min_base_lots_to_fill: u64,
475 min_quote_lots_to_fill: u64,
476 self_trade_behavior: SelfTradeBehavior,
477 match_limit: Option<u64>,
478 client_order_id: u128,
479 use_only_deposited_funds: bool,
480 last_valid_slot: Option<u64>,
481 last_valid_unix_timestamp_in_seconds: Option<u64>,
482 ) -> Self {
483 Self::ImmediateOrCancel {
484 side,
485 price_in_ticks: price_in_ticks.map(Ticks::new),
486 num_base_lots: BaseLots::new(num_base_lots),
487 num_quote_lots: QuoteLots::new(num_quote_lots),
488 min_base_lots_to_fill: BaseLots::new(min_base_lots_to_fill),
489 min_quote_lots_to_fill: QuoteLots::new(min_quote_lots_to_fill),
490 self_trade_behavior,
491 match_limit,
492 client_order_id,
493 use_only_deposited_funds,
494 last_valid_slot,
495 last_valid_unix_timestamp_in_seconds,
496 }
497 }
498}
499
500impl OrderPacket {
501 pub fn side(&self) -> Side {
502 match self {
503 Self::PostOnly { side, .. } => *side,
504 Self::Limit { side, .. } => *side,
505 Self::ImmediateOrCancel { side, .. } => *side,
506 }
507 }
508
509 pub fn fail_silently_on_insufficient_funds(&self) -> bool {
510 match self {
511 Self::PostOnly {
512 fail_silently_on_insufficient_funds,
513 ..
514 } => *fail_silently_on_insufficient_funds,
515 Self::Limit {
516 fail_silently_on_insufficient_funds,
517 ..
518 } => *fail_silently_on_insufficient_funds,
519 Self::ImmediateOrCancel { .. } => false,
520 }
521 }
522
523 pub fn client_order_id(&self) -> u128 {
524 match self {
525 Self::PostOnly {
526 client_order_id, ..
527 } => *client_order_id,
528 Self::Limit {
529 client_order_id, ..
530 } => *client_order_id,
531 Self::ImmediateOrCancel {
532 client_order_id, ..
533 } => *client_order_id,
534 }
535 }
536
537 pub fn num_base_lots(&self) -> BaseLots {
538 match self {
539 Self::PostOnly { num_base_lots, .. } => *num_base_lots,
540 Self::Limit { num_base_lots, .. } => *num_base_lots,
541 Self::ImmediateOrCancel { num_base_lots, .. } => *num_base_lots,
542 }
543 }
544
545 pub fn num_quote_lots(&self) -> QuoteLots {
546 match self {
547 Self::PostOnly { .. } => QuoteLots::ZERO,
548 Self::Limit { .. } => QuoteLots::ZERO,
549 Self::ImmediateOrCancel { num_quote_lots, .. } => *num_quote_lots,
550 }
551 }
552
553 pub fn base_lot_budget(&self) -> BaseLots {
554 let base_lots = self.num_base_lots();
555 if base_lots == BaseLots::ZERO {
556 BaseLots::MAX
557 } else {
558 base_lots
559 }
560 }
561
562 pub fn quote_lot_budget(&self) -> Option<QuoteLots> {
563 let quote_lots = self.num_quote_lots();
564 if quote_lots == QuoteLots::ZERO {
565 None
566 } else {
567 Some(quote_lots)
568 }
569 }
570
571 pub fn match_limit(&self) -> u64 {
572 match self {
573 Self::PostOnly { .. } => u64::MAX,
574 Self::Limit { match_limit, .. } => match_limit.unwrap_or(u64::MAX),
575 Self::ImmediateOrCancel { match_limit, .. } => match_limit.unwrap_or(u64::MAX),
576 }
577 }
578
579 pub fn self_trade_behavior(&self) -> SelfTradeBehavior {
580 match self {
581 Self::PostOnly { .. } => panic!("PostOnly orders do not have a self trade behavior"),
582 Self::Limit {
583 self_trade_behavior,
584 ..
585 } => *self_trade_behavior,
586 Self::ImmediateOrCancel {
587 self_trade_behavior,
588 ..
589 } => *self_trade_behavior,
590 }
591 }
592
593 pub fn get_price_in_ticks(&self) -> Ticks {
594 match self {
595 Self::PostOnly { price_in_ticks, .. } => *price_in_ticks,
596 Self::Limit { price_in_ticks, .. } => *price_in_ticks,
597 Self::ImmediateOrCancel { price_in_ticks, .. } => {
598 price_in_ticks.unwrap_or(match self.side() {
599 Side::Bid => Ticks::MAX,
600 Side::Ask => Ticks::MIN,
601 })
602 }
603 }
604 }
605
606 pub fn set_price_in_ticks(&mut self, price_in_ticks: Ticks) {
607 match self {
608 Self::PostOnly {
609 price_in_ticks: old_price_in_ticks,
610 ..
611 } => *old_price_in_ticks = price_in_ticks,
612 Self::Limit {
613 price_in_ticks: old_price_in_ticks,
614 ..
615 } => *old_price_in_ticks = price_in_ticks,
616 Self::ImmediateOrCancel {
617 price_in_ticks: old_price_in_ticks,
618 ..
619 } => *old_price_in_ticks = Some(price_in_ticks),
620 }
621 }
622
623 pub fn get_last_valid_slot(&self) -> Option<u64> {
624 match self {
625 Self::PostOnly {
626 last_valid_slot, ..
627 } => *last_valid_slot,
628 Self::Limit {
629 last_valid_slot, ..
630 } => *last_valid_slot,
631 Self::ImmediateOrCancel {
632 last_valid_slot, ..
633 } => *last_valid_slot,
634 }
635 }
636
637 pub fn get_last_valid_unix_timestamp_in_seconds(&self) -> Option<u64> {
638 match self {
639 Self::PostOnly {
640 last_valid_unix_timestamp_in_seconds,
641 ..
642 } => *last_valid_unix_timestamp_in_seconds,
643 Self::Limit {
644 last_valid_unix_timestamp_in_seconds,
645 ..
646 } => *last_valid_unix_timestamp_in_seconds,
647 Self::ImmediateOrCancel {
648 last_valid_unix_timestamp_in_seconds,
649 ..
650 } => *last_valid_unix_timestamp_in_seconds,
651 }
652 }
653
654 pub fn is_expired(&self, current_slot: u64, current_unix_timestamp_in_seconds: u64) -> bool {
655 if let Some(last_valid_slot) = self.get_last_valid_slot() {
656 if current_slot > last_valid_slot {
657 return true;
658 }
659 }
660 if let Some(last_valid_unix_timestamp_in_seconds) =
661 self.get_last_valid_unix_timestamp_in_seconds()
662 {
663 if current_unix_timestamp_in_seconds > last_valid_unix_timestamp_in_seconds {
664 return true;
665 }
666 }
667 false
668 }
669}
670
671pub fn decode_order_packet(bytes: &[u8]) -> Option<OrderPacket> {
672 match OrderPacket::try_from_slice(bytes) {
674 Ok(order_packet) => Some(order_packet),
675 Err(_) => {
683 let additional_fields = &[
685 0_u8, 0_u8, 0_u8, ];
689 let mut padded_bytes = [bytes, additional_fields].concat();
690 for _ in 0..additional_fields.len() {
691 if let Ok(order_packet) = OrderPacket::try_from_slice(&padded_bytes) {
692 return Some(order_packet);
693 }
694 padded_bytes.pop();
695 }
696 None
697 }
698 }
699}
700
701#[test]
702fn test_decode_order_packet() {
703 use rand::Rng;
704 use rand::{rngs::StdRng, SeedableRng};
705 let mut rng = StdRng::seed_from_u64(42);
706
707 let num_iters = 100;
708
709 #[derive(Deserialize, Serialize, Copy, Clone, PartialEq, Eq, Debug)]
710 pub enum DeprecatedOrderPacket {
711 PostOnly {
712 side: Side,
713 price_in_ticks: Ticks,
714 num_base_lots: BaseLots,
715 client_order_id: u128,
716 reject_post_only: bool,
717 use_only_deposited_funds: bool,
718 },
719 Limit {
720 side: Side,
721 price_in_ticks: Ticks,
722 num_base_lots: BaseLots,
723 self_trade_behavior: SelfTradeBehavior,
724 match_limit: Option<u64>,
725 client_order_id: u128,
726 use_only_deposited_funds: bool,
727 },
728
729 ImmediateOrCancel {
730 side: Side,
731 price_in_ticks: Option<Ticks>,
732 num_base_lots: BaseLots,
733 num_quote_lots: QuoteLots,
734 min_base_lots_to_fill: BaseLots,
735 min_quote_lots_to_fill: QuoteLots,
736 self_trade_behavior: SelfTradeBehavior,
737 match_limit: Option<u64>,
738 client_order_id: u128,
739 use_only_deposited_funds: bool,
740 },
741 }
742 for _ in 0..num_iters {
743 let side = if rng.gen::<f64>() > 0.5 {
744 Side::Bid
745 } else {
746 Side::Ask
747 };
748
749 let price_in_ticks = Ticks::new(rng.gen::<u64>());
750 let num_base_lots = BaseLots::new(rng.gen::<u64>());
751 let client_order_id = rng.gen::<u128>();
752 let reject_post_only = rng.gen::<bool>();
753 let use_only_deposited_funds = rng.gen::<bool>();
754 let packet = OrderPacket::PostOnly {
755 side,
756 price_in_ticks,
757 num_base_lots,
758 client_order_id,
759 reject_post_only,
760 use_only_deposited_funds,
761 last_valid_slot: None,
762 last_valid_unix_timestamp_in_seconds: None,
763 fail_silently_on_insufficient_funds: false,
764 };
765 let deprecated_packet = DeprecatedOrderPacket::PostOnly {
766 side,
767 price_in_ticks,
768 num_base_lots,
769 client_order_id,
770 reject_post_only,
771 use_only_deposited_funds,
772 };
773 let bytes = packet.try_to_vec().unwrap();
774 let decoded_normal = decode_order_packet(&bytes).unwrap();
775 let decoded_inferred_1 = decode_order_packet(&bytes[..bytes.len() - 1]).unwrap();
776 let decoded_inferred_2 = decode_order_packet(&bytes[..bytes.len() - 3]).unwrap();
777 let deprecated_bytes = deprecated_packet.try_to_vec().unwrap();
778 let decoded_deprecated = decode_order_packet(&deprecated_bytes).unwrap();
779 assert_eq!(packet, decoded_normal);
780 assert_eq!(decoded_normal, decoded_inferred_1);
781 assert_eq!(decoded_inferred_1, decoded_deprecated);
782 assert_eq!(decoded_inferred_1, decoded_inferred_2);
783 }
784
785 for _ in 0..num_iters {
786 let side = if rng.gen::<f64>() > 0.5 {
787 Side::Bid
788 } else {
789 Side::Ask
790 };
791
792 let price_in_ticks = Ticks::new(rng.gen::<u64>());
793 let num_base_lots = BaseLots::new(rng.gen::<u64>());
794 let client_order_id = rng.gen::<u128>();
795 let self_trade_behavior = match rng.gen_range(0, 3) {
796 0 => SelfTradeBehavior::DecrementTake,
797 1 => SelfTradeBehavior::CancelProvide,
798 2 => SelfTradeBehavior::Abort,
799 _ => unreachable!(),
800 };
801 let match_limit = if rng.gen::<f64>() > 0.5 {
802 Some(rng.gen::<u64>())
803 } else {
804 None
805 };
806 let use_only_deposited_funds = rng.gen::<bool>();
807 let packet = OrderPacket::Limit {
808 side,
809 price_in_ticks,
810 num_base_lots,
811 client_order_id,
812 self_trade_behavior,
813 match_limit,
814 use_only_deposited_funds,
815 last_valid_slot: None,
816 last_valid_unix_timestamp_in_seconds: None,
817 fail_silently_on_insufficient_funds: false,
818 };
819 let deprecated_packet = DeprecatedOrderPacket::Limit {
820 side,
821 price_in_ticks,
822 num_base_lots,
823 client_order_id,
824 self_trade_behavior,
825 match_limit,
826 use_only_deposited_funds,
827 };
828 let bytes = packet.try_to_vec().unwrap();
829 let decoded_normal = decode_order_packet(&bytes).unwrap();
830 let decoded_inferred_1 = decode_order_packet(&bytes[..bytes.len() - 1]).unwrap();
831 let decoded_inferred_2 = decode_order_packet(&bytes[..bytes.len() - 3]).unwrap();
832 let deprecated_bytes = deprecated_packet.try_to_vec().unwrap();
833 let decoded_deprecated = decode_order_packet(&deprecated_bytes).unwrap();
834 assert_eq!(packet, decoded_normal);
835 assert_eq!(decoded_normal, decoded_inferred_1);
836 assert_eq!(decoded_inferred_1, decoded_deprecated);
837 assert_eq!(decoded_inferred_1, decoded_inferred_2);
838 }
839
840 for _ in 0..num_iters {
841 let side = if rng.gen::<f64>() > 0.5 {
842 Side::Bid
843 } else {
844 Side::Ask
845 };
846
847 let price_in_ticks = if rng.gen::<f64>() > 0.5 {
848 Some(Ticks::new(rng.gen::<u64>()))
849 } else {
850 None
851 };
852 let num_base_lots = BaseLots::new(rng.gen::<u64>());
853 let min_base_lots_to_fill = BaseLots::new(rng.gen::<u64>());
854 let num_quote_lots = QuoteLots::new(rng.gen::<u64>());
855 let min_quote_lots_to_fill = QuoteLots::new(rng.gen::<u64>());
856 let client_order_id = rng.gen::<u128>();
857 let self_trade_behavior = match rng.gen_range(0, 3) {
858 0 => SelfTradeBehavior::DecrementTake,
859 1 => SelfTradeBehavior::CancelProvide,
860 2 => SelfTradeBehavior::Abort,
861 _ => unreachable!(),
862 };
863 let match_limit = if rng.gen::<f64>() > 0.5 {
864 Some(rng.gen::<u64>())
865 } else {
866 None
867 };
868 let use_only_deposited_funds = rng.gen::<bool>();
869 let packet = OrderPacket::ImmediateOrCancel {
870 side,
871 price_in_ticks,
872 num_base_lots,
873 num_quote_lots,
874 min_base_lots_to_fill,
875 min_quote_lots_to_fill,
876 client_order_id,
877 self_trade_behavior,
878 match_limit,
879 use_only_deposited_funds,
880 last_valid_slot: None,
881 last_valid_unix_timestamp_in_seconds: None,
882 };
883 let deprecated_packet = DeprecatedOrderPacket::ImmediateOrCancel {
884 side,
885 price_in_ticks,
886 num_base_lots,
887 num_quote_lots,
888 min_base_lots_to_fill,
889 min_quote_lots_to_fill,
890 client_order_id,
891 self_trade_behavior,
892 match_limit,
893 use_only_deposited_funds,
894 };
895 let bytes = packet.try_to_vec().unwrap();
896 let decoded_normal = decode_order_packet(&bytes).unwrap();
897 let decoded_inferred_1 = decode_order_packet(&bytes[..bytes.len() - 2]).unwrap();
898 let decoded_inferred_2 = decode_order_packet(&bytes[..bytes.len() - 1]).unwrap();
899 let deprecated_bytes = deprecated_packet.try_to_vec().unwrap();
900 let decoded_deprecated = decode_order_packet(&deprecated_bytes).unwrap();
901 assert_eq!(packet, decoded_normal);
902 assert_eq!(decoded_normal, decoded_inferred_1);
903 assert_eq!(decoded_inferred_1, decoded_deprecated);
904 assert_eq!(decoded_inferred_1, decoded_inferred_2);
905 }
906}