nautilus_kraken/
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::KRAKEN_VENUE, enums::KrakenProductType},
34 config::{KrakenDataClientConfig, KrakenExecClientConfig},
35 data::{KrakenFuturesDataClient, KrakenSpotDataClient},
36 execution::{KrakenFuturesExecutionClient, KrakenSpotExecutionClient},
37};
38
39impl ClientConfig for KrakenDataClientConfig {
40 fn as_any(&self) -> &dyn Any {
41 self
42 }
43}
44
45#[derive(Debug, Clone)]
47#[cfg_attr(
48 feature = "python",
49 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.kraken", from_py_object)
50)]
51#[cfg_attr(
52 feature = "python",
53 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.kraken")
54)]
55pub struct KrakenDataClientFactory;
56
57impl KrakenDataClientFactory {
58 #[must_use]
60 pub const fn new() -> Self {
61 Self
62 }
63}
64
65impl Default for KrakenDataClientFactory {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl DataClientFactory for KrakenDataClientFactory {
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 kraken_config = config
80 .as_any()
81 .downcast_ref::<KrakenDataClientConfig>()
82 .ok_or_else(|| {
83 anyhow::anyhow!(
84 "Invalid config type for KrakenDataClientFactory. Expected KrakenDataClientConfig, was {config:?}",
85 )
86 })?
87 .clone();
88
89 let client_id = ClientId::from(name);
90
91 match kraken_config.product_type {
92 KrakenProductType::Spot => {
93 let client = KrakenSpotDataClient::new(client_id, kraken_config)?;
94 Ok(Box::new(client))
95 }
96 KrakenProductType::Futures => {
97 let client = KrakenFuturesDataClient::new(client_id, kraken_config)?;
98 Ok(Box::new(client))
99 }
100 }
101 }
102
103 fn name(&self) -> &'static str {
104 "KRAKEN"
105 }
106
107 fn config_type(&self) -> &'static str {
108 "KrakenDataClientConfig"
109 }
110}
111
112impl ClientConfig for KrakenExecClientConfig {
113 fn as_any(&self) -> &dyn Any {
114 self
115 }
116}
117
118#[derive(Debug, Clone)]
120#[cfg_attr(
121 feature = "python",
122 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.kraken", from_py_object)
123)]
124#[cfg_attr(
125 feature = "python",
126 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.kraken")
127)]
128pub struct KrakenExecutionClientFactory;
129
130impl KrakenExecutionClientFactory {
131 #[must_use]
133 pub const fn new() -> Self {
134 Self
135 }
136}
137
138impl Default for KrakenExecutionClientFactory {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144impl ExecutionClientFactory for KrakenExecutionClientFactory {
145 fn create(
146 &self,
147 name: &str,
148 config: &dyn ClientConfig,
149 cache: Rc<RefCell<Cache>>,
150 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
151 let kraken_config = config
152 .as_any()
153 .downcast_ref::<KrakenExecClientConfig>()
154 .ok_or_else(|| {
155 anyhow::anyhow!(
156 "Invalid config type for KrakenExecutionClientFactory. Expected KrakenExecClientConfig, was {config:?}",
157 )
158 })?
159 .clone();
160
161 let oms_type = OmsType::Netting;
162 let account_type = match kraken_config.product_type {
163 KrakenProductType::Spot => AccountType::Cash,
164 KrakenProductType::Futures => AccountType::Margin,
165 };
166
167 let client_id = ClientId::from(name);
168 let core = ExecutionClientCore::new(
169 kraken_config.trader_id,
170 client_id,
171 *KRAKEN_VENUE,
172 oms_type,
173 kraken_config.account_id,
174 account_type,
175 None, cache,
177 );
178
179 match kraken_config.product_type {
180 KrakenProductType::Spot => {
181 let client = KrakenSpotExecutionClient::new(core, kraken_config)?;
182 Ok(Box::new(client))
183 }
184 KrakenProductType::Futures => {
185 let client = KrakenFuturesExecutionClient::new(core, kraken_config)?;
186 Ok(Box::new(client))
187 }
188 }
189 }
190
191 fn name(&self) -> &'static str {
192 "KRAKEN"
193 }
194
195 fn config_type(&self) -> &'static str {
196 "KrakenExecClientConfig"
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use std::{cell::RefCell, rc::Rc};
203
204 use nautilus_common::{
205 cache::Cache,
206 clock::TestClock,
207 factories::{ClientConfig, DataClientFactory, ExecutionClientFactory},
208 live::runner::set_data_event_sender,
209 messages::DataEvent,
210 };
211 use rstest::rstest;
212
213 use super::*;
214 use crate::common::enums::KrakenProductType;
215
216 fn setup_test_env() {
217 let (sender, _receiver) = tokio::sync::mpsc::unbounded_channel::<DataEvent>();
218 set_data_event_sender(sender);
219 }
220
221 #[rstest]
222 fn test_kraken_data_client_factory_creation() {
223 let factory = KrakenDataClientFactory::new();
224 assert_eq!(factory.name(), "KRAKEN");
225 assert_eq!(factory.config_type(), "KrakenDataClientConfig");
226 }
227
228 #[rstest]
229 fn test_kraken_data_client_factory_default() {
230 let factory = KrakenDataClientFactory::new();
231 assert_eq!(factory.name(), "KRAKEN");
232 }
233
234 #[rstest]
235 fn test_kraken_data_client_config_implements_client_config() {
236 let config = KrakenDataClientConfig {
237 product_type: KrakenProductType::Spot,
238 ..Default::default()
239 };
240
241 let boxed_config: Box<dyn ClientConfig> = Box::new(config);
242 let downcasted = boxed_config
243 .as_any()
244 .downcast_ref::<KrakenDataClientConfig>();
245
246 assert!(downcasted.is_some());
247 }
248
249 #[rstest]
250 fn test_kraken_data_client_factory_creates_client() {
251 setup_test_env();
252
253 let factory = KrakenDataClientFactory::new();
254 let config = KrakenDataClientConfig {
255 product_type: KrakenProductType::Spot,
256 ..Default::default()
257 };
258
259 let cache = Rc::new(RefCell::new(Cache::default()));
260 let clock = Rc::new(RefCell::new(TestClock::new()));
261
262 let result = factory.create("KRAKEN-TEST", &config, cache, clock);
263 assert!(result.is_ok());
264
265 let client = result.unwrap();
266 assert_eq!(client.client_id(), ClientId::from("KRAKEN-TEST"));
267 }
268
269 #[rstest]
270 fn test_kraken_execution_client_factory_creates_spot_client_with_netting_oms() {
271 let factory = KrakenExecutionClientFactory::new();
272 let config = KrakenExecClientConfig {
273 product_type: KrakenProductType::Spot,
274 ..Default::default()
275 };
276 let cache = Rc::new(RefCell::new(Cache::default()));
277
278 let result = factory.create("KRAKEN-TEST", &config, cache);
279 assert!(result.is_ok());
280
281 let client = result.unwrap();
282 assert_eq!(client.client_id(), ClientId::from("KRAKEN-TEST"));
283 assert_eq!(client.account_id(), config.account_id);
284 assert_eq!(client.oms_type(), OmsType::Netting);
285 }
286
287 #[rstest]
288 fn test_kraken_execution_client_factory_creates_futures_client_with_netting_oms() {
289 let factory = KrakenExecutionClientFactory::new();
290 let config = KrakenExecClientConfig {
291 product_type: KrakenProductType::Futures,
292 ..Default::default()
293 };
294 let cache = Rc::new(RefCell::new(Cache::default()));
295
296 let result = factory.create("KRAKEN-TEST", &config, cache);
297 assert!(result.is_ok());
298
299 let client = result.unwrap();
300 assert_eq!(client.client_id(), ClientId::from("KRAKEN-TEST"));
301 assert_eq!(client.account_id(), config.account_id);
302 assert_eq!(client.oms_type(), OmsType::Netting);
303 }
304}