1use indexmap::IndexMap;
17use nautilus_core::{UnixNanos, UUID4};
18use rust_decimal::Decimal;
19use ustr::Ustr;
20
21use crate::{
22 enums::{
23 ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType,
24 TriggerType,
25 },
26 identifiers::{
27 ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, StrategyId, TradeId, TraderId,
28 },
29 orders::{
30 any::OrderAny, limit::LimitOrder, limit_if_touched::LimitIfTouchedOrder,
31 market::MarketOrder, market_if_touched::MarketIfTouchedOrder,
32 market_to_limit::MarketToLimitOrder, stop_limit::StopLimitOrder,
33 stop_market::StopMarketOrder, trailing_stop_limit::TrailingStopLimitOrder,
34 trailing_stop_market::TrailingStopMarketOrder,
35 },
36 types::{Currency, Price, Quantity},
37};
38
39pub struct OrderTestBuilder {
40 kind: OrderType,
41 trader_id: Option<TraderId>,
42 strategy_id: Option<StrategyId>,
43 instrument_id: Option<InstrumentId>,
44 client_order_id: Option<ClientOrderId>,
45 trade_id: Option<TradeId>,
46 currency: Option<Currency>,
47 side: Option<OrderSide>,
48 quantity: Option<Quantity>,
49 price: Option<Price>,
50 trigger_price: Option<Price>,
51 trigger_type: Option<TriggerType>,
52 limit_offset: Option<Decimal>,
53 trailing_offset: Option<Decimal>,
54 trailing_offset_type: Option<TrailingOffsetType>,
55 time_in_force: Option<TimeInForce>,
56 expire_time: Option<UnixNanos>,
57 reduce_only: Option<bool>,
58 post_only: Option<bool>,
59 quote_quantity: Option<bool>,
60 reconciliation: Option<bool>,
61 display_qty: Option<Quantity>,
62 liquidity_side: Option<LiquiditySide>,
63 emulation_trigger: Option<TriggerType>,
64 trigger_instrument_id: Option<InstrumentId>,
65 order_list_id: Option<OrderListId>,
66 linked_order_ids: Option<Vec<ClientOrderId>>,
67 parent_order_id: Option<ClientOrderId>,
68 exec_algorithm_id: Option<ExecAlgorithmId>,
69 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
70 exec_spawn_id: Option<ClientOrderId>,
71 tags: Option<Vec<Ustr>>,
72 init_id: Option<UUID4>,
73 ts_init: Option<UnixNanos>,
74 contingency_type: Option<ContingencyType>,
75}
76
77impl OrderTestBuilder {
78 pub fn new(kind: OrderType) -> Self {
80 OrderTestBuilder {
81 kind,
82 trader_id: None,
83 strategy_id: None,
84 instrument_id: None,
85 client_order_id: None,
86 trade_id: None,
87 currency: None,
88 side: None,
89 quantity: None,
90 price: None,
91 trigger_price: None,
92 trigger_type: None,
93 limit_offset: None,
94 trailing_offset: None,
95 trailing_offset_type: None,
96 time_in_force: None,
97 contingency_type: None,
98 expire_time: None,
99 reduce_only: None,
100 post_only: None,
101 quote_quantity: None,
102 reconciliation: None,
103 display_qty: None,
104 liquidity_side: None,
105 emulation_trigger: None,
106 trigger_instrument_id: None,
107 linked_order_ids: None,
108 order_list_id: None,
109 parent_order_id: None,
110 exec_algorithm_id: None,
111 exec_algorithm_params: None,
112 exec_spawn_id: None,
113 init_id: None,
114 ts_init: None,
115 tags: None,
116 }
117 }
118
119 pub fn kind(&mut self, kind: OrderType) -> &mut Self {
120 self.kind = kind;
121 self
122 }
123
124 pub fn trader_id(&mut self, trader_id: TraderId) -> &mut Self {
126 self.trader_id = Some(trader_id);
127 self
128 }
129
130 fn get_trader_id(&self) -> TraderId {
131 self.trader_id.unwrap_or_default()
132 }
133
134 pub fn strategy_id(&mut self, strategy_id: StrategyId) -> &mut Self {
136 self.strategy_id = Some(strategy_id);
137 self
138 }
139
140 fn get_strategy_id(&self) -> StrategyId {
141 self.strategy_id.unwrap_or_default()
142 }
143
144 pub fn instrument_id(&mut self, instrument_id: InstrumentId) -> &mut Self {
146 self.instrument_id = Some(instrument_id);
147 self
148 }
149
150 fn get_instrument_id(&self) -> InstrumentId {
151 self.instrument_id.expect("Instrument ID not set")
152 }
153
154 pub fn client_order_id(&mut self, client_order_id: ClientOrderId) -> &mut Self {
156 self.client_order_id = Some(client_order_id);
157 self
158 }
159
160 fn get_client_order_id(&self) -> ClientOrderId {
161 self.client_order_id.unwrap_or_default()
162 }
163
164 pub fn trade_id(&mut self, trade_id: TradeId) -> &mut Self {
166 self.trade_id = Some(trade_id);
167 self
168 }
169
170 fn get_trade_id(&self) -> TradeId {
171 self.trade_id.unwrap_or_default()
172 }
173
174 pub fn currency(&mut self, currency: Currency) -> &mut Self {
176 self.currency = Some(currency);
177 self
178 }
179
180 fn get_currency(&self) -> Currency {
181 self.currency.unwrap_or(Currency::from("USDT"))
182 }
183
184 pub fn side(&mut self, side: OrderSide) -> &mut Self {
186 self.side = Some(side);
187 self
188 }
189
190 fn get_side(&self) -> OrderSide {
191 self.side.unwrap_or(OrderSide::Buy)
192 }
193
194 pub fn quantity(&mut self, quantity: Quantity) -> &mut Self {
196 self.quantity = Some(quantity);
197 self
198 }
199
200 fn get_quantity(&self) -> Quantity {
201 self.quantity.expect("Order quantity not set")
202 }
203
204 pub fn price(&mut self, price: Price) -> &mut Self {
206 self.price = Some(price);
207 self
208 }
209
210 fn get_price(&self) -> Price {
211 self.price.expect("Price not set")
212 }
213
214 pub fn trigger_price(&mut self, trigger_price: Price) -> &mut Self {
216 self.trigger_price = Some(trigger_price);
217 self
218 }
219
220 fn get_trigger_price(&self) -> Price {
221 self.trigger_price.expect("Trigger price not set")
222 }
223
224 pub fn trigger_type(&mut self, trigger_type: TriggerType) -> &mut Self {
226 self.trigger_type = Some(trigger_type);
227 self
228 }
229
230 fn get_trigger_type(&self) -> TriggerType {
231 self.trigger_type.unwrap_or(TriggerType::Default)
232 }
233
234 pub fn limit_offset(&mut self, limit_offset: Decimal) -> &mut Self {
236 self.limit_offset = Some(limit_offset);
237 self
238 }
239
240 fn get_limit_offset(&self) -> Decimal {
241 self.limit_offset.expect("Limit offset not set")
242 }
243
244 pub fn trailing_offset(&mut self, trailing_offset: Decimal) -> &mut Self {
246 self.trailing_offset = Some(trailing_offset);
247 self
248 }
249
250 fn get_trailing_offset(&self) -> Decimal {
251 self.trailing_offset.expect("Trailing offset not set")
252 }
253
254 pub fn trailing_offset_type(&mut self, trailing_offset_type: TrailingOffsetType) -> &mut Self {
256 self.trailing_offset_type = Some(trailing_offset_type);
257 self
258 }
259
260 fn get_trailing_offset_type(&self) -> TrailingOffsetType {
261 self.trailing_offset_type
262 .unwrap_or(TrailingOffsetType::NoTrailingOffset)
263 }
264
265 pub fn time_in_force(&mut self, time_in_force: TimeInForce) -> &mut Self {
267 self.time_in_force = Some(time_in_force);
268 self
269 }
270
271 fn get_time_in_force(&self) -> TimeInForce {
272 self.time_in_force.unwrap_or(TimeInForce::Gtc)
273 }
274
275 pub fn expire_time(&mut self, expire_time: UnixNanos) -> &mut Self {
277 self.expire_time = Some(expire_time);
278 self
279 }
280
281 fn get_expire_time(&self) -> Option<UnixNanos> {
282 self.expire_time
283 }
284
285 pub fn display_qty(&mut self, display_qty: Quantity) -> &mut Self {
287 self.display_qty = Some(display_qty);
288 self
289 }
290
291 fn get_display_qty(&self) -> Option<Quantity> {
292 self.display_qty
293 }
294
295 pub fn liquidity_side(&mut self, liquidity_side: LiquiditySide) -> &mut Self {
297 self.liquidity_side = Some(liquidity_side);
298 self
299 }
300
301 fn get_liquidity_side(&self) -> LiquiditySide {
302 self.liquidity_side.unwrap_or(LiquiditySide::Maker)
303 }
304
305 pub fn emulation_trigger(&mut self, emulation_trigger: TriggerType) -> &mut Self {
307 self.emulation_trigger = Some(emulation_trigger);
308 self
309 }
310
311 fn get_emulation_trigger(&self) -> Option<TriggerType> {
312 self.emulation_trigger
313 }
314
315 pub fn trigger_instrument_id(&mut self, trigger_instrument_id: InstrumentId) -> &mut Self {
317 self.trigger_instrument_id = Some(trigger_instrument_id);
318 self
319 }
320
321 fn get_trigger_instrument_id(&self) -> Option<InstrumentId> {
322 self.trigger_instrument_id
323 }
324
325 pub fn order_list_id(&mut self, order_list_id: OrderListId) -> &mut Self {
327 self.order_list_id = Some(order_list_id);
328 self
329 }
330
331 fn get_order_list_id(&self) -> Option<OrderListId> {
332 self.order_list_id
333 }
334
335 pub fn linked_order_ids(&mut self, linked_order_ids: Vec<ClientOrderId>) -> &mut Self {
337 self.linked_order_ids = Some(linked_order_ids);
338 self
339 }
340
341 fn get_linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
342 self.linked_order_ids.clone()
343 }
344
345 pub fn parent_order_id(&mut self, parent_order_id: ClientOrderId) -> &mut Self {
347 self.parent_order_id = Some(parent_order_id);
348 self
349 }
350
351 fn get_parent_order_id(&self) -> Option<ClientOrderId> {
352 self.parent_order_id
353 }
354
355 pub fn exec_algorithm_id(&mut self, exec_algorithm_id: ExecAlgorithmId) -> &mut Self {
357 self.exec_algorithm_id = Some(exec_algorithm_id);
358 self
359 }
360
361 fn get_exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
362 self.exec_algorithm_id
363 }
364
365 pub fn exec_algorithm_params(
367 &mut self,
368 exec_algorithm_params: IndexMap<Ustr, Ustr>,
369 ) -> &mut Self {
370 self.exec_algorithm_params = Some(exec_algorithm_params);
371 self
372 }
373
374 fn get_exec_algorithm_params(&self) -> Option<IndexMap<Ustr, Ustr>> {
375 self.exec_algorithm_params.clone()
376 }
377
378 pub fn exec_spawn_id(&mut self, exec_spawn_id: ClientOrderId) -> &mut Self {
380 self.exec_spawn_id = Some(exec_spawn_id);
381 self
382 }
383
384 fn get_exec_spawn_id(&self) -> Option<ClientOrderId> {
385 self.exec_spawn_id
386 }
387
388 pub fn tags(&mut self, tags: Vec<Ustr>) -> &mut Self {
390 self.tags = Some(tags);
391 self
392 }
393
394 fn get_tags(&self) -> Option<Vec<Ustr>> {
395 self.tags.clone()
396 }
397
398 pub fn init_id(&mut self, init_id: UUID4) -> &mut Self {
400 self.init_id = Some(init_id);
401 self
402 }
403
404 fn get_init_id(&self) -> UUID4 {
405 self.init_id.unwrap_or_default()
406 }
407
408 pub fn ts_init(&mut self, ts_init: UnixNanos) -> &mut Self {
410 self.ts_init = Some(ts_init);
411 self
412 }
413
414 fn get_ts_init(&self) -> UnixNanos {
415 self.ts_init.unwrap_or_default()
416 }
417
418 pub fn reduce_only(&mut self, reduce_only: bool) -> &mut Self {
420 self.reduce_only = Some(reduce_only);
421 self
422 }
423
424 fn get_reduce_only(&self) -> bool {
425 self.reduce_only.unwrap_or(false)
426 }
427
428 pub fn post_only(&mut self, post_only: bool) -> &mut Self {
430 self.post_only = Some(post_only);
431 self
432 }
433
434 fn get_post_only(&self) -> bool {
435 self.post_only.unwrap_or(false)
436 }
437
438 pub fn quote_quantity(&mut self, quote_quantity: bool) -> &mut Self {
440 self.quote_quantity = Some(quote_quantity);
441 self
442 }
443
444 fn get_quote_quantity(&self) -> bool {
445 self.quote_quantity.unwrap_or(false)
446 }
447
448 pub fn reconciliation(&mut self, reconciliation: bool) -> &mut Self {
450 self.reconciliation = Some(reconciliation);
451 self
452 }
453
454 fn get_reconciliation(&self) -> bool {
455 self.reconciliation.unwrap_or(false)
456 }
457
458 pub fn contingency_type(&mut self, contingency_type: ContingencyType) -> &mut Self {
460 self.contingency_type = Some(contingency_type);
461 self
462 }
463
464 fn get_contingency_type(&self) -> Option<ContingencyType> {
465 Some(
466 self.contingency_type
467 .unwrap_or(ContingencyType::NoContingency),
468 )
469 }
470
471 pub fn build(&self) -> OrderAny {
472 match self.kind {
473 OrderType::Market => OrderAny::Market(MarketOrder::new(
474 self.get_trader_id(),
475 self.get_strategy_id(),
476 self.get_instrument_id(),
477 self.get_client_order_id(),
478 self.get_side(),
479 self.get_quantity(),
480 self.get_time_in_force(),
481 self.get_init_id(),
482 self.get_ts_init(),
483 self.get_reduce_only(),
484 self.get_quote_quantity(),
485 self.get_contingency_type(),
486 self.get_order_list_id(),
487 self.get_linked_order_ids(),
488 self.get_parent_order_id(),
489 self.get_exec_algorithm_id(),
490 self.get_exec_algorithm_params(),
491 self.get_exec_spawn_id(),
492 self.get_tags(),
493 )),
494 OrderType::Limit => OrderAny::Limit(
495 LimitOrder::new(
496 self.get_trader_id(),
497 self.get_strategy_id(),
498 self.get_instrument_id(),
499 self.get_client_order_id(),
500 self.get_side(),
501 self.get_quantity(),
502 self.get_price(),
503 self.get_time_in_force(),
504 self.get_expire_time(),
505 self.get_post_only(),
506 self.get_reduce_only(),
507 self.get_quote_quantity(),
508 self.get_display_qty(),
509 self.get_emulation_trigger(),
510 self.get_trigger_instrument_id(),
511 self.get_contingency_type(),
512 self.get_order_list_id(),
513 self.get_linked_order_ids(),
514 self.get_parent_order_id(),
515 self.get_exec_algorithm_id(),
516 self.get_exec_algorithm_params(),
517 self.get_exec_spawn_id(),
518 self.get_tags(),
519 self.get_init_id(),
520 self.get_ts_init(),
521 )
522 .unwrap(),
523 ),
524 OrderType::StopMarket => OrderAny::StopMarket(StopMarketOrder::new(
525 self.get_trader_id(),
526 self.get_strategy_id(),
527 self.get_instrument_id(),
528 self.get_client_order_id(),
529 self.get_side(),
530 self.get_quantity(),
531 self.get_trigger_price(),
532 self.get_trigger_type(),
533 self.get_time_in_force(),
534 self.get_expire_time(),
535 self.get_reduce_only(),
536 self.get_quote_quantity(),
537 self.get_display_qty(),
538 self.get_emulation_trigger(),
539 self.get_trigger_instrument_id(),
540 self.get_contingency_type(),
541 self.get_order_list_id(),
542 self.get_linked_order_ids(),
543 self.get_parent_order_id(),
544 self.get_exec_algorithm_id(),
545 self.get_exec_algorithm_params(),
546 self.get_exec_spawn_id(),
547 self.get_tags(),
548 self.get_init_id(),
549 self.get_ts_init(),
550 )),
551 OrderType::StopLimit => OrderAny::StopLimit(StopLimitOrder::new(
552 self.get_trader_id(),
553 self.get_strategy_id(),
554 self.get_instrument_id(),
555 self.get_client_order_id(),
556 self.get_side(),
557 self.get_quantity(),
558 self.get_price(),
559 self.get_trigger_price(),
560 self.get_trigger_type(),
561 self.get_time_in_force(),
562 self.get_expire_time(),
563 self.get_post_only(),
564 self.get_reduce_only(),
565 self.get_quote_quantity(),
566 self.get_display_qty(),
567 self.get_emulation_trigger(),
568 self.get_trigger_instrument_id(),
569 self.get_contingency_type(),
570 self.get_order_list_id(),
571 self.get_linked_order_ids(),
572 self.get_parent_order_id(),
573 self.get_exec_algorithm_id(),
574 self.get_exec_algorithm_params(),
575 self.get_exec_spawn_id(),
576 self.get_tags(),
577 self.get_init_id(),
578 self.get_ts_init(),
579 )),
580 OrderType::MarketToLimit => OrderAny::MarketToLimit(MarketToLimitOrder::new(
581 self.get_trader_id(),
582 self.get_strategy_id(),
583 self.get_instrument_id(),
584 self.get_client_order_id(),
585 self.get_side(),
586 self.get_quantity(),
587 self.get_time_in_force(),
588 self.get_expire_time(),
589 self.get_post_only(),
590 self.get_reduce_only(),
591 self.get_quote_quantity(),
592 self.get_display_qty(),
593 self.get_contingency_type(),
594 self.get_order_list_id(),
595 self.get_linked_order_ids(),
596 self.get_parent_order_id(),
597 self.get_exec_algorithm_id(),
598 self.get_exec_algorithm_params(),
599 self.get_exec_spawn_id(),
600 self.get_tags(),
601 self.get_init_id(),
602 self.get_ts_init(),
603 )),
604 OrderType::MarketIfTouched => OrderAny::MarketIfTouched(MarketIfTouchedOrder::new(
605 self.get_trader_id(),
606 self.get_strategy_id(),
607 self.get_instrument_id(),
608 self.get_client_order_id(),
609 self.get_side(),
610 self.get_quantity(),
611 self.get_trigger_price(),
612 self.get_trigger_type(),
613 self.get_time_in_force(),
614 self.get_expire_time(),
615 self.get_reduce_only(),
616 self.get_quote_quantity(),
617 self.get_display_qty(),
618 self.get_emulation_trigger(),
619 self.get_trigger_instrument_id(),
620 self.get_contingency_type(),
621 self.get_order_list_id(),
622 self.get_linked_order_ids(),
623 self.get_parent_order_id(),
624 self.get_exec_algorithm_id(),
625 self.get_exec_algorithm_params(),
626 self.get_exec_spawn_id(),
627 self.get_tags(),
628 self.get_init_id(),
629 self.get_ts_init(),
630 )),
631 OrderType::LimitIfTouched => OrderAny::LimitIfTouched(LimitIfTouchedOrder::new(
632 self.get_trader_id(),
633 self.get_strategy_id(),
634 self.get_instrument_id(),
635 self.get_client_order_id(),
636 self.get_side(),
637 self.get_quantity(),
638 self.get_price(),
639 self.get_trigger_price(),
640 self.get_trigger_type(),
641 self.get_time_in_force(),
642 self.get_expire_time(),
643 self.get_post_only(),
644 self.get_reduce_only(),
645 self.get_quote_quantity(),
646 self.get_display_qty(),
647 self.get_emulation_trigger(),
648 self.get_trigger_instrument_id(),
649 self.get_contingency_type(),
650 self.get_order_list_id(),
651 self.get_linked_order_ids(),
652 self.get_parent_order_id(),
653 self.get_exec_algorithm_id(),
654 self.get_exec_algorithm_params(),
655 self.get_exec_spawn_id(),
656 self.get_tags(),
657 self.get_init_id(),
658 self.get_ts_init(),
659 )),
660 OrderType::TrailingStopMarket => {
661 OrderAny::TrailingStopMarket(TrailingStopMarketOrder::new(
662 self.get_trader_id(),
663 self.get_strategy_id(),
664 self.get_instrument_id(),
665 self.get_client_order_id(),
666 self.get_side(),
667 self.get_quantity(),
668 self.get_trigger_price(),
669 self.get_trigger_type(),
670 self.get_trailing_offset(),
671 self.get_trailing_offset_type(),
672 self.get_time_in_force(),
673 self.get_expire_time(),
674 self.get_reduce_only(),
675 self.get_quote_quantity(),
676 self.get_display_qty(),
677 self.get_emulation_trigger(),
678 self.get_trigger_instrument_id(),
679 self.get_contingency_type(),
680 self.get_order_list_id(),
681 self.get_linked_order_ids(),
682 self.get_parent_order_id(),
683 self.get_exec_algorithm_id(),
684 self.get_exec_algorithm_params(),
685 self.get_exec_spawn_id(),
686 self.get_tags(),
687 self.get_init_id(),
688 self.get_ts_init(),
689 ))
690 }
691 OrderType::TrailingStopLimit => {
692 OrderAny::TrailingStopLimit(TrailingStopLimitOrder::new(
693 self.get_trader_id(),
694 self.get_strategy_id(),
695 self.get_instrument_id(),
696 self.get_client_order_id(),
697 self.get_side(),
698 self.get_quantity(),
699 self.get_price(),
700 self.get_trigger_price(),
701 self.get_trigger_type(),
702 self.get_limit_offset(),
703 self.get_trailing_offset(),
704 self.get_trailing_offset_type(),
705 self.get_time_in_force(),
706 self.get_expire_time(),
707 self.get_post_only(),
708 self.get_reduce_only(),
709 self.get_quote_quantity(),
710 self.get_display_qty(),
711 self.get_emulation_trigger(),
712 self.get_trigger_instrument_id(),
713 self.get_contingency_type(),
714 self.get_order_list_id(),
715 self.get_linked_order_ids(),
716 self.get_parent_order_id(),
717 self.get_exec_algorithm_id(),
718 self.get_exec_algorithm_params(),
719 self.get_exec_spawn_id(),
720 self.get_tags(),
721 self.get_init_id(),
722 self.get_ts_init(),
723 ))
724 }
725 }
726 }
727}