nautilus_derive/
factories.rs1use std::{any::Any, cell::RefCell, rc::Rc};
19
20use nautilus_common::{
21 cache::CacheView,
22 clients::{DataClient, ExecutionClient},
23 clock::Clock,
24 factories::{ClientConfig, DataClientFactory, ExecutionClientFactory},
25};
26use nautilus_live::ExecutionClientCore;
27use nautilus_model::{
28 enums::{AccountType, OmsType},
29 identifiers::{AccountId, ClientId, TraderId},
30};
31
32use crate::{
33 common::consts::{DERIVE, DERIVE_VENUE},
34 config::{DeriveDataClientConfig, DeriveExecClientConfig},
35 data::DeriveDataClient,
36 execution::DeriveExecutionClient,
37};
38
39impl ClientConfig for DeriveDataClientConfig {
40 fn as_any(&self) -> &dyn Any {
41 self
42 }
43}
44
45impl ClientConfig for DeriveExecClientConfig {
46 fn as_any(&self) -> &dyn Any {
47 self
48 }
49}
50
51#[derive(Debug, Clone)]
53#[cfg_attr(
54 feature = "python",
55 pyo3::pyclass(module = "nautilus_trader.adapters.derive", from_py_object)
56)]
57#[cfg_attr(
58 feature = "python",
59 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.derive")
60)]
61pub struct DeriveDataClientFactory;
62
63impl DeriveDataClientFactory {
64 #[must_use]
65 pub const fn new() -> Self {
66 Self
67 }
68}
69
70impl Default for DeriveDataClientFactory {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl DataClientFactory for DeriveDataClientFactory {
77 fn create(
78 &self,
79 name: &str,
80 config: &dyn ClientConfig,
81 _cache: CacheView,
82 _clock: Rc<RefCell<dyn Clock>>,
83 ) -> anyhow::Result<Box<dyn DataClient>> {
84 let derive_config = config
85 .as_any()
86 .downcast_ref::<DeriveDataClientConfig>()
87 .ok_or_else(|| {
88 anyhow::anyhow!(
89 "Invalid config type for DeriveDataClientFactory. Expected DeriveDataClientConfig, was {config:?}",
90 )
91 })?
92 .clone();
93
94 let client = DeriveDataClient::new(ClientId::from(name), derive_config)?;
95 Ok(Box::new(client))
96 }
97
98 fn name(&self) -> &'static str {
99 DERIVE
100 }
101
102 fn config_type(&self) -> &'static str {
103 stringify!(DeriveDataClientConfig)
104 }
105}
106
107#[derive(Clone, Debug)]
112#[cfg_attr(
113 feature = "python",
114 pyo3::pyclass(module = "nautilus_trader.adapters.derive", from_py_object)
115)]
116#[cfg_attr(
117 feature = "python",
118 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.derive")
119)]
120pub struct DeriveExecFactoryConfig {
121 pub trader_id: TraderId,
123 pub account_id: AccountId,
125 pub config: DeriveExecClientConfig,
127}
128
129#[cfg(feature = "python")]
130nautilus_core::impl_pyo3_config_getters!(DeriveExecFactoryConfig {
131 trader_id: TraderId,
132 account_id: AccountId,
133 config: DeriveExecClientConfig,
134});
135
136impl ClientConfig for DeriveExecFactoryConfig {
137 fn as_any(&self) -> &dyn Any {
138 self
139 }
140}
141
142#[derive(Debug, Clone)]
144#[cfg_attr(
145 feature = "python",
146 pyo3::pyclass(module = "nautilus_trader.adapters.derive", from_py_object)
147)]
148#[cfg_attr(
149 feature = "python",
150 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.derive")
151)]
152pub struct DeriveExecutionClientFactory;
153
154impl DeriveExecutionClientFactory {
155 #[must_use]
156 pub const fn new() -> Self {
157 Self
158 }
159}
160
161impl Default for DeriveExecutionClientFactory {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167impl ExecutionClientFactory for DeriveExecutionClientFactory {
168 fn create(
169 &self,
170 name: &str,
171 config: &dyn ClientConfig,
172 cache: CacheView,
173 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
174 let factory_config = config
175 .as_any()
176 .downcast_ref::<DeriveExecFactoryConfig>()
177 .ok_or_else(|| {
178 anyhow::anyhow!(
179 "Invalid config type for DeriveExecutionClientFactory. Expected DeriveExecFactoryConfig, was {config:?}",
180 )
181 })?
182 .clone();
183
184 let oms_type = OmsType::Netting;
186 let account_type = AccountType::Margin;
187
188 let core = ExecutionClientCore::new(
189 factory_config.trader_id,
190 ClientId::from(name),
191 *DERIVE_VENUE,
192 oms_type,
193 factory_config.account_id,
194 account_type,
195 None,
196 cache,
197 );
198
199 let client = DeriveExecutionClient::new(core, factory_config.config)?;
200 Ok(Box::new(client))
201 }
202
203 fn name(&self) -> &'static str {
204 DERIVE
205 }
206
207 fn config_type(&self) -> &'static str {
208 stringify!(DeriveExecFactoryConfig)
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use nautilus_common::{
215 cache::Cache, clock::TestClock, live::runner::replace_data_event_sender,
216 messages::DataEvent,
217 };
218 use rstest::rstest;
219
220 use super::*;
221
222 #[derive(Debug)]
223 struct WrongConfig;
224
225 impl ClientConfig for WrongConfig {
226 fn as_any(&self) -> &dyn Any {
227 self
228 }
229 }
230
231 #[rstest]
232 fn test_data_client_factory_metadata() {
233 let factory = DeriveDataClientFactory::new();
234
235 assert_eq!(factory.name(), DERIVE);
236 assert_eq!(factory.config_type(), "DeriveDataClientConfig");
237 }
238
239 #[rstest]
240 fn test_data_client_factory_creates_client() {
241 let factory = DeriveDataClientFactory::new();
242 let cache = Rc::new(RefCell::new(Cache::default()));
243 let clock = Rc::new(RefCell::new(TestClock::new()));
244 let config = DeriveDataClientConfig::default();
245 let (tx, _rx) = tokio::sync::mpsc::unbounded_channel::<DataEvent>();
246 replace_data_event_sender(tx);
247
248 let client = factory
249 .create(DERIVE, &config, cache.into(), clock)
250 .expect("factory creates data client");
251
252 assert_eq!(client.client_id(), ClientId::from(DERIVE));
253 assert_eq!(client.venue(), Some(*DERIVE_VENUE));
254 }
255
256 #[rstest]
257 fn test_data_client_factory_rejects_wrong_config_type() {
258 let factory = DeriveDataClientFactory::new();
259 let cache = Rc::new(RefCell::new(Cache::default()));
260 let clock = Rc::new(RefCell::new(TestClock::new()));
261 let wrong_config = WrongConfig;
262
263 let result = factory.create(DERIVE, &wrong_config, cache.into(), clock);
264
265 assert!(result.is_err());
266 assert!(
267 result
268 .err()
269 .unwrap()
270 .to_string()
271 .contains("Invalid config type")
272 );
273 }
274
275 #[rstest]
276 fn test_exec_client_factory_metadata() {
277 let factory = DeriveExecutionClientFactory::new();
278
279 assert_eq!(factory.name(), DERIVE);
280 assert_eq!(factory.config_type(), "DeriveExecFactoryConfig");
281 }
282
283 #[rstest]
284 fn test_exec_client_factory_rejects_wrong_config_type() {
285 let factory = DeriveExecutionClientFactory::new();
286 let cache = Rc::new(RefCell::new(Cache::default()));
287 let wrong_config = DeriveDataClientConfig::default();
288
289 let result = factory.create(DERIVE, &wrong_config, cache.into());
290
291 assert!(result.is_err());
292 assert!(
293 result
294 .err()
295 .unwrap()
296 .to_string()
297 .contains("Invalid config type")
298 );
299 }
300
301 #[rstest]
302 fn test_exec_factory_config_implements_client_config() {
303 let factory_config = DeriveExecFactoryConfig {
304 trader_id: TraderId::from("TRADER-001"),
305 account_id: AccountId::from("DERIVE-001"),
306 config: DeriveExecClientConfig::default(),
307 };
308
309 let boxed: Box<dyn ClientConfig> = Box::new(factory_config);
310 assert!(
311 boxed
312 .as_any()
313 .downcast_ref::<DeriveExecFactoryConfig>()
314 .is_some()
315 );
316 }
317}