1use indexmap::IndexMap;
19use nautilus_core::{AtomicTime, UUID4};
20use nautilus_model::{
21 enums::{ContingencyType, OrderSide, TimeInForce},
22 identifiers::{
23 ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, StrategyId, TraderId,
24 },
25 orders::{MarketOrder, OrderAny},
26 types::Quantity,
27};
28use ustr::Ustr;
29
30use crate::generators::{
31 client_order_id::ClientOrderIdGenerator, order_list_id::OrderListIdGenerator,
32};
33
34#[repr(C)]
35#[derive(Debug)]
36pub struct OrderFactory {
37 clock: &'static AtomicTime,
38 trader_id: TraderId,
39 strategy_id: StrategyId,
40 order_id_generator: ClientOrderIdGenerator,
41 order_list_id_generator: OrderListIdGenerator,
42}
43
44impl OrderFactory {
45 pub fn new(
47 trader_id: TraderId,
48 strategy_id: StrategyId,
49 init_order_id_count: Option<usize>,
50 init_order_list_id_count: Option<usize>,
51 clock: &'static AtomicTime,
52 ) -> Self {
53 let order_id_generator = ClientOrderIdGenerator::new(
54 trader_id,
55 strategy_id,
56 init_order_id_count.unwrap_or(0),
57 clock,
58 );
59 let order_list_id_generator = OrderListIdGenerator::new(
60 trader_id,
61 strategy_id,
62 init_order_list_id_count.unwrap_or(0),
63 clock,
64 );
65 Self {
66 clock,
67 trader_id,
68 strategy_id,
69 order_id_generator,
70 order_list_id_generator,
71 }
72 }
73
74 pub const fn set_client_order_id_count(&mut self, count: usize) {
75 self.order_id_generator.set_count(count);
76 }
77
78 pub const fn set_order_list_id_count(&mut self, count: usize) {
79 self.order_list_id_generator.set_count(count);
80 }
81
82 pub fn generate_client_order_id(&mut self) -> ClientOrderId {
83 self.order_id_generator.generate()
84 }
85
86 pub fn generate_order_list_id(&mut self) -> OrderListId {
87 self.order_list_id_generator.generate()
88 }
89
90 pub const fn reset_factory(&mut self) {
91 self.order_id_generator.reset();
92 self.order_list_id_generator.reset();
93 }
94
95 #[allow(clippy::too_many_arguments)]
96 pub fn market(
97 &mut self,
98 instrument_id: InstrumentId,
99 order_side: OrderSide,
100 quantity: Quantity,
101 time_in_force: Option<TimeInForce>,
102 reduce_only: Option<bool>,
103 quote_quantity: Option<bool>,
104 exec_algorithm_id: Option<ExecAlgorithmId>,
105 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
106 tags: Option<Vec<Ustr>>,
107 client_order_id: Option<ClientOrderId>,
108 ) -> OrderAny {
109 let client_order_id = client_order_id.unwrap_or_else(|| self.generate_client_order_id());
110 let exec_spawn_id: Option<ClientOrderId> = if exec_algorithm_id.is_none() {
111 None
112 } else {
113 Some(client_order_id)
114 };
115 let order = MarketOrder::new(
116 self.trader_id,
117 self.strategy_id,
118 instrument_id,
119 client_order_id,
120 order_side,
121 quantity,
122 time_in_force.unwrap_or(TimeInForce::Gtc),
123 UUID4::new(),
124 self.clock.get_time_ns(),
125 reduce_only.unwrap_or(false),
126 quote_quantity.unwrap_or(false),
127 Some(ContingencyType::NoContingency),
128 None,
129 None,
130 None,
131 exec_algorithm_id,
132 exec_algorithm_params,
133 exec_spawn_id,
134 tags,
135 );
136 OrderAny::Market(order)
137 }
138}
139
140#[cfg(test)]
144pub mod tests {
145 use nautilus_core::time::get_atomic_clock_static;
146 use nautilus_model::{
147 enums::{OrderSide, TimeInForce},
148 identifiers::{
149 stubs::{strategy_id_ema_cross, trader_id},
150 ClientOrderId, InstrumentId, OrderListId,
151 },
152 };
153 use rstest::{fixture, rstest};
154
155 use crate::factories::OrderFactory;
156
157 #[fixture]
158 pub fn order_factory() -> OrderFactory {
159 let trader_id = trader_id();
160 let strategy_id = strategy_id_ema_cross();
161 OrderFactory::new(
162 trader_id,
163 strategy_id,
164 None,
165 None,
166 get_atomic_clock_static(),
167 )
168 }
169
170 #[rstest]
171 fn test_generate_client_order_id(mut order_factory: OrderFactory) {
172 let client_order_id = order_factory.generate_client_order_id();
173 assert_eq!(
174 client_order_id,
175 ClientOrderId::new("O-19700101-000000-001-001-1")
176 );
177 }
178
179 #[rstest]
180 fn test_generate_order_list_id(mut order_factory: OrderFactory) {
181 let order_list_id = order_factory.generate_order_list_id();
182 assert_eq!(
183 order_list_id,
184 OrderListId::new("OL-19700101-000000-001-001-1")
185 );
186 }
187
188 #[rstest]
189 fn test_set_client_order_id_count(mut order_factory: OrderFactory) {
190 order_factory.set_client_order_id_count(10);
191 let client_order_id = order_factory.generate_client_order_id();
192 assert_eq!(
193 client_order_id,
194 ClientOrderId::new("O-19700101-000000-001-001-11")
195 );
196 }
197
198 #[rstest]
199 fn test_set_order_list_id_count(mut order_factory: OrderFactory) {
200 order_factory.set_order_list_id_count(10);
201 let order_list_id = order_factory.generate_order_list_id();
202 assert_eq!(
203 order_list_id,
204 OrderListId::new("OL-19700101-000000-001-001-11")
205 );
206 }
207
208 #[rstest]
209 fn test_reset_factory(mut order_factory: OrderFactory) {
210 order_factory.generate_order_list_id();
211 order_factory.generate_client_order_id();
212 order_factory.reset_factory();
213 let client_order_id = order_factory.generate_client_order_id();
214 let order_list_id = order_factory.generate_order_list_id();
215 assert_eq!(
216 client_order_id,
217 ClientOrderId::new("O-19700101-000000-001-001-1")
218 );
219 assert_eq!(
220 order_list_id,
221 OrderListId::new("OL-19700101-000000-001-001-1")
222 );
223 }
224
225 #[rstest]
226 fn test_market_order(mut order_factory: OrderFactory) {
227 let market_order = order_factory.market(
228 InstrumentId::from("BTCUSDT.BINANCE"),
229 OrderSide::Buy,
230 100.into(),
231 Some(TimeInForce::Gtc),
232 Some(false),
233 Some(false),
234 None,
235 None,
236 None,
237 None,
238 );
239 assert_eq!(market_order.instrument_id(), "BTCUSDT.BINANCE".into());
241 assert_eq!(market_order.order_side(), OrderSide::Buy);
242 assert_eq!(market_order.quantity(), 100.into());
243 assert_eq!(market_order.exec_algorithm_id(), None);
247 assert_eq!(
251 market_order.client_order_id(),
252 ClientOrderId::new("O-19700101-000000-001-001-1")
253 );
254 }
256}