nautilus_common/generators/
client_order_id.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
16use nautilus_core::AtomicTime;
17use nautilus_model::identifiers::{ClientOrderId, StrategyId, TraderId};
18
19use super::get_datetime_tag;
20
21#[repr(C)]
22#[derive(Debug)]
23pub struct ClientOrderIdGenerator {
24    clock: &'static AtomicTime,
25    trader_id: TraderId,
26    strategy_id: StrategyId,
27    count: usize,
28}
29
30impl ClientOrderIdGenerator {
31    /// Creates a new [`ClientOrderIdGenerator`] instance.
32    #[must_use]
33    pub const fn new(
34        trader_id: TraderId,
35        strategy_id: StrategyId,
36        initial_count: usize,
37        clock: &'static AtomicTime,
38    ) -> Self {
39        Self {
40            trader_id,
41            strategy_id,
42            count: initial_count,
43            clock,
44        }
45    }
46
47    pub const fn set_count(&mut self, count: usize) {
48        self.count = count;
49    }
50
51    pub const fn reset(&mut self) {
52        self.count = 0;
53    }
54
55    #[must_use]
56    pub const fn count(&self) -> usize {
57        self.count
58    }
59
60    pub fn generate(&mut self) -> ClientOrderId {
61        let datetime_tag = get_datetime_tag(self.clock.get_time_ms());
62        let trader_tag = self.trader_id.get_tag();
63        let strategy_tag = self.strategy_id.get_tag();
64        self.count += 1;
65        let value = format!(
66            "O-{}-{}-{}-{}",
67            datetime_tag, trader_tag, strategy_tag, self.count
68        );
69        ClientOrderId::from(value)
70    }
71}
72
73////////////////////////////////////////////////////////////////////////////////
74// Tests
75////////////////////////////////////////////////////////////////////////////////
76#[cfg(test)]
77mod tests {
78    use nautilus_core::time::get_atomic_clock_static;
79    use nautilus_model::identifiers::{ClientOrderId, StrategyId, TraderId};
80    use rstest::rstest;
81
82    use crate::generators::client_order_id::ClientOrderIdGenerator;
83
84    fn get_client_order_id_generator(initial_count: Option<usize>) -> ClientOrderIdGenerator {
85        ClientOrderIdGenerator::new(
86            TraderId::default(),
87            StrategyId::default(),
88            initial_count.unwrap_or(0),
89            get_atomic_clock_static(),
90        )
91    }
92
93    #[rstest]
94    fn test_init() {
95        let generator = get_client_order_id_generator(None);
96        assert_eq!(generator.count(), 0);
97    }
98
99    #[rstest]
100    fn test_init_with_initial_count() {
101        let generator = get_client_order_id_generator(Some(7));
102        assert_eq!(generator.count(), 7);
103    }
104
105    #[rstest]
106    fn test_generate_client_order_id_from_start() {
107        let mut generator = get_client_order_id_generator(None);
108        let result1 = generator.generate();
109        let result2 = generator.generate();
110        let result3 = generator.generate();
111
112        assert_eq!(result1, ClientOrderId::new("O-19700101-000000-001-001-1"));
113        assert_eq!(result2, ClientOrderId::new("O-19700101-000000-001-001-2"));
114        assert_eq!(result3, ClientOrderId::new("O-19700101-000000-001-001-3"));
115    }
116
117    #[rstest]
118    fn test_generate_client_order_id_from_initial() {
119        let mut generator = get_client_order_id_generator(Some(5));
120        let result1 = generator.generate();
121        let result2 = generator.generate();
122        let result3 = generator.generate();
123
124        assert_eq!(result1, ClientOrderId::new("O-19700101-000000-001-001-6"));
125        assert_eq!(result2, ClientOrderId::new("O-19700101-000000-001-001-7"));
126        assert_eq!(result3, ClientOrderId::new("O-19700101-000000-001-001-8"));
127    }
128
129    #[rstest]
130    fn test_reset() {
131        let mut generator = get_client_order_id_generator(None);
132        generator.generate();
133        generator.generate();
134        generator.reset();
135        let result = generator.generate();
136
137        assert_eq!(result, ClientOrderId::new("O-19700101-000000-001-001-1"));
138    }
139}