1#[cfg(test)]
27use chrono::Duration;
28use chrono::{DateTime, Utc};
29use nautilus_model::enums::OrderType;
30use rust_decimal::{Decimal, prelude::ToPrimitive};
31
32use crate::proto::dydxprotocol::{
33 clob::{
34 Order, OrderId,
35 order::{ConditionType, GoodTilOneof, Side as OrderSide, TimeInForce as OrderTimeInForce},
36 },
37 subaccounts::SubaccountId,
38};
39
40pub const SHORT_TERM_ORDER_MAXIMUM_LIFETIME: u32 = 20;
44
45pub const DEFAULT_MARKET_ORDER_SLIPPAGE: Decimal = Decimal::from_parts(1, 0, 0, false, 2);
48
49pub const DEFAULT_RUST_CLIENT_METADATA: u32 = 4;
51
52#[derive(Clone, Debug)]
54pub enum OrderGoodUntil {
55 Block(u32),
58 Time(DateTime<Utc>),
61}
62
63#[derive(Clone, Debug)]
68pub enum OrderFlags {
69 ShortTerm,
71 LongTerm,
73 Conditional,
78}
79
80#[derive(Clone, Debug)]
85pub struct OrderMarketParams {
86 pub atomic_resolution: i32,
88 pub clob_pair_id: u32,
90 pub oracle_price: Option<Decimal>,
92 pub quantum_conversion_exponent: i32,
94 pub step_base_quantums: u64,
96 pub subticks_per_tick: u32,
98}
99
100impl OrderMarketParams {
101 pub fn quantize_price(&self, price: Decimal) -> Result<u64, anyhow::Error> {
107 const QUOTE_QUANTUMS_ATOMIC_RESOLUTION: i32 = -6;
108 let exponent = -(self.atomic_resolution
109 - self.quantum_conversion_exponent
110 - QUOTE_QUANTUMS_ATOMIC_RESOLUTION);
111
112 let factor = if exponent < 0 {
115 Decimal::from(10_i64.pow(exponent.unsigned_abs()))
116 } else {
117 Decimal::new(1, exponent.unsigned_abs())
118 };
119
120 let raw_subticks = price * factor;
121 let subticks_per_tick = Decimal::from(self.subticks_per_tick);
122 let quantums = Self::quantize(&raw_subticks, &subticks_per_tick);
123 let result = quantums.max(subticks_per_tick);
124
125 result
126 .to_u64()
127 .ok_or_else(|| anyhow::anyhow!("Failed to convert price to u64"))
128 }
129
130 pub fn quantize_quantity(&self, quantity: Decimal) -> Result<u64, anyhow::Error> {
136 let factor = if self.atomic_resolution < 0 {
139 Decimal::from(10_i64.pow(self.atomic_resolution.unsigned_abs()))
140 } else {
141 Decimal::new(1, self.atomic_resolution.unsigned_abs())
142 };
143
144 let raw_quantums = quantity * factor;
145 let step_base_quantums = Decimal::from(self.step_base_quantums);
146 let quantums = Self::quantize(&raw_quantums, &step_base_quantums);
147 let result = quantums.max(step_base_quantums);
148
149 result
150 .to_u64()
151 .ok_or_else(|| anyhow::anyhow!("Failed to convert quantity to u64"))
152 }
153
154 fn quantize(value: &Decimal, fraction: &Decimal) -> Decimal {
156 (value / fraction).round() * fraction
157 }
158
159 pub fn market_order_subticks(&self, side: OrderSide) -> Result<u64, anyhow::Error> {
165 let oracle = self
166 .oracle_price
167 .ok_or_else(|| anyhow::anyhow!("Oracle price required for market orders"))?;
168 let worst_price = match side {
169 OrderSide::Buy => oracle * (Decimal::ONE + DEFAULT_MARKET_ORDER_SLIPPAGE),
170 OrderSide::Sell => oracle * (Decimal::ONE - DEFAULT_MARKET_ORDER_SLIPPAGE),
171 _ => oracle,
172 };
173 self.quantize_price(worst_price)
174 }
175
176 #[must_use]
178 pub fn clob_pair_id(&self) -> u32 {
179 self.clob_pair_id
180 }
181}
182
183#[derive(Clone, Debug)]
194pub struct OrderBuilder {
195 market_params: OrderMarketParams,
196 subaccount_owner: String,
197 subaccount_number: u32,
198 client_id: u32,
199 client_metadata: u32,
202 flags: OrderFlags,
203 side: Option<OrderSide>,
204 order_type: Option<OrderType>,
205 size: Option<Decimal>,
206 price: Option<Decimal>,
207 time_in_force: Option<OrderTimeInForce>,
208 reduce_only: Option<bool>,
209 until: Option<OrderGoodUntil>,
210 trigger_price: Option<Decimal>,
211 condition_type: Option<ConditionType>,
212}
213
214impl OrderBuilder {
215 #[must_use]
225 pub fn new(
226 market_params: OrderMarketParams,
227 subaccount_owner: String,
228 subaccount_number: u32,
229 client_id: u32,
230 client_metadata: u32,
231 ) -> Self {
232 Self {
233 market_params,
234 subaccount_owner,
235 subaccount_number,
236 client_id,
237 client_metadata,
238 flags: OrderFlags::ShortTerm,
239 side: Some(OrderSide::Buy),
240 order_type: Some(OrderType::Market),
241 size: None,
242 price: None,
243 time_in_force: None,
244 reduce_only: None,
245 until: None,
246 trigger_price: None,
247 condition_type: None,
248 }
249 }
250
251 #[must_use]
256 pub fn market(mut self, side: OrderSide, size: Decimal) -> Self {
257 self.order_type = Some(OrderType::Market);
258 self.side = Some(side);
259 self.size = Some(size);
260 self.time_in_force = Some(OrderTimeInForce::Ioc);
261 self
262 }
263
264 #[must_use]
269 pub fn limit(mut self, side: OrderSide, price: Decimal, size: Decimal) -> Self {
270 self.order_type = Some(OrderType::Limit);
271 self.price = Some(price);
272 self.side = Some(side);
273 self.size = Some(size);
274 self
275 }
276
277 #[must_use]
281 pub fn stop_limit(
282 mut self,
283 side: OrderSide,
284 price: Decimal,
285 trigger_price: Decimal,
286 size: Decimal,
287 ) -> Self {
288 self.order_type = Some(OrderType::StopLimit);
289 self.price = Some(price);
290 self.trigger_price = Some(trigger_price);
291 self.side = Some(side);
292 self.size = Some(size);
293 self.condition_type = Some(ConditionType::StopLoss);
294 self.conditional()
295 }
296
297 #[must_use]
301 pub fn stop_market(mut self, side: OrderSide, trigger_price: Decimal, size: Decimal) -> Self {
302 self.order_type = Some(OrderType::StopMarket);
303 self.trigger_price = Some(trigger_price);
304 self.side = Some(side);
305 self.size = Some(size);
306 self.condition_type = Some(ConditionType::StopLoss);
307 self.conditional()
308 }
309
310 #[must_use]
314 pub fn take_profit_limit(
315 mut self,
316 side: OrderSide,
317 price: Decimal,
318 trigger_price: Decimal,
319 size: Decimal,
320 ) -> Self {
321 self.order_type = Some(OrderType::LimitIfTouched);
322 self.price = Some(price);
323 self.trigger_price = Some(trigger_price);
324 self.side = Some(side);
325 self.size = Some(size);
326 self.condition_type = Some(ConditionType::TakeProfit);
327 self.conditional()
328 }
329
330 #[must_use]
334 pub fn take_profit_market(
335 mut self,
336 side: OrderSide,
337 trigger_price: Decimal,
338 size: Decimal,
339 ) -> Self {
340 self.order_type = Some(OrderType::MarketIfTouched);
341 self.trigger_price = Some(trigger_price);
342 self.side = Some(side);
343 self.size = Some(size);
344 self.condition_type = Some(ConditionType::TakeProfit);
345 self.conditional()
346 }
347
348 #[must_use]
350 pub fn long_term(mut self) -> Self {
351 self.flags = OrderFlags::LongTerm;
352 self
353 }
354
355 #[must_use]
357 pub fn short_term(mut self) -> Self {
358 self.flags = OrderFlags::ShortTerm;
359 self
360 }
361
362 #[must_use]
364 pub fn conditional(mut self) -> Self {
365 self.flags = OrderFlags::Conditional;
366 self
367 }
368
369 #[must_use]
371 pub fn price(mut self, price: Decimal) -> Self {
372 self.price = Some(price);
373 self
374 }
375
376 #[must_use]
378 pub fn size(mut self, size: Decimal) -> Self {
379 self.size = Some(size);
380 self
381 }
382
383 #[must_use]
385 pub fn time_in_force(mut self, tif: OrderTimeInForce) -> Self {
386 self.time_in_force = Some(tif);
387 self
388 }
389
390 #[must_use]
392 pub fn reduce_only(mut self, reduce: bool) -> Self {
393 self.reduce_only = Some(reduce);
394 self
395 }
396
397 #[must_use]
399 pub fn until(mut self, gtof: OrderGoodUntil) -> Self {
400 self.until = Some(gtof);
401 self
402 }
403
404 pub fn build(self) -> Result<Order, anyhow::Error> {
410 let side = self
411 .side
412 .ok_or_else(|| anyhow::anyhow!("Order side not set"))?;
413 let size = self
414 .size
415 .ok_or_else(|| anyhow::anyhow!("Order size not set"))?;
416
417 let quantums = self.market_params.quantize_quantity(size)?;
419
420 let order_id = Some(OrderId {
422 subaccount_id: Some(SubaccountId {
423 owner: self.subaccount_owner.clone(),
424 number: self.subaccount_number,
425 }),
426 client_id: self.client_id,
427 order_flags: match self.flags {
428 OrderFlags::ShortTerm => 0,
429 OrderFlags::LongTerm => 64,
430 OrderFlags::Conditional => 32,
431 },
432 clob_pair_id: self.market_params.clob_pair_id,
433 });
434
435 let until = self
437 .until
438 .ok_or_else(|| anyhow::anyhow!("Order expiration (until) not set"))?;
439
440 let good_til_oneof = match until {
441 OrderGoodUntil::Block(height) => Some(GoodTilOneof::GoodTilBlock(height)),
442 OrderGoodUntil::Time(time) => {
443 Some(GoodTilOneof::GoodTilBlockTime(time.timestamp().try_into()?))
444 }
445 };
446
447 let subticks = if let Some(price) = self.price {
449 self.market_params.quantize_price(price)?
450 } else if matches!(
451 self.order_type,
452 Some(OrderType::Market | OrderType::StopMarket | OrderType::MarketIfTouched)
453 ) {
454 let side = self
455 .side
456 .ok_or_else(|| anyhow::anyhow!("Order side not set"))?;
457 self.market_params.market_order_subticks(side)?
458 } else {
459 0
460 };
461
462 Ok(Order {
463 order_id,
464 side: side as i32,
465 quantums,
466 subticks,
467 good_til_oneof,
468 time_in_force: self.time_in_force.map_or(0, |tif| tif as i32),
469 reduce_only: self.reduce_only.unwrap_or(false),
470 client_metadata: self.client_metadata,
471 condition_type: self.condition_type.map_or(0, |ct| ct as i32),
472 conditional_order_trigger_subticks: self
473 .trigger_price
474 .map(|tp| self.market_params.quantize_price(tp))
475 .transpose()?
476 .unwrap_or(0),
477 twap_parameters: None,
478 builder_code_parameters: None,
479 order_router_address: String::new(),
480 })
481 }
482}
483
484impl Default for OrderBuilder {
485 fn default() -> Self {
486 Self {
487 market_params: OrderMarketParams {
488 atomic_resolution: -10,
489 clob_pair_id: 0,
490 oracle_price: Some(Decimal::from(50_000)),
491 quantum_conversion_exponent: -9,
492 step_base_quantums: 1_000_000,
493 subticks_per_tick: 100_000,
494 },
495 subaccount_owner: String::new(),
496 subaccount_number: 0,
497 client_id: 0,
498 client_metadata: DEFAULT_RUST_CLIENT_METADATA,
499 flags: OrderFlags::ShortTerm,
500 side: Some(OrderSide::Buy),
501 order_type: Some(OrderType::Market),
502 size: None,
503 price: None,
504 time_in_force: None,
505 reduce_only: None,
506 until: None,
507 trigger_price: None,
508 condition_type: None,
509 }
510 }
511}
512
513#[cfg(test)]
514mod tests {
515 use rstest::rstest;
516 use rust_decimal_macros::dec;
517
518 use super::*;
519
520 fn sample_market_params() -> OrderMarketParams {
521 OrderMarketParams {
522 atomic_resolution: -10,
523 clob_pair_id: 0,
524 oracle_price: Some(dec!(50000)),
525 quantum_conversion_exponent: -9,
526 step_base_quantums: 1_000_000,
527 subticks_per_tick: 100_000,
528 }
529 }
530
531 #[rstest]
532 fn test_market_params_quantize_price() {
533 let market = sample_market_params();
534 let price = dec!(50000);
535 let subticks = market.quantize_price(price).unwrap();
536 assert_eq!(subticks, 5_000_000_000);
539 }
540
541 #[rstest]
542 fn test_market_params_quantize_quantity() {
543 let market = sample_market_params();
544 let quantity = dec!(0.01);
545 let quantums = market.quantize_quantity(quantity).unwrap();
546 assert_eq!(quantums, 100_000_000);
549 }
550
551 #[rstest]
552 fn test_quantize_price_rounding_up() {
553 let market = sample_market_params();
554 let price = dec!(50000.6);
556 let subticks = market.quantize_price(price).unwrap();
557 assert_eq!(subticks, 5_000_100_000);
558 }
559
560 #[rstest]
561 fn test_quantize_price_rounding_down() {
562 let market = sample_market_params();
563 let price = dec!(49999.4);
565 let subticks = market.quantize_price(price).unwrap();
566 assert_eq!(subticks, 4_999_900_000);
567 }
568
569 #[rstest]
570 fn test_quantize_quantity_rounding_up() {
571 let market = sample_market_params();
572 let quantity = dec!(0.0105); let quantums = market.quantize_quantity(quantity).unwrap();
575 assert_eq!(quantums, 105_000_000);
576 }
577
578 #[rstest]
579 fn test_quantize_quantity_rounding_down() {
580 let market = sample_market_params();
581 let quantity = dec!(0.0104); let quantums = market.quantize_quantity(quantity).unwrap();
584 assert_eq!(quantums, 104_000_000);
585 }
586
587 #[rstest]
588 fn test_quantize_price_minimum_tick() {
589 let market = sample_market_params();
590 let price = dec!(0.001);
592 let subticks = market.quantize_price(price).unwrap();
593 assert_eq!(subticks, market.subticks_per_tick as u64);
594 }
595
596 #[rstest]
597 fn test_quantize_quantity_minimum_quantum() {
598 let market = sample_market_params();
599 let quantity = dec!(0.00000001);
601 let quantums = market.quantize_quantity(quantity).unwrap();
602 assert_eq!(quantums, market.step_base_quantums);
603 }
604
605 #[rstest]
606 fn test_quantize_price_large_values() {
607 let market = sample_market_params();
608 let price = dec!(100000);
610 let subticks = market.quantize_price(price).unwrap();
611 assert_eq!(subticks, 10_000_000_000);
612 }
613
614 #[rstest]
615 fn test_quantize_quantity_large_values() {
616 let market = sample_market_params();
617 let quantity = dec!(10);
619 let quantums = market.quantize_quantity(quantity).unwrap();
620 assert_eq!(quantums, 100_000_000_000);
621 }
622
623 #[rstest]
624 fn test_order_builder_market_buy() {
625 let market = sample_market_params();
626 let builder = OrderBuilder::new(
627 market,
628 "dydx1test".to_string(),
629 0,
630 1,
631 DEFAULT_RUST_CLIENT_METADATA,
632 );
633
634 let order = builder
635 .market(OrderSide::Buy, dec!(0.01))
636 .until(OrderGoodUntil::Block(100))
637 .build()
638 .unwrap();
639
640 assert_eq!(order.side, OrderSide::Buy as i32);
641 assert_eq!(order.quantums, 100_000_000); assert_eq!(order.subticks, 5_050_000_000); assert_eq!(order.time_in_force, OrderTimeInForce::Ioc as i32);
644 assert!(!order.reduce_only);
645 assert_eq!(order.client_metadata, DEFAULT_RUST_CLIENT_METADATA);
646 }
647
648 #[rstest]
649 fn test_order_builder_market_sell() {
650 let market = sample_market_params();
651 let builder = OrderBuilder::new(
652 market,
653 "dydx1test".to_string(),
654 0,
655 2,
656 DEFAULT_RUST_CLIENT_METADATA,
657 );
658
659 let order = builder
660 .market(OrderSide::Sell, dec!(0.02))
661 .until(OrderGoodUntil::Block(100))
662 .build()
663 .unwrap();
664
665 assert_eq!(order.side, OrderSide::Sell as i32);
666 assert_eq!(order.quantums, 200_000_000); assert_eq!(order.subticks, 4_950_000_000); assert_eq!(order.time_in_force, OrderTimeInForce::Ioc as i32);
669 }
670
671 #[rstest]
672 fn test_order_builder_market_no_oracle_price_error() {
673 let mut market = sample_market_params();
674 market.oracle_price = None;
675
676 let builder = OrderBuilder::new(
677 market,
678 "dydx1test".to_string(),
679 0,
680 13,
681 DEFAULT_RUST_CLIENT_METADATA,
682 );
683
684 let result = builder
685 .market(OrderSide::Buy, dec!(0.01))
686 .until(OrderGoodUntil::Block(100))
687 .build();
688
689 assert!(result.is_err());
690 assert!(
691 result
692 .unwrap_err()
693 .to_string()
694 .contains("Oracle price required")
695 );
696 }
697
698 #[rstest]
699 fn test_order_builder_limit_buy() {
700 let market = sample_market_params();
701 let builder = OrderBuilder::new(
702 market,
703 "dydx1test".to_string(),
704 0,
705 3,
706 DEFAULT_RUST_CLIENT_METADATA,
707 );
708
709 let order = builder
710 .limit(OrderSide::Buy, dec!(49000), dec!(0.01))
711 .until(OrderGoodUntil::Block(100))
712 .build()
713 .unwrap();
714
715 assert_eq!(order.side, OrderSide::Buy as i32);
716 assert_eq!(order.quantums, 100_000_000); assert_eq!(order.subticks, 4_900_000_000); assert!(!order.reduce_only);
719 }
720
721 #[rstest]
722 fn test_order_builder_limit_sell() {
723 let market = sample_market_params();
724 let builder = OrderBuilder::new(
725 market,
726 "dydx1test".to_string(),
727 0,
728 4,
729 DEFAULT_RUST_CLIENT_METADATA,
730 );
731
732 let order = builder
733 .limit(OrderSide::Sell, dec!(51000), dec!(0.015))
734 .until(OrderGoodUntil::Block(100))
735 .build()
736 .unwrap();
737
738 assert_eq!(order.side, OrderSide::Sell as i32);
739 assert_eq!(order.quantums, 150_000_000); assert_eq!(order.subticks, 5_100_000_000); }
742
743 #[rstest]
744 fn test_order_builder_limit_with_reduce_only() {
745 let market = sample_market_params();
746 let builder = OrderBuilder::new(
747 market,
748 "dydx1test".to_string(),
749 0,
750 5,
751 DEFAULT_RUST_CLIENT_METADATA,
752 );
753
754 let order = builder
755 .limit(OrderSide::Sell, dec!(50000), dec!(0.01))
756 .reduce_only(true)
757 .until(OrderGoodUntil::Block(100))
758 .build()
759 .unwrap();
760
761 assert!(order.reduce_only);
762 }
763
764 #[rstest]
765 fn test_order_builder_short_term_flag() {
766 let market = sample_market_params();
767 let builder = OrderBuilder::new(
768 market,
769 "dydx1test".to_string(),
770 0,
771 6,
772 DEFAULT_RUST_CLIENT_METADATA,
773 );
774
775 let order = builder
776 .short_term()
777 .market(OrderSide::Buy, dec!(0.01))
778 .until(OrderGoodUntil::Block(100))
779 .build()
780 .unwrap();
781
782 assert_eq!(order.order_id.as_ref().unwrap().order_flags, 0);
784 }
785
786 #[rstest]
787 fn test_order_builder_long_term_flag() {
788 let market = sample_market_params();
789 let builder = OrderBuilder::new(
790 market,
791 "dydx1test".to_string(),
792 0,
793 7,
794 DEFAULT_RUST_CLIENT_METADATA,
795 );
796
797 let now = Utc::now();
798 let until = now + Duration::hours(1);
799
800 let order = builder
801 .long_term()
802 .limit(OrderSide::Buy, dec!(50000), dec!(0.01))
803 .until(OrderGoodUntil::Time(until))
804 .build()
805 .unwrap();
806
807 assert_eq!(order.order_id.as_ref().unwrap().order_flags, 64);
809 }
810
811 #[rstest]
812 fn test_order_builder_conditional_flag() {
813 let market = sample_market_params();
814 let builder = OrderBuilder::new(
815 market,
816 "dydx1test".to_string(),
817 0,
818 8,
819 DEFAULT_RUST_CLIENT_METADATA,
820 );
821
822 let order = builder
823 .stop_limit(OrderSide::Sell, dec!(48000), dec!(49000), dec!(0.01))
824 .until(OrderGoodUntil::Block(100))
825 .build()
826 .unwrap();
827
828 assert_eq!(order.order_id.as_ref().unwrap().order_flags, 32);
830 assert_eq!(order.conditional_order_trigger_subticks, 4_900_000_000);
831 }
832
833 #[rstest]
834 fn test_stop_limit_sets_condition_type() {
835 let market = sample_market_params();
836 let builder = OrderBuilder::new(
837 market,
838 "dydx1test".to_string(),
839 0,
840 100,
841 DEFAULT_RUST_CLIENT_METADATA,
842 );
843
844 let order = builder
845 .stop_limit(OrderSide::Sell, dec!(48000), dec!(49000), dec!(0.01))
846 .until(OrderGoodUntil::Block(100))
847 .build()
848 .unwrap();
849
850 assert_eq!(order.condition_type, ConditionType::StopLoss as i32);
851 }
852
853 #[rstest]
854 fn test_stop_market_sets_condition_type() {
855 let market = sample_market_params();
856 let builder = OrderBuilder::new(
857 market,
858 "dydx1test".to_string(),
859 0,
860 101,
861 DEFAULT_RUST_CLIENT_METADATA,
862 );
863
864 let order = builder
865 .stop_market(OrderSide::Sell, dec!(49000), dec!(0.01))
866 .until(OrderGoodUntil::Block(100))
867 .build()
868 .unwrap();
869
870 assert_eq!(order.condition_type, ConditionType::StopLoss as i32);
871 }
872
873 #[rstest]
874 fn test_take_profit_limit_sets_condition_type() {
875 let market = sample_market_params();
876 let builder = OrderBuilder::new(
877 market,
878 "dydx1test".to_string(),
879 0,
880 102,
881 DEFAULT_RUST_CLIENT_METADATA,
882 );
883
884 let order = builder
885 .take_profit_limit(OrderSide::Sell, dec!(52000), dec!(51000), dec!(0.01))
886 .until(OrderGoodUntil::Block(100))
887 .build()
888 .unwrap();
889
890 assert_eq!(order.condition_type, ConditionType::TakeProfit as i32);
891 }
892
893 #[rstest]
894 fn test_take_profit_market_sets_condition_type() {
895 let market = sample_market_params();
896 let builder = OrderBuilder::new(
897 market,
898 "dydx1test".to_string(),
899 0,
900 103,
901 DEFAULT_RUST_CLIENT_METADATA,
902 );
903
904 let order = builder
905 .take_profit_market(OrderSide::Sell, dec!(51000), dec!(0.01))
906 .until(OrderGoodUntil::Block(100))
907 .build()
908 .unwrap();
909
910 assert_eq!(order.condition_type, ConditionType::TakeProfit as i32);
911 }
912
913 #[rstest]
914 fn test_order_builder_missing_size_error() {
915 let market = sample_market_params();
916 let builder = OrderBuilder::new(
917 market,
918 "dydx1test".to_string(),
919 0,
920 9,
921 DEFAULT_RUST_CLIENT_METADATA,
922 );
923
924 let result = builder.until(OrderGoodUntil::Block(100)).build();
925
926 assert!(result.is_err());
927 assert!(result.unwrap_err().to_string().contains("size"));
928 }
929
930 #[rstest]
931 fn test_order_builder_missing_until_error() {
932 let market = sample_market_params();
933 let builder = OrderBuilder::new(
934 market,
935 "dydx1test".to_string(),
936 0,
937 10,
938 DEFAULT_RUST_CLIENT_METADATA,
939 );
940
941 let result = builder.market(OrderSide::Buy, dec!(0.01)).build();
942
943 assert!(result.is_err());
944 }
945
946 #[rstest]
947 fn test_order_builder_time_in_force() {
948 let market = sample_market_params();
949 let builder = OrderBuilder::new(
950 market,
951 "dydx1test".to_string(),
952 0,
953 11,
954 DEFAULT_RUST_CLIENT_METADATA,
955 );
956
957 let order = builder
958 .limit(OrderSide::Buy, dec!(50000), dec!(0.01))
959 .time_in_force(OrderTimeInForce::Ioc)
960 .until(OrderGoodUntil::Block(100))
961 .build()
962 .unwrap();
963
964 assert_eq!(order.time_in_force, OrderTimeInForce::Ioc as i32);
965 }
966
967 #[rstest]
968 fn test_order_builder_clob_pair_id() {
969 let mut market = sample_market_params();
970 market.clob_pair_id = 5;
971
972 let builder = OrderBuilder::new(
973 market,
974 "dydx1test".to_string(),
975 0,
976 12,
977 DEFAULT_RUST_CLIENT_METADATA,
978 );
979
980 let order = builder
981 .market(OrderSide::Buy, dec!(0.01))
982 .until(OrderGoodUntil::Block(100))
983 .build()
984 .unwrap();
985
986 assert_eq!(order.order_id.as_ref().unwrap().clob_pair_id, 5);
987 }
988}