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 use_uuids_for_client_order_ids: bool,
53 use_hyphens_in_client_order_ids: bool,
54 ) -> Self {
55 let order_id_generator = ClientOrderIdGenerator::new(
56 trader_id,
57 strategy_id,
58 init_order_id_count.unwrap_or(0),
59 clock,
60 use_uuids_for_client_order_ids,
61 use_hyphens_in_client_order_ids,
62 );
63 let order_list_id_generator = OrderListIdGenerator::new(
64 trader_id,
65 strategy_id,
66 init_order_list_id_count.unwrap_or(0),
67 clock,
68 );
69 Self {
70 clock,
71 trader_id,
72 strategy_id,
73 order_id_generator,
74 order_list_id_generator,
75 }
76 }
77
78 pub const fn set_client_order_id_count(&mut self, count: usize) {
80 self.order_id_generator.set_count(count);
81 }
82
83 pub const fn set_order_list_id_count(&mut self, count: usize) {
85 self.order_list_id_generator.set_count(count);
86 }
87
88 pub fn generate_client_order_id(&mut self) -> ClientOrderId {
90 self.order_id_generator.generate()
91 }
92
93 pub fn generate_order_list_id(&mut self) -> OrderListId {
95 self.order_list_id_generator.generate()
96 }
97
98 pub const fn reset_factory(&mut self) {
100 self.order_id_generator.reset();
101 self.order_list_id_generator.reset();
102 }
103
104 #[allow(clippy::too_many_arguments)]
106 pub fn market(
107 &mut self,
108 instrument_id: InstrumentId,
109 order_side: OrderSide,
110 quantity: Quantity,
111 time_in_force: Option<TimeInForce>,
112 reduce_only: Option<bool>,
113 quote_quantity: Option<bool>,
114 exec_algorithm_id: Option<ExecAlgorithmId>,
115 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
116 tags: Option<Vec<Ustr>>,
117 client_order_id: Option<ClientOrderId>,
118 ) -> OrderAny {
119 let client_order_id = client_order_id.unwrap_or_else(|| self.generate_client_order_id());
120 let exec_spawn_id: Option<ClientOrderId> = if exec_algorithm_id.is_none() {
121 None
122 } else {
123 Some(client_order_id)
124 };
125 let order = MarketOrder::new(
126 self.trader_id,
127 self.strategy_id,
128 instrument_id,
129 client_order_id,
130 order_side,
131 quantity,
132 time_in_force.unwrap_or(TimeInForce::Gtc),
133 UUID4::new(),
134 self.clock.get_time_ns(),
135 reduce_only.unwrap_or(false),
136 quote_quantity.unwrap_or(false),
137 Some(ContingencyType::NoContingency),
138 None,
139 None,
140 None,
141 exec_algorithm_id,
142 exec_algorithm_params,
143 exec_spawn_id,
144 tags,
145 );
146 OrderAny::Market(order)
147 }
148}
149
150#[cfg(test)]
154pub mod tests {
155 use nautilus_core::time::get_atomic_clock_static;
156 use nautilus_model::{
157 enums::{OrderSide, TimeInForce},
158 identifiers::{
159 ClientOrderId, InstrumentId, OrderListId,
160 stubs::{strategy_id_ema_cross, trader_id},
161 },
162 orders::Order,
163 };
164 use rstest::{fixture, rstest};
165
166 use crate::factories::OrderFactory;
167
168 #[fixture]
169 pub fn order_factory() -> OrderFactory {
170 let trader_id = trader_id();
171 let strategy_id = strategy_id_ema_cross();
172 OrderFactory::new(
173 trader_id,
174 strategy_id,
175 None,
176 None,
177 get_atomic_clock_static(),
178 false, true, )
181 }
182
183 #[rstest]
184 fn test_generate_client_order_id(mut order_factory: OrderFactory) {
185 let client_order_id = order_factory.generate_client_order_id();
186 assert_eq!(
187 client_order_id,
188 ClientOrderId::new("O-19700101-000000-001-001-1")
189 );
190 }
191
192 #[rstest]
193 fn test_generate_order_list_id(mut order_factory: OrderFactory) {
194 let order_list_id = order_factory.generate_order_list_id();
195 assert_eq!(
196 order_list_id,
197 OrderListId::new("OL-19700101-000000-001-001-1")
198 );
199 }
200
201 #[rstest]
202 fn test_set_client_order_id_count(mut order_factory: OrderFactory) {
203 order_factory.set_client_order_id_count(10);
204 let client_order_id = order_factory.generate_client_order_id();
205 assert_eq!(
206 client_order_id,
207 ClientOrderId::new("O-19700101-000000-001-001-11")
208 );
209 }
210
211 #[rstest]
212 fn test_set_order_list_id_count(mut order_factory: OrderFactory) {
213 order_factory.set_order_list_id_count(10);
214 let order_list_id = order_factory.generate_order_list_id();
215 assert_eq!(
216 order_list_id,
217 OrderListId::new("OL-19700101-000000-001-001-11")
218 );
219 }
220
221 #[rstest]
222 fn test_reset_factory(mut order_factory: OrderFactory) {
223 order_factory.generate_order_list_id();
224 order_factory.generate_client_order_id();
225 order_factory.reset_factory();
226 let client_order_id = order_factory.generate_client_order_id();
227 let order_list_id = order_factory.generate_order_list_id();
228 assert_eq!(
229 client_order_id,
230 ClientOrderId::new("O-19700101-000000-001-001-1")
231 );
232 assert_eq!(
233 order_list_id,
234 OrderListId::new("OL-19700101-000000-001-001-1")
235 );
236 }
237
238 #[fixture]
239 pub fn order_factory_with_uuids() -> OrderFactory {
240 let trader_id = trader_id();
241 let strategy_id = strategy_id_ema_cross();
242 OrderFactory::new(
243 trader_id,
244 strategy_id,
245 None,
246 None,
247 get_atomic_clock_static(),
248 true, true, )
251 }
252
253 #[fixture]
254 pub fn order_factory_with_hyphens_removed() -> OrderFactory {
255 let trader_id = trader_id();
256 let strategy_id = strategy_id_ema_cross();
257 OrderFactory::new(
258 trader_id,
259 strategy_id,
260 None,
261 None,
262 get_atomic_clock_static(),
263 false, false, )
266 }
267
268 #[fixture]
269 pub fn order_factory_with_uuids_and_hyphens_removed() -> OrderFactory {
270 let trader_id = trader_id();
271 let strategy_id = strategy_id_ema_cross();
272 OrderFactory::new(
273 trader_id,
274 strategy_id,
275 None,
276 None,
277 get_atomic_clock_static(),
278 true, false, )
281 }
282
283 #[rstest]
284 fn test_generate_client_order_id_with_uuids(mut order_factory_with_uuids: OrderFactory) {
285 let client_order_id = order_factory_with_uuids.generate_client_order_id();
286
287 assert_eq!(client_order_id.as_str().len(), 36);
289 assert!(client_order_id.as_str().contains('-'));
290 }
291
292 #[rstest]
293 fn test_generate_client_order_id_with_hyphens_removed(
294 mut order_factory_with_hyphens_removed: OrderFactory,
295 ) {
296 let client_order_id = order_factory_with_hyphens_removed.generate_client_order_id();
297
298 assert_eq!(
299 client_order_id,
300 ClientOrderId::new("O197001010000000010011")
301 );
302 assert!(!client_order_id.as_str().contains('-'));
303 }
304
305 #[rstest]
306 fn test_generate_client_order_id_with_uuids_and_hyphens_removed(
307 mut order_factory_with_uuids_and_hyphens_removed: OrderFactory,
308 ) {
309 let client_order_id =
310 order_factory_with_uuids_and_hyphens_removed.generate_client_order_id();
311
312 assert_eq!(client_order_id.as_str().len(), 32);
314 assert!(!client_order_id.as_str().contains('-'));
315 }
316
317 #[rstest]
318 fn test_market_order(mut order_factory: OrderFactory) {
319 let market_order = order_factory.market(
320 InstrumentId::from("BTCUSDT.BINANCE"),
321 OrderSide::Buy,
322 100.into(),
323 Some(TimeInForce::Gtc),
324 Some(false),
325 Some(false),
326 None,
327 None,
328 None,
329 None,
330 );
331 assert_eq!(market_order.instrument_id(), "BTCUSDT.BINANCE".into());
333 assert_eq!(market_order.order_side(), OrderSide::Buy);
334 assert_eq!(market_order.quantity(), 100.into());
335 assert_eq!(market_order.exec_algorithm_id(), None);
339 assert_eq!(
343 market_order.client_order_id(),
344 ClientOrderId::new("O-19700101-000000-001-001-1")
345 );
346 }
348}