nautilus_common/
factories.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Factories for constructing domain objects such as orders.
17
18use 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    /// Creates a new [`OrderFactory`] instance.
46    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////////////////////////////////////////////////////////////////////////////////
141// Tests
142////////////////////////////////////////////////////////////////////////////////
143#[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        // TODO: Add additional polymorphic getters
240        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.time_in_force(), TimeInForce::Gtc);
244        // assert!(!market_order.is_reduce_only);
245        // assert!(!market_order.is_quote_quantity);
246        assert_eq!(market_order.exec_algorithm_id(), None);
247        // assert_eq!(market_order.exec_algorithm_params(), None);
248        // assert_eq!(market_order.exec_spawn_id, None);
249        // assert_eq!(market_order.tags, None);
250        assert_eq!(
251            market_order.client_order_id(),
252            ClientOrderId::new("O-19700101-000000-001-001-1")
253        );
254        // assert_eq!(market_order.order_list_id(), None);
255    }
256}