1use std::{
17 fmt::Display,
18 ops::{Deref, DerefMut},
19};
20
21use indexmap::IndexMap;
22use nautilus_core::{
23 UUID4, UnixNanos,
24 correctness::{FAILED, check_predicate_false},
25};
26use rust_decimal::Decimal;
27use serde::{Deserialize, Serialize};
28use ustr::Ustr;
29
30use super::{Order, OrderAny, OrderCore};
31use crate::{
32 enums::{
33 ContingencyType, LiquiditySide, OrderSide, OrderStatus, OrderType, PositionSide,
34 TimeInForce, TrailingOffsetType, TriggerType,
35 },
36 events::{OrderEventAny, OrderInitialized, OrderUpdated},
37 identifiers::{
38 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
39 StrategyId, Symbol, TradeId, TraderId, Venue, VenueOrderId,
40 },
41 orders::OrderError,
42 types::{Currency, Money, Price, Quantity, quantity::check_positive_quantity},
43};
44
45#[derive(Clone, Debug, Serialize, Deserialize)]
46#[cfg_attr(
47 feature = "python",
48 pyo3::pyclass(module = "nautilus_trader.model", from_py_object)
49)]
50#[cfg_attr(
51 feature = "python",
52 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
53)]
54pub struct MarketOrder {
55 core: OrderCore,
56 pub protection_price: Option<Price>,
57}
58
59impl MarketOrder {
60 #[expect(clippy::too_many_arguments)]
69 pub fn new_checked(
70 trader_id: TraderId,
71 strategy_id: StrategyId,
72 instrument_id: InstrumentId,
73 client_order_id: ClientOrderId,
74 order_side: OrderSide,
75 quantity: Quantity,
76 time_in_force: TimeInForce,
77 init_id: UUID4,
78 ts_init: UnixNanos,
79 reduce_only: bool,
80 quote_quantity: bool,
81 contingency_type: Option<ContingencyType>,
82 order_list_id: Option<OrderListId>,
83 linked_order_ids: Option<Vec<ClientOrderId>>,
84 parent_order_id: Option<ClientOrderId>,
85 exec_algorithm_id: Option<ExecAlgorithmId>,
86 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
87 exec_spawn_id: Option<ClientOrderId>,
88 tags: Option<Vec<Ustr>>,
89 ) -> Result<Self, OrderError> {
90 check_positive_quantity(quantity, stringify!(quantity))?;
91 check_predicate_false(
92 time_in_force == TimeInForce::Gtd,
93 "GTD not supported for Market orders",
94 )?;
95
96 let init_order = OrderInitialized::new_checked(
97 trader_id,
98 strategy_id,
99 instrument_id,
100 client_order_id,
101 order_side,
102 OrderType::Market,
103 quantity,
104 time_in_force,
105 false,
106 reduce_only,
107 quote_quantity,
108 false,
109 init_id,
110 ts_init,
111 ts_init,
112 None,
113 None,
114 None,
115 None,
116 None,
117 None,
118 None,
119 None,
120 None,
121 None,
122 None,
123 contingency_type,
124 order_list_id,
125 linked_order_ids,
126 parent_order_id,
127 exec_algorithm_id,
128 exec_algorithm_params,
129 exec_spawn_id,
130 tags,
131 )?;
132
133 Ok(Self {
134 core: OrderCore::new(init_order),
135 protection_price: None,
136 })
137 }
138
139 #[expect(clippy::too_many_arguments)]
145 #[must_use]
146 pub fn new(
147 trader_id: TraderId,
148 strategy_id: StrategyId,
149 instrument_id: InstrumentId,
150 client_order_id: ClientOrderId,
151 order_side: OrderSide,
152 quantity: Quantity,
153 time_in_force: TimeInForce,
154 init_id: UUID4,
155 ts_init: UnixNanos,
156 reduce_only: bool,
157 quote_quantity: bool,
158 contingency_type: Option<ContingencyType>,
159 order_list_id: Option<OrderListId>,
160 linked_order_ids: Option<Vec<ClientOrderId>>,
161 parent_order_id: Option<ClientOrderId>,
162 exec_algorithm_id: Option<ExecAlgorithmId>,
163 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
164 exec_spawn_id: Option<ClientOrderId>,
165 tags: Option<Vec<Ustr>>,
166 ) -> Self {
167 Self::new_checked(
168 trader_id,
169 strategy_id,
170 instrument_id,
171 client_order_id,
172 order_side,
173 quantity,
174 time_in_force,
175 init_id,
176 ts_init,
177 reduce_only,
178 quote_quantity,
179 contingency_type,
180 order_list_id,
181 linked_order_ids,
182 parent_order_id,
183 exec_algorithm_id,
184 exec_algorithm_params,
185 exec_spawn_id,
186 tags,
187 )
188 .unwrap_or_else(|e| panic!("{FAILED}: {e}"))
189 }
190}
191
192impl Deref for MarketOrder {
193 type Target = OrderCore;
194
195 fn deref(&self) -> &Self::Target {
196 &self.core
197 }
198}
199
200impl DerefMut for MarketOrder {
201 fn deref_mut(&mut self) -> &mut Self::Target {
202 &mut self.core
203 }
204}
205
206impl PartialEq for MarketOrder {
207 fn eq(&self, other: &Self) -> bool {
208 self.client_order_id == other.client_order_id
209 }
210}
211
212impl Order for MarketOrder {
213 fn into_any(self) -> OrderAny {
214 OrderAny::Market(self)
215 }
216
217 fn status(&self) -> OrderStatus {
218 self.status
219 }
220
221 fn trader_id(&self) -> TraderId {
222 self.trader_id
223 }
224
225 fn strategy_id(&self) -> StrategyId {
226 self.strategy_id
227 }
228
229 fn instrument_id(&self) -> InstrumentId {
230 self.instrument_id
231 }
232
233 fn symbol(&self) -> Symbol {
234 self.instrument_id.symbol
235 }
236
237 fn venue(&self) -> Venue {
238 self.instrument_id.venue
239 }
240
241 fn client_order_id(&self) -> ClientOrderId {
242 self.client_order_id
243 }
244
245 fn venue_order_id(&self) -> Option<VenueOrderId> {
246 self.venue_order_id
247 }
248
249 fn position_id(&self) -> Option<PositionId> {
250 self.position_id
251 }
252
253 fn account_id(&self) -> Option<AccountId> {
254 self.account_id
255 }
256
257 fn last_trade_id(&self) -> Option<TradeId> {
258 self.last_trade_id
259 }
260
261 fn order_side(&self) -> OrderSide {
262 self.side
263 }
264
265 fn order_type(&self) -> OrderType {
266 self.order_type
267 }
268
269 fn quantity(&self) -> Quantity {
270 self.quantity
271 }
272
273 fn time_in_force(&self) -> TimeInForce {
274 self.time_in_force
275 }
276
277 fn expire_time(&self) -> Option<UnixNanos> {
278 None
279 }
280
281 fn price(&self) -> Option<Price> {
282 self.protection_price
283 }
284
285 fn trigger_price(&self) -> Option<Price> {
286 None
287 }
288
289 fn trigger_type(&self) -> Option<TriggerType> {
290 None
291 }
292
293 fn liquidity_side(&self) -> Option<LiquiditySide> {
294 self.liquidity_side
295 }
296
297 fn is_post_only(&self) -> bool {
298 false
299 }
300
301 fn is_reduce_only(&self) -> bool {
302 self.is_reduce_only
303 }
304
305 fn is_quote_quantity(&self) -> bool {
306 self.is_quote_quantity
307 }
308
309 fn has_price(&self) -> bool {
310 self.protection_price.is_some()
311 }
312
313 fn display_qty(&self) -> Option<Quantity> {
314 None
315 }
316
317 fn limit_offset(&self) -> Option<Decimal> {
318 None
319 }
320
321 fn trailing_offset(&self) -> Option<Decimal> {
322 None
323 }
324
325 fn trailing_offset_type(&self) -> Option<TrailingOffsetType> {
326 None
327 }
328
329 fn emulation_trigger(&self) -> Option<TriggerType> {
330 None
331 }
332
333 fn trigger_instrument_id(&self) -> Option<InstrumentId> {
334 None
335 }
336
337 fn contingency_type(&self) -> Option<ContingencyType> {
338 self.contingency_type
339 }
340
341 fn order_list_id(&self) -> Option<OrderListId> {
342 self.order_list_id
343 }
344
345 fn linked_order_ids(&self) -> Option<&[ClientOrderId]> {
346 self.linked_order_ids.as_deref()
347 }
348
349 fn parent_order_id(&self) -> Option<ClientOrderId> {
350 self.parent_order_id
351 }
352
353 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
354 self.exec_algorithm_id
355 }
356
357 fn exec_algorithm_params(&self) -> Option<&IndexMap<Ustr, Ustr>> {
358 self.exec_algorithm_params.as_ref()
359 }
360
361 fn exec_spawn_id(&self) -> Option<ClientOrderId> {
362 self.exec_spawn_id
363 }
364
365 fn tags(&self) -> Option<&[Ustr]> {
366 self.tags.as_deref()
367 }
368
369 fn filled_qty(&self) -> Quantity {
370 self.filled_qty
371 }
372
373 fn voided_qty(&self) -> Quantity {
374 self.voided_qty
375 }
376
377 fn non_reopened_voided_qty(&self) -> Quantity {
378 self.non_reopened_voided_qty
379 }
380
381 fn leaves_qty(&self) -> Quantity {
382 self.leaves_qty
383 }
384
385 fn overfill_qty(&self) -> Quantity {
386 self.overfill_qty
387 }
388
389 fn avg_px(&self) -> Option<Decimal> {
390 self.avg_px
391 }
392
393 fn slippage(&self) -> Option<Decimal> {
394 self.slippage
395 }
396
397 fn init_id(&self) -> UUID4 {
398 self.init_id
399 }
400
401 fn ts_init(&self) -> UnixNanos {
402 self.ts_init
403 }
404
405 fn ts_submitted(&self) -> Option<UnixNanos> {
406 self.ts_submitted
407 }
408
409 fn ts_accepted(&self) -> Option<UnixNanos> {
410 self.ts_accepted
411 }
412
413 fn ts_closed(&self) -> Option<UnixNanos> {
414 self.ts_closed
415 }
416
417 fn ts_last(&self) -> UnixNanos {
418 self.ts_last
419 }
420
421 fn apply(&mut self, event: OrderEventAny) -> Result<(), OrderError> {
422 self.core.apply(event.clone())?;
423
424 if let OrderEventAny::Updated(ref event) = event {
425 self.update(event);
426 }
427
428 Ok(())
429 }
430
431 fn update(&mut self, event: &OrderUpdated) {
432 assert!(event.price.is_none(), "{}", OrderError::InvalidOrderEvent);
433 assert!(
434 event.trigger_price.is_none(),
435 "{}",
436 OrderError::InvalidOrderEvent
437 );
438
439 if let Some(protection_price) = event.protection_price {
440 self.protection_price = Some(protection_price);
441 }
442 self.core.apply_updated_quantity(event.quantity);
443 }
444
445 fn events(&self) -> Vec<&OrderEventAny> {
446 self.events.iter().collect()
447 }
448
449 fn venue_order_ids(&self) -> Vec<&VenueOrderId> {
450 self.venue_order_ids.iter().collect()
451 }
452
453 fn trade_ids(&self) -> Vec<&TradeId> {
454 self.trade_ids.iter().collect()
455 }
456
457 fn commissions(&self) -> &IndexMap<Currency, Money> {
458 &self.commissions
459 }
460
461 fn is_triggered(&self) -> Option<bool> {
462 None
463 }
464
465 fn set_position_id(&mut self, position_id: Option<PositionId>) {
466 self.position_id = position_id;
467 }
468
469 fn set_quantity(&mut self, quantity: Quantity) {
470 self.quantity = quantity;
471 }
472
473 fn set_leaves_qty(&mut self, leaves_qty: Quantity) {
474 self.leaves_qty = leaves_qty;
475 }
476
477 fn set_emulation_trigger(&mut self, emulation_trigger: Option<TriggerType>) {
478 self.emulation_trigger = emulation_trigger;
479 }
480
481 fn set_is_quote_quantity(&mut self, is_quote_quantity: bool) {
482 self.is_quote_quantity = is_quote_quantity;
483 }
484
485 fn set_liquidity_side(&mut self, liquidity_side: LiquiditySide) {
486 self.liquidity_side = Some(liquidity_side);
487 }
488
489 fn would_reduce_only(&self, side: PositionSide, position_qty: Quantity) -> bool {
490 self.core.would_reduce_only(side, position_qty)
491 }
492
493 fn previous_status(&self) -> Option<OrderStatus> {
494 self.core.previous_status
495 }
496}
497
498impl Display for MarketOrder {
499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
500 write!(
501 f,
502 "MarketOrder(\
503 {} {} {} @ {} {}, \
504 status={}, \
505 client_order_id={}, \
506 venue_order_id={}, \
507 position_id={}, \
508 exec_algorithm_id={}, \
509 exec_spawn_id={}, \
510 tags={:?}\
511 )",
512 self.side,
513 self.quantity.to_formatted_string(),
514 self.instrument_id,
515 self.order_type,
516 self.time_in_force,
517 self.status,
518 self.client_order_id,
519 self.venue_order_id.map_or_else(
520 || "None".to_string(),
521 |venue_order_id| format!("{venue_order_id}")
522 ),
523 self.position_id.map_or_else(
524 || "None".to_string(),
525 |position_id| format!("{position_id}")
526 ),
527 self.exec_algorithm_id
528 .map_or_else(|| "None".to_string(), |id| format!("{id}")),
529 self.exec_spawn_id
530 .map_or_else(|| "None".to_string(), |id| format!("{id}")),
531 self.tags
532 )
533 }
534}
535
536impl TryFrom<OrderInitialized> for MarketOrder {
537 type Error = OrderError;
538
539 fn try_from(event: OrderInitialized) -> Result<Self, Self::Error> {
540 Self::new_checked(
541 event.trader_id,
542 event.strategy_id,
543 event.instrument_id,
544 event.client_order_id,
545 event.order_side,
546 event.quantity,
547 event.time_in_force,
548 event.event_id,
549 event.ts_event,
550 event.reduce_only,
551 event.quote_quantity,
552 event.contingency_type,
553 event.order_list_id,
554 event.linked_order_ids,
555 event.parent_order_id,
556 event.exec_algorithm_id,
557 event.exec_algorithm_params,
558 event.exec_spawn_id,
559 event.tags,
560 )
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 use nautilus_core::{UUID4, UnixNanos, correctness::CorrectnessError};
567 use rstest::rstest;
568
569 use crate::{
570 enums::{ContingencyType, OrderSide, OrderType, TimeInForce},
571 events::{
572 OrderEventAny, OrderInitialized, OrderUpdated, order::spec::OrderInitializedSpec,
573 },
574 identifiers::{ClientOrderId, ExecAlgorithmId, InstrumentId, StrategyId, TraderId},
575 instruments::{CurrencyPair, stubs::*},
576 orders::{
577 MarketOrder, Order, OrderError, builder::OrderTestBuilder, stubs::TestOrderStubs,
578 },
579 types::{Price, Quantity},
580 };
581
582 #[rstest]
583 #[should_panic(
584 expected = "Condition failed: invalid `Quantity` for 'quantity' not positive, was 0"
585 )]
586 fn test_positive_quantity_condition(audusd_sim: CurrencyPair) {
587 let _ = OrderTestBuilder::new(OrderType::Market)
588 .instrument_id(audusd_sim.id)
589 .side(OrderSide::Buy)
590 .quantity(Quantity::from(0))
591 .build();
592 }
593
594 #[rstest]
595 #[should_panic(expected = "GTD not supported for Market orders")]
596 fn test_gtd_condition(audusd_sim: CurrencyPair) {
597 let _ = OrderTestBuilder::new(OrderType::Market)
598 .instrument_id(audusd_sim.id)
599 .side(OrderSide::Buy)
600 .quantity(Quantity::from(100))
601 .time_in_force(TimeInForce::Gtd)
602 .build();
603 }
604 #[rstest]
605 fn test_market_order_creation(audusd_sim: CurrencyPair) {
606 let order = OrderTestBuilder::new(OrderType::Market)
608 .instrument_id(audusd_sim.id)
609 .quantity(Quantity::from(10))
610 .side(OrderSide::Buy)
611 .time_in_force(TimeInForce::Ioc)
612 .build();
613
614 assert_eq!(order.time_in_force(), TimeInForce::Ioc);
616 assert_eq!(order.order_type(), OrderType::Market);
617 assert!(order.price().is_none());
618 }
619
620 #[rstest]
621 fn test_direct_market_order_rejects_contingency_without_linked_orders(
622 audusd_sim: CurrencyPair,
623 ) {
624 let result =
625 market_order_with_metadata(audusd_sim.id, Some(ContingencyType::Oco), None, None, None);
626
627 let Err(OrderError::Invariant(CorrectnessError::PredicateViolation { message })) = result
628 else {
629 panic!("expected a predicate violation, was {result:?}");
630 };
631 assert_eq!(
632 message,
633 "`linked_order_ids` is required for contingent orders"
634 );
635 }
636
637 #[rstest]
638 fn test_direct_market_order_rejects_exec_algorithm_without_spawn(audusd_sim: CurrencyPair) {
639 let result = market_order_with_metadata(
640 audusd_sim.id,
641 None,
642 None,
643 Some(ExecAlgorithmId::from("TWAP")),
644 None,
645 );
646
647 let Err(OrderError::Invariant(CorrectnessError::PredicateViolation { message })) = result
648 else {
649 panic!("expected a predicate violation, was {result:?}");
650 };
651 assert_eq!(
652 message,
653 "`exec_spawn_id` is required when `exec_algorithm_id` is set"
654 );
655 }
656
657 #[rstest]
658 #[case(
659 OrderInitialized {
660 contingency_type: Some(ContingencyType::Oco),
661 linked_order_ids: None,
662 ..OrderInitialized::default()
663 },
664 "`linked_order_ids` is required for contingent orders"
665 )]
666 #[case(
667 OrderInitialized {
668 exec_algorithm_id: Some(ExecAlgorithmId::from("TWAP")),
669 exec_spawn_id: None,
670 ..OrderInitialized::default()
671 },
672 "`exec_spawn_id` is required when `exec_algorithm_id` is set"
673 )]
674 fn test_market_order_reconstruction_rejects_invalid_metadata(
675 #[case] event: OrderInitialized,
676 #[case] expected: &str,
677 ) {
678 let result = MarketOrder::try_from(event);
679
680 let Err(OrderError::Invariant(CorrectnessError::PredicateViolation { message })) = result
681 else {
682 panic!("expected a predicate violation, was {result:?}");
683 };
684 assert_eq!(message, expected);
685 }
686
687 #[rstest]
688 fn test_market_order_update(audusd_sim: CurrencyPair) {
689 let order = OrderTestBuilder::new(OrderType::Market)
691 .instrument_id(audusd_sim.id)
692 .quantity(Quantity::from(10))
693 .side(OrderSide::Buy)
694 .build();
695
696 let mut accepted_order = TestOrderStubs::make_accepted_order(&order);
697
698 let updated_quantity = Quantity::from(5);
700
701 let event = OrderUpdated {
702 client_order_id: accepted_order.client_order_id(),
703 strategy_id: accepted_order.strategy_id(),
704 quantity: updated_quantity,
705 ..Default::default()
706 };
707
708 accepted_order.apply(OrderEventAny::Updated(event)).unwrap();
709
710 assert_eq!(accepted_order.quantity(), updated_quantity);
712 }
713
714 #[rstest]
715 #[case(Some(Price::new(95.0, 2)), None)]
716 #[case(None, Some(Price::new(95.0, 2)))]
717 fn test_market_order_rejects_invalid_update_atomically(
718 audusd_sim: CurrencyPair,
719 #[case] price: Option<Price>,
720 #[case] trigger_price: Option<Price>,
721 ) {
722 let order = OrderTestBuilder::new(OrderType::Market)
723 .instrument_id(audusd_sim.id)
724 .quantity(Quantity::from(10))
725 .side(OrderSide::Buy)
726 .build();
727 let mut accepted_order = TestOrderStubs::make_accepted_order(&order);
728 let state = (
729 accepted_order.status(),
730 accepted_order.previous_status(),
731 accepted_order.ts_last(),
732 accepted_order.events().len(),
733 );
734 let event = OrderUpdated {
735 client_order_id: accepted_order.client_order_id(),
736 strategy_id: accepted_order.strategy_id(),
737 price,
738 trigger_price,
739 ..Default::default()
740 };
741
742 let result = accepted_order.apply(OrderEventAny::Updated(event));
743
744 assert!(matches!(result, Err(OrderError::InvalidOrderEvent)));
745 assert_eq!(accepted_order.status(), state.0);
746 assert_eq!(accepted_order.previous_status(), state.1);
747 assert_eq!(accepted_order.ts_last(), state.2);
748 assert_eq!(accepted_order.events().len(), state.3);
749 }
750
751 #[rstest]
752 fn test_market_order_from_order_initialized(audusd_sim: CurrencyPair) {
753 let order_initialized = OrderInitializedSpec::builder()
755 .order_type(OrderType::Market)
756 .instrument_id(audusd_sim.id)
757 .quantity(Quantity::from(10))
758 .order_side(OrderSide::Buy)
759 .build();
760
761 let order: MarketOrder = order_initialized.clone().try_into().unwrap();
763
764 assert_eq!(order.trader_id(), order_initialized.trader_id);
766 assert_eq!(order.strategy_id(), order_initialized.strategy_id);
767 assert_eq!(order.instrument_id(), order_initialized.instrument_id);
768 assert_eq!(order.client_order_id(), order_initialized.client_order_id);
769 assert_eq!(order.order_side(), order_initialized.order_side);
770 assert_eq!(order.quantity(), order_initialized.quantity);
771 }
772
773 #[rstest]
774 #[should_panic(
775 expected = "Condition failed: invalid `Quantity` for 'quantity' not positive, was 0"
776 )]
777 fn test_market_order_invalid_quantity(audusd_sim: CurrencyPair) {
778 let _ = OrderTestBuilder::new(OrderType::Market)
779 .instrument_id(audusd_sim.id)
780 .quantity(Quantity::from(0))
781 .side(OrderSide::Buy)
782 .build();
783 }
784
785 #[rstest]
786 fn test_display(audusd_sim: CurrencyPair) {
787 let order = OrderTestBuilder::new(OrderType::Market)
788 .instrument_id(audusd_sim.id)
789 .quantity(Quantity::from(10))
790 .side(OrderSide::Buy)
791 .build();
792
793 assert_eq!(
795 order.to_string(),
796 format!(
797 "MarketOrder({} {} {} @ {} {}, status=INITIALIZED, client_order_id={}, venue_order_id=None, position_id=None, exec_algorithm_id=None, exec_spawn_id=None, tags=None)",
798 order.order_side(),
799 order.quantity().to_formatted_string(),
800 order.instrument_id(),
801 order.order_type(),
802 order.time_in_force(),
803 order.client_order_id()
804 )
805 );
806 }
807
808 #[rstest]
809 fn test_stop_market_order_protection_price_update(audusd_sim: CurrencyPair) {
810 let order = OrderTestBuilder::new(OrderType::Market)
812 .instrument_id(audusd_sim.id)
813 .quantity(Quantity::from(10))
814 .side(OrderSide::Buy)
815 .build();
816
817 let mut accepted_order = TestOrderStubs::make_accepted_order(&order);
818
819 let calculated_protection_price = Price::new(95.0, 2);
821
822 let event = OrderUpdated {
823 client_order_id: accepted_order.client_order_id(),
824 strategy_id: accepted_order.strategy_id(),
825 protection_price: Some(calculated_protection_price),
826 ..Default::default()
827 };
828
829 assert_eq!(accepted_order.price(), None);
830 assert!(!accepted_order.has_price());
831
832 accepted_order.apply(OrderEventAny::Updated(event)).unwrap();
833
834 assert_eq!(accepted_order.price(), Some(calculated_protection_price));
836 assert!(accepted_order.has_price());
837 }
838
839 fn market_order_with_metadata(
840 instrument_id: InstrumentId,
841 contingency_type: Option<ContingencyType>,
842 linked_order_ids: Option<Vec<ClientOrderId>>,
843 exec_algorithm_id: Option<ExecAlgorithmId>,
844 exec_spawn_id: Option<ClientOrderId>,
845 ) -> Result<MarketOrder, OrderError> {
846 MarketOrder::new_checked(
847 TraderId::from("TRADER-001"),
848 StrategyId::from("S-001"),
849 instrument_id,
850 ClientOrderId::from("O-001"),
851 OrderSide::Buy,
852 Quantity::from(1),
853 TimeInForce::Gtc,
854 UUID4::new(),
855 UnixNanos::default(),
856 false,
857 false,
858 contingency_type,
859 None,
860 linked_order_ids,
861 None,
862 exec_algorithm_id,
863 None,
864 exec_spawn_id,
865 None,
866 )
867 }
868}