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