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 ClientOrderId, InstrumentId, OrderListId,
150 stubs::{strategy_id_ema_cross, trader_id},
151 },
152 orders::Order,
153 };
154 use rstest::{fixture, rstest};
155
156 use crate::factories::OrderFactory;
157
158 #[fixture]
159 pub fn order_factory() -> OrderFactory {
160 let trader_id = trader_id();
161 let strategy_id = strategy_id_ema_cross();
162 OrderFactory::new(
163 trader_id,
164 strategy_id,
165 None,
166 None,
167 get_atomic_clock_static(),
168 )
169 }
170
171 #[rstest]
172 fn test_generate_client_order_id(mut order_factory: OrderFactory) {
173 let client_order_id = order_factory.generate_client_order_id();
174 assert_eq!(
175 client_order_id,
176 ClientOrderId::new("O-19700101-000000-001-001-1")
177 );
178 }
179
180 #[rstest]
181 fn test_generate_order_list_id(mut order_factory: OrderFactory) {
182 let order_list_id = order_factory.generate_order_list_id();
183 assert_eq!(
184 order_list_id,
185 OrderListId::new("OL-19700101-000000-001-001-1")
186 );
187 }
188
189 #[rstest]
190 fn test_set_client_order_id_count(mut order_factory: OrderFactory) {
191 order_factory.set_client_order_id_count(10);
192 let client_order_id = order_factory.generate_client_order_id();
193 assert_eq!(
194 client_order_id,
195 ClientOrderId::new("O-19700101-000000-001-001-11")
196 );
197 }
198
199 #[rstest]
200 fn test_set_order_list_id_count(mut order_factory: OrderFactory) {
201 order_factory.set_order_list_id_count(10);
202 let order_list_id = order_factory.generate_order_list_id();
203 assert_eq!(
204 order_list_id,
205 OrderListId::new("OL-19700101-000000-001-001-11")
206 );
207 }
208
209 #[rstest]
210 fn test_reset_factory(mut order_factory: OrderFactory) {
211 order_factory.generate_order_list_id();
212 order_factory.generate_client_order_id();
213 order_factory.reset_factory();
214 let client_order_id = order_factory.generate_client_order_id();
215 let order_list_id = order_factory.generate_order_list_id();
216 assert_eq!(
217 client_order_id,
218 ClientOrderId::new("O-19700101-000000-001-001-1")
219 );
220 assert_eq!(
221 order_list_id,
222 OrderListId::new("OL-19700101-000000-001-001-1")
223 );
224 }
225
226 #[rstest]
227 fn test_market_order(mut order_factory: OrderFactory) {
228 let market_order = order_factory.market(
229 InstrumentId::from("BTCUSDT.BINANCE"),
230 OrderSide::Buy,
231 100.into(),
232 Some(TimeInForce::Gtc),
233 Some(false),
234 Some(false),
235 None,
236 None,
237 None,
238 None,
239 );
240 assert_eq!(market_order.instrument_id(), "BTCUSDT.BINANCE".into());
242 assert_eq!(market_order.order_side(), OrderSide::Buy);
243 assert_eq!(market_order.quantity(), 100.into());
244 assert_eq!(market_order.exec_algorithm_id(), None);
248 assert_eq!(
252 market_order.client_order_id(),
253 ClientOrderId::new("O-19700101-000000-001-001-1")
254 );
255 }
257}