Skip to main content

nautilus_common/factories/
client.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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//! Client factory traits and registries shared by live, sandbox, and backtest runtimes.
17//!
18//! The traits in this module describe how to construct adapter [`DataClient`] and
19//! [`ExecutionClient`] instances from a configuration object, without coupling the
20//! definition site to any particular runtime (tokio, native threads, simulated time).
21//! Adapters implement these traits in their own crates; the live system kernel and
22//! any future backtest registry consume them via the registries below.
23
24use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc};
25
26use ahash::AHashMap;
27use nautilus_model::identifiers::TraderId;
28
29use crate::{
30    cache::{Cache, CacheView},
31    clients::{DataClient, ExecutionClient},
32    clock::Clock,
33};
34
35/// Configuration for creating client instances.
36///
37/// This trait allows different client types to provide their configuration
38/// in a type-safe manner while still being usable in generic factory contexts.
39pub trait ClientConfig: Debug {
40    /// Return the configuration as a trait object.
41    fn as_any(&self) -> &dyn Any;
42}
43
44/// Factory trait for creating data client instances.
45///
46/// Implementations of this trait should create specific data client types
47/// (e.g., Binance, Bybit, Databento) based on the provided configuration.
48pub trait DataClientFactory: Debug {
49    /// Create a new data client instance.
50    ///
51    /// Data clients receive a read-only cache view so adapters can query platform state during
52    /// construction without mutating the cache.
53    ///
54    /// # Errors
55    ///
56    /// Returns an error if client creation fails.
57    fn create(
58        &self,
59        name: &str,
60        config: &dyn ClientConfig,
61        cache: CacheView,
62        clock: Rc<RefCell<dyn Clock>>,
63    ) -> anyhow::Result<Box<dyn DataClient>>;
64
65    /// Returns the name of this factory.
66    fn name(&self) -> &str;
67
68    /// Returns the supported configuration type name for this factory.
69    fn config_type(&self) -> &str;
70}
71
72/// Factory trait for creating execution client instances.
73///
74/// Implementations of this trait should create specific execution client types
75/// (e.g., Binance, Bybit, Interactive Brokers) based on the provided configuration.
76pub trait ExecutionClientFactory: Debug {
77    /// Create a new execution client instance.
78    ///
79    /// Execution clients receive a read-only cache view so venue adapters can query platform state
80    /// during construction without mutating the cache. The trader ID comes from the owning node.
81    ///
82    /// # Errors
83    ///
84    /// Returns an error if client creation fails.
85    fn create(
86        &self,
87        trader_id: TraderId,
88        name: &str,
89        config: &dyn ClientConfig,
90        cache: CacheView,
91    ) -> anyhow::Result<Box<dyn ExecutionClient>>;
92
93    /// Returns the name of this factory.
94    fn name(&self) -> &str;
95
96    /// Returns the supported configuration type name for this factory.
97    fn config_type(&self) -> &str;
98}
99
100/// Factory trait for simulated execution clients owned by the sync core.
101///
102/// Simulated execution clients may need the mutable cache handle because they host the matching
103/// engine and therefore own cache updates that a live venue adapter must not perform.
104pub trait SimulatedExecutionClientFactory: Debug {
105    /// Create a new simulated execution client instance.
106    ///
107    /// # Errors
108    ///
109    /// Returns an error if client creation fails.
110    fn create(
111        &self,
112        trader_id: TraderId,
113        name: &str,
114        config: &dyn ClientConfig,
115        cache: Rc<RefCell<Cache>>,
116    ) -> anyhow::Result<Box<dyn ExecutionClient>>;
117
118    /// Returns the name of this factory.
119    fn name(&self) -> &str;
120
121    /// Returns the supported configuration type name for this factory.
122    fn config_type(&self) -> &str;
123}
124
125/// Registry for managing data client factories.
126///
127/// Allows dynamic registration and lookup of factories by name,
128/// enabling a plugin-like architecture for different data providers.
129#[derive(Debug, Default)]
130pub struct DataClientFactoryRegistry {
131    factories: AHashMap<String, Box<dyn DataClientFactory>>,
132}
133
134impl DataClientFactoryRegistry {
135    /// Creates a new empty registry.
136    #[must_use]
137    pub fn new() -> Self {
138        Self {
139            factories: AHashMap::new(),
140        }
141    }
142
143    /// Registers a data client factory.
144    ///
145    /// # Errors
146    ///
147    /// Returns an error if a factory with the same name is already registered.
148    pub fn register(
149        &mut self,
150        name: String,
151        factory: Box<dyn DataClientFactory>,
152    ) -> anyhow::Result<()> {
153        if self.factories.contains_key(&name) {
154            anyhow::bail!("Data client factory '{name}' is already registered");
155        }
156
157        self.factories.insert(name, factory);
158        Ok(())
159    }
160
161    /// Gets a registered factory by name.
162    ///
163    /// # Returns
164    ///
165    /// The factory if found, None otherwise.
166    #[must_use]
167    pub fn get(&self, name: &str) -> Option<&dyn DataClientFactory> {
168        self.factories.get(name).map(std::convert::AsRef::as_ref)
169    }
170
171    /// Gets a list of all registered factory names.
172    #[must_use]
173    pub fn names(&self) -> Vec<&String> {
174        self.factories.keys().collect()
175    }
176
177    /// Checks if a factory is registered.
178    #[must_use]
179    pub fn contains(&self, name: &str) -> bool {
180        self.factories.contains_key(name)
181    }
182}
183
184/// Registry for managing execution client factories.
185///
186/// Allows dynamic registration and lookup of factories by name,
187/// enabling a plugin-like architecture for different execution providers.
188#[derive(Debug, Default)]
189pub struct ExecutionClientFactoryRegistry {
190    factories: AHashMap<String, Box<dyn ExecutionClientFactory>>,
191}
192
193impl ExecutionClientFactoryRegistry {
194    /// Creates a new empty registry.
195    #[must_use]
196    pub fn new() -> Self {
197        Self {
198            factories: AHashMap::new(),
199        }
200    }
201
202    /// Registers an execution client factory.
203    ///
204    /// # Errors
205    ///
206    /// Returns an error if a factory with the same name is already registered.
207    pub fn register(
208        &mut self,
209        name: String,
210        factory: Box<dyn ExecutionClientFactory>,
211    ) -> anyhow::Result<()> {
212        if self.factories.contains_key(&name) {
213            anyhow::bail!("Execution client factory '{name}' is already registered");
214        }
215
216        self.factories.insert(name, factory);
217        Ok(())
218    }
219
220    /// Gets a registered factory by name (if found).
221    #[must_use]
222    pub fn get(&self, name: &str) -> Option<&dyn ExecutionClientFactory> {
223        self.factories.get(name).map(std::convert::AsRef::as_ref)
224    }
225
226    /// Gets a list of all registered factory names.
227    #[must_use]
228    pub fn names(&self) -> Vec<&String> {
229        self.factories.keys().collect()
230    }
231
232    /// Checks if a factory is registered.
233    #[must_use]
234    pub fn contains(&self, name: &str) -> bool {
235        self.factories.contains_key(name)
236    }
237}
238
239#[allow(dead_code)]
240#[cfg(test)]
241mod tests {
242    use std::any::Any;
243
244    use rstest::*;
245
246    use super::*;
247
248    #[derive(Debug)]
249    struct MockConfig {
250        #[allow(dead_code)]
251        value: String,
252    }
253
254    impl ClientConfig for MockConfig {
255        fn as_any(&self) -> &dyn Any {
256            self
257        }
258    }
259
260    #[derive(Debug)]
261    struct MockDataClientFactory;
262
263    impl DataClientFactory for MockDataClientFactory {
264        fn create(
265            &self,
266            _name: &str,
267            _config: &dyn ClientConfig,
268            _cache: CacheView,
269            _clock: Rc<RefCell<dyn Clock>>,
270        ) -> anyhow::Result<Box<dyn DataClient>> {
271            Err(anyhow::anyhow!("Mock factory - not implemented"))
272        }
273
274        fn name(&self) -> &'static str {
275            "mock"
276        }
277
278        fn config_type(&self) -> &'static str {
279            "MockConfig"
280        }
281    }
282
283    #[rstest]
284    fn test_data_client_factory_registry() {
285        let mut registry = DataClientFactoryRegistry::new();
286
287        assert!(registry.names().is_empty());
288        assert!(!registry.contains("mock"));
289        assert!(registry.get("mock").is_none());
290
291        let factory = Box::new(MockDataClientFactory);
292        registry.register("mock".to_string(), factory).unwrap();
293
294        assert_eq!(registry.names().len(), 1);
295        assert!(registry.contains("mock"));
296        assert!(registry.get("mock").is_some());
297
298        let factory2 = Box::new(MockDataClientFactory);
299        let result = registry.register("mock".to_string(), factory2);
300        assert!(result.is_err());
301    }
302
303    #[rstest]
304    fn test_empty_data_client_factory_registry() {
305        let registry = DataClientFactoryRegistry::new();
306
307        assert!(registry.names().is_empty());
308        assert!(!registry.contains("mock"));
309        assert!(registry.get("mock").is_none());
310    }
311
312    #[rstest]
313    fn test_empty_execution_client_factory_registry() {
314        let registry = ExecutionClientFactoryRegistry::new();
315
316        assert!(registry.names().is_empty());
317        assert!(!registry.contains("mock"));
318        assert!(registry.get("mock").is_none());
319    }
320}