nautilus_common/
factories.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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    /// Sets the client order ID generator count.
75    pub const fn set_client_order_id_count(&mut self, count: usize) {
76        self.order_id_generator.set_count(count);
77    }
78
79    /// Sets the order list ID generator count.
80    pub const fn set_order_list_id_count(&mut self, count: usize) {
81        self.order_list_id_generator.set_count(count);
82    }
83
84    /// Generates a new client order ID.
85    pub fn generate_client_order_id(&mut self) -> ClientOrderId {
86        self.order_id_generator.generate()
87    }
88
89    /// Generates a new order list ID.
90    pub fn generate_order_list_id(&mut self) -> OrderListId {
91        self.order_list_id_generator.generate()
92    }
93
94    /// Resets the factory by resetting all ID generators.
95    pub const fn reset_factory(&mut self) {
96        self.order_id_generator.reset();
97        self.order_list_id_generator.reset();
98    }
99
100    /// Creates a new market order.
101    #[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////////////////////////////////////////////////////////////////////////////////
147// Tests
148////////////////////////////////////////////////////////////////////////////////
149#[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        // TODO: Add additional polymorphic getters
247        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.time_in_force(), TimeInForce::Gtc);
251        // assert!(!market_order.is_reduce_only);
252        // assert!(!market_order.is_quote_quantity);
253        assert_eq!(market_order.exec_algorithm_id(), None);
254        // assert_eq!(market_order.exec_algorithm_params(), None);
255        // assert_eq!(market_order.exec_spawn_id, None);
256        // assert_eq!(market_order.tags, None);
257        assert_eq!(
258            market_order.client_order_id(),
259            ClientOrderId::new("O-19700101-000000-001-001-1")
260        );
261        // assert_eq!(market_order.order_list_id(), None);
262    }
263}