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) {
76 self.order_id_generator.set_count(count);
77 }
78
79 pub const fn set_order_list_id_count(&mut self, count: usize) {
81 self.order_list_id_generator.set_count(count);
82 }
83
84 pub fn generate_client_order_id(&mut self) -> ClientOrderId {
86 self.order_id_generator.generate()
87 }
88
89 pub fn generate_order_list_id(&mut self) -> OrderListId {
91 self.order_list_id_generator.generate()
92 }
93
94 pub const fn reset_factory(&mut self) {
96 self.order_id_generator.reset();
97 self.order_list_id_generator.reset();
98 }
99
100 #[allow(clippy::too_many_arguments)]
102 pub fn market(
103 &mut self,
104 instrument_id: InstrumentId,
105 order_side: OrderSide,
106 quantity: Quantity,
107 time_in_force: Option<TimeInForce>,
108 reduce_only: Option<bool>,
109 quote_quantity: Option<bool>,
110 exec_algorithm_id: Option<ExecAlgorithmId>,
111 exec_algorithm_params: Option<IndexMap<Ustr, Ustr>>,
112 tags: Option<Vec<Ustr>>,
113 client_order_id: Option<ClientOrderId>,
114 ) -> OrderAny {
115 let client_order_id = client_order_id.unwrap_or_else(|| self.generate_client_order_id());
116 let exec_spawn_id: Option<ClientOrderId> = if exec_algorithm_id.is_none() {
117 None
118 } else {
119 Some(client_order_id)
120 };
121 let order = MarketOrder::new(
122 self.trader_id,
123 self.strategy_id,
124 instrument_id,
125 client_order_id,
126 order_side,
127 quantity,
128 time_in_force.unwrap_or(TimeInForce::Gtc),
129 UUID4::new(),
130 self.clock.get_time_ns(),
131 reduce_only.unwrap_or(false),
132 quote_quantity.unwrap_or(false),
133 Some(ContingencyType::NoContingency),
134 None,
135 None,
136 None,
137 exec_algorithm_id,
138 exec_algorithm_params,
139 exec_spawn_id,
140 tags,
141 );
142 OrderAny::Market(order)
143 }
144}
145
146#[cfg(test)]
150pub mod tests {
151 use nautilus_core::time::get_atomic_clock_static;
152 use nautilus_model::{
153 enums::{OrderSide, TimeInForce},
154 identifiers::{
155 ClientOrderId, InstrumentId, OrderListId,
156 stubs::{strategy_id_ema_cross, trader_id},
157 },
158 orders::Order,
159 };
160 use rstest::{fixture, rstest};
161
162 use crate::factories::OrderFactory;
163
164 #[fixture]
165 pub fn order_factory() -> OrderFactory {
166 let trader_id = trader_id();
167 let strategy_id = strategy_id_ema_cross();
168 OrderFactory::new(
169 trader_id,
170 strategy_id,
171 None,
172 None,
173 get_atomic_clock_static(),
174 )
175 }
176
177 #[rstest]
178 fn test_generate_client_order_id(mut order_factory: OrderFactory) {
179 let client_order_id = order_factory.generate_client_order_id();
180 assert_eq!(
181 client_order_id,
182 ClientOrderId::new("O-19700101-000000-001-001-1")
183 );
184 }
185
186 #[rstest]
187 fn test_generate_order_list_id(mut order_factory: OrderFactory) {
188 let order_list_id = order_factory.generate_order_list_id();
189 assert_eq!(
190 order_list_id,
191 OrderListId::new("OL-19700101-000000-001-001-1")
192 );
193 }
194
195 #[rstest]
196 fn test_set_client_order_id_count(mut order_factory: OrderFactory) {
197 order_factory.set_client_order_id_count(10);
198 let client_order_id = order_factory.generate_client_order_id();
199 assert_eq!(
200 client_order_id,
201 ClientOrderId::new("O-19700101-000000-001-001-11")
202 );
203 }
204
205 #[rstest]
206 fn test_set_order_list_id_count(mut order_factory: OrderFactory) {
207 order_factory.set_order_list_id_count(10);
208 let order_list_id = order_factory.generate_order_list_id();
209 assert_eq!(
210 order_list_id,
211 OrderListId::new("OL-19700101-000000-001-001-11")
212 );
213 }
214
215 #[rstest]
216 fn test_reset_factory(mut order_factory: OrderFactory) {
217 order_factory.generate_order_list_id();
218 order_factory.generate_client_order_id();
219 order_factory.reset_factory();
220 let client_order_id = order_factory.generate_client_order_id();
221 let order_list_id = order_factory.generate_order_list_id();
222 assert_eq!(
223 client_order_id,
224 ClientOrderId::new("O-19700101-000000-001-001-1")
225 );
226 assert_eq!(
227 order_list_id,
228 OrderListId::new("OL-19700101-000000-001-001-1")
229 );
230 }
231
232 #[rstest]
233 fn test_market_order(mut order_factory: OrderFactory) {
234 let market_order = order_factory.market(
235 InstrumentId::from("BTCUSDT.BINANCE"),
236 OrderSide::Buy,
237 100.into(),
238 Some(TimeInForce::Gtc),
239 Some(false),
240 Some(false),
241 None,
242 None,
243 None,
244 None,
245 );
246 assert_eq!(market_order.instrument_id(), "BTCUSDT.BINANCE".into());
248 assert_eq!(market_order.order_side(), OrderSide::Buy);
249 assert_eq!(market_order.quantity(), 100.into());
250 assert_eq!(market_order.exec_algorithm_id(), None);
254 assert_eq!(
258 market_order.client_order_id(),
259 ClientOrderId::new("O-19700101-000000-001-001-1")
260 );
261 }
263}