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};
25use nautilus_live::ExecutionClientCore;
26use nautilus_model::{
27 enums::{AccountType, OmsType},
28 identifiers::ClientId,
29};
30use nautilus_system::factories::{ClientConfig, DataClientFactory, ExecutionClientFactory};
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)]
47pub struct KrakenDataClientFactory;
48
49impl KrakenDataClientFactory {
50 #[must_use]
52 pub const fn new() -> Self {
53 Self
54 }
55}
56
57impl Default for KrakenDataClientFactory {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63impl DataClientFactory for KrakenDataClientFactory {
64 fn create(
65 &self,
66 name: &str,
67 config: &dyn ClientConfig,
68 _cache: Rc<RefCell<Cache>>,
69 _clock: Rc<RefCell<dyn Clock>>,
70 ) -> anyhow::Result<Box<dyn DataClient>> {
71 let kraken_config = config
72 .as_any()
73 .downcast_ref::<KrakenDataClientConfig>()
74 .ok_or_else(|| {
75 anyhow::anyhow!(
76 "Invalid config type for KrakenDataClientFactory. Expected KrakenDataClientConfig, was {config:?}",
77 )
78 })?
79 .clone();
80
81 let client_id = ClientId::from(name);
82 match kraken_config.product_type {
83 KrakenProductType::Spot => {
84 let client = KrakenSpotDataClient::new(client_id, kraken_config)?;
85 Ok(Box::new(client))
86 }
87 KrakenProductType::Futures => {
88 let client = KrakenFuturesDataClient::new(client_id, kraken_config)?;
89 Ok(Box::new(client))
90 }
91 }
92 }
93
94 fn name(&self) -> &'static str {
95 "KRAKEN"
96 }
97
98 fn config_type(&self) -> &'static str {
99 "KrakenDataClientConfig"
100 }
101}
102
103impl ClientConfig for KrakenExecClientConfig {
104 fn as_any(&self) -> &dyn Any {
105 self
106 }
107}
108
109#[derive(Debug)]
111pub struct KrakenExecutionClientFactory;
112
113impl KrakenExecutionClientFactory {
114 #[must_use]
116 pub const fn new() -> Self {
117 Self
118 }
119}
120
121impl Default for KrakenExecutionClientFactory {
122 fn default() -> Self {
123 Self::new()
124 }
125}
126
127impl ExecutionClientFactory for KrakenExecutionClientFactory {
128 fn create(
129 &self,
130 name: &str,
131 config: &dyn ClientConfig,
132 cache: Rc<RefCell<Cache>>,
133 ) -> anyhow::Result<Box<dyn ExecutionClient>> {
134 let kraken_config = config
135 .as_any()
136 .downcast_ref::<KrakenExecClientConfig>()
137 .ok_or_else(|| {
138 anyhow::anyhow!(
139 "Invalid config type for KrakenExecutionClientFactory. Expected KrakenExecClientConfig, was {config:?}",
140 )
141 })?
142 .clone();
143
144 let oms_type = match kraken_config.product_type {
146 KrakenProductType::Spot => OmsType::Hedging,
147 KrakenProductType::Futures => OmsType::Netting,
148 };
149 let account_type = AccountType::Margin;
150
151 let client_id = ClientId::from(name);
152 let core = ExecutionClientCore::new(
153 kraken_config.trader_id,
154 client_id,
155 *KRAKEN_VENUE,
156 oms_type,
157 kraken_config.account_id,
158 account_type,
159 None, cache,
161 );
162
163 match kraken_config.product_type {
164 KrakenProductType::Spot => {
165 let client = KrakenSpotExecutionClient::new(core, kraken_config)?;
166 Ok(Box::new(client))
167 }
168 KrakenProductType::Futures => {
169 let client = KrakenFuturesExecutionClient::new(core, kraken_config)?;
170 Ok(Box::new(client))
171 }
172 }
173 }
174
175 fn name(&self) -> &'static str {
176 "KRAKEN"
177 }
178
179 fn config_type(&self) -> &'static str {
180 "KrakenExecClientConfig"
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use std::{cell::RefCell, rc::Rc};
187
188 use nautilus_common::{
189 cache::Cache, clock::TestClock, live::runner::set_data_event_sender, messages::DataEvent,
190 };
191 use nautilus_system::factories::{ClientConfig, DataClientFactory};
192 use rstest::rstest;
193
194 use super::*;
195 use crate::common::enums::KrakenProductType;
196
197 fn setup_test_env() {
198 let (sender, _receiver) = tokio::sync::mpsc::unbounded_channel::<DataEvent>();
199 set_data_event_sender(sender);
200 }
201
202 #[rstest]
203 fn test_kraken_data_client_factory_creation() {
204 let factory = KrakenDataClientFactory::new();
205 assert_eq!(factory.name(), "KRAKEN");
206 assert_eq!(factory.config_type(), "KrakenDataClientConfig");
207 }
208
209 #[rstest]
210 fn test_kraken_data_client_factory_default() {
211 let factory = KrakenDataClientFactory::new();
212 assert_eq!(factory.name(), "KRAKEN");
213 }
214
215 #[rstest]
216 fn test_kraken_data_client_config_implements_client_config() {
217 let config = KrakenDataClientConfig {
218 product_type: KrakenProductType::Spot,
219 ..Default::default()
220 };
221
222 let boxed_config: Box<dyn ClientConfig> = Box::new(config);
223 let downcasted = boxed_config
224 .as_any()
225 .downcast_ref::<KrakenDataClientConfig>();
226
227 assert!(downcasted.is_some());
228 }
229
230 #[rstest]
231 fn test_kraken_data_client_factory_creates_client() {
232 setup_test_env();
233
234 let factory = KrakenDataClientFactory::new();
235 let config = KrakenDataClientConfig {
236 product_type: KrakenProductType::Spot,
237 ..Default::default()
238 };
239
240 let cache = Rc::new(RefCell::new(Cache::default()));
241 let clock = Rc::new(RefCell::new(TestClock::new()));
242
243 let result = factory.create("KRAKEN-TEST", &config, cache, clock);
244 assert!(result.is_ok());
245
246 let client = result.unwrap();
247 assert_eq!(client.client_id(), ClientId::from("KRAKEN-TEST"));
248 }
249}