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