nautilus_architect_ax/
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::ClientId,
30};
31
32use crate::{
33 common::{
34 consts::{AX, AX_VENUE},
35 credential::Credential,
36 },
37 config::{AxDataClientConfig, AxExecClientConfig},
38 data::AxDataClient,
39 execution::AxExecutionClient,
40 http::client::AxHttpClient,
41 websocket::data::AxMdWebSocketClient,
42};
43
44impl ClientConfig for AxDataClientConfig {
45 fn as_any(&self) -> &dyn Any {
46 self
47 }
48}
49
50impl ClientConfig for AxExecClientConfig {
51 fn as_any(&self) -> &dyn Any {
52 self
53 }
54}
55
56#[derive(Debug)]
58pub struct AxDataClientFactory;
59
60impl AxDataClientFactory {
61 #[must_use]
63 pub const fn new() -> Self {
64 Self
65 }
66}
67
68impl Default for AxDataClientFactory {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl DataClientFactory for AxDataClientFactory {
75 fn create(
76 &self,
77 name: &str,
78 config: &dyn ClientConfig,
79 _cache: CacheView,
80 _clock: Rc<RefCell<dyn Clock>>,
81 ) -> anyhow::Result<Box<dyn DataClient>> {
82 let ax_config = config
83 .as_any()
84 .downcast_ref::<AxDataClientConfig>()
85 .ok_or_else(|| {
86 anyhow::anyhow!(
87 "Invalid config type for AxDataClientFactory. Expected AxDataClientConfig, was {config:?}",
88 )
89 })?
90 .clone();
91
92 let client_id = ClientId::from(name);
93
94 let http_client = if ax_config.has_api_credentials() {
95 let credential =
96 Credential::resolve(ax_config.api_key.clone(), ax_config.api_secret.clone())
97 .ok_or_else(|| anyhow::anyhow!("API credentials not configured"))?;
98
99 AxHttpClient::with_credentials(
100 credential.api_key().to_string(),
101 credential.api_secret().to_string(),
102 Some(ax_config.http_base_url()),
103 None, ax_config.http_timeout_secs,
105 ax_config.max_retries,
106 ax_config.retry_delay_initial_ms,
107 ax_config.retry_delay_max_ms,
108 ax_config.proxy_url.clone(),
109 )
110 .map_err(|e| anyhow::anyhow!("Failed to create HTTP client: {e}"))?
111 } else {
112 AxHttpClient::new(
113 Some(ax_config.http_base_url()),
114 None, ax_config.http_timeout_secs,
116 ax_config.max_retries,
117 ax_config.retry_delay_initial_ms,
118 ax_config.retry_delay_max_ms,
119 ax_config.proxy_url.clone(),
120 )
121 .map_err(|e| anyhow::anyhow!("Failed to create HTTP client: {e}"))?
122 };
123
124 let ws_url = ax_config.ws_public_url();
125
126 let ws_client = AxMdWebSocketClient::without_auth(
128 ws_url,
129 ax_config.heartbeat_interval_secs,
130 ax_config.transport_backend,
131 ax_config.proxy_url.clone(),
132 );
133
134 let client = AxDataClient::new(client_id, ax_config, http_client, ws_client)?;
135 Ok(Box::new(client))
136 }
137
138 fn name(&self) -> &'static str {
139 AX
140 }
141
142 fn config_type(&self) -> &'static str {
143 "AxDataClientConfig"
144 }
145}
146
147#[derive(Debug)]
149pub struct AxExecutionClientFactory;
150
151impl AxExecutionClientFactory {
152 #[must_use]
154 pub const fn new() -> Self {
155 Self
156 }
157}
158
159impl Default for AxExecutionClientFactory {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165impl ExecutionClientFactory for AxExecutionClientFactory {
166 fn create(
167 &self,
168 name: &str,
169 config: &dyn ClientConfig,
170 cache: CacheView,
171 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
172 let ax_config = config
173 .as_any()
174 .downcast_ref::<AxExecClientConfig>()
175 .ok_or_else(|| {
176 anyhow::anyhow!(
177 "Invalid config type for AxExecutionClientFactory. Expected AxExecClientConfig, was {config:?}",
178 )
179 })?
180 .clone();
181
182 let oms_type = OmsType::Netting;
184 let account_type = AccountType::Margin;
185
186 let core = ExecutionClientCore::new(
187 ax_config.trader_id,
188 ClientId::from(name),
189 *AX_VENUE,
190 oms_type,
191 ax_config.account_id,
192 account_type,
193 None, cache,
195 );
196
197 let client = AxExecutionClient::new(core, ax_config)?;
198
199 Ok(Box::new(client))
200 }
201
202 fn name(&self) -> &'static str {
203 AX
204 }
205
206 fn config_type(&self) -> &'static str {
207 "AxExecClientConfig"
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use nautilus_common::factories::ClientConfig;
214 use rstest::rstest;
215
216 use super::*;
217 use crate::config::AxDataClientConfig;
218
219 #[rstest]
220 fn test_ax_data_client_config_implements_client_config() {
221 let config = AxDataClientConfig::default();
222
223 let boxed_config: Box<dyn ClientConfig> = Box::new(config);
224 let downcasted = boxed_config.as_any().downcast_ref::<AxDataClientConfig>();
225
226 assert!(downcasted.is_some());
227 }
228
229 #[rstest]
230 fn test_ax_data_client_factory_creation() {
231 let factory = AxDataClientFactory::new();
232 assert_eq!(factory.name(), AX);
233 assert_eq!(factory.config_type(), "AxDataClientConfig");
234 }
235
236 #[rstest]
237 fn test_ax_data_client_factory_default() {
238 let factory = AxDataClientFactory;
239 assert_eq!(factory.name(), AX);
240 }
241}