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            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        // TODO: Add additional polymorphic getters
241        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.time_in_force(), TimeInForce::Gtc);
245        // assert!(!market_order.is_reduce_only);
246        // assert!(!market_order.is_quote_quantity);
247        assert_eq!(market_order.exec_algorithm_id(), None);
248        // assert_eq!(market_order.exec_algorithm_params(), None);
249        // assert_eq!(market_order.exec_spawn_id, None);
250        // assert_eq!(market_order.tags, None);
251        assert_eq!(
252            market_order.client_order_id(),
253            ClientOrderId::new("O-19700101-000000-001-001-1")
254        );
255        // assert_eq!(market_order.order_list_id(), None);
256    }
257}