Skip to main content

nautilus_kraken/
factories.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Factory functions for creating Kraken clients and components.
17
18use 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/// Factory for creating Kraken data clients.
46#[derive(Debug)]
47pub struct KrakenDataClientFactory;
48
49impl KrakenDataClientFactory {
50    /// Creates a new [`KrakenDataClientFactory`] instance.
51    #[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/// Factory for creating Kraken execution clients.
110#[derive(Debug)]
111pub struct KrakenExecutionClientFactory;
112
113impl KrakenExecutionClientFactory {
114    /// Creates a new [`KrakenExecutionClientFactory`] instance.
115    #[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        // Kraken Spot uses hedging, Futures uses netting
145        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, // base_currency
160            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}