Skip to main content

nautilus_binance/
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 Binance clients and components.
17
18use std::{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::{
34        consts::{BINANCE, BINANCE_VENUE},
35        enums::BinanceProductType,
36    },
37    config::{BinanceDataClientConfig, BinanceExecClientConfig},
38    futures::{data::BinanceFuturesDataClient, execution::BinanceFuturesExecutionClient},
39    spot::{data::BinanceSpotDataClient, execution::BinanceSpotExecutionClient},
40};
41
42/// Factory for creating Binance data clients.
43#[derive(Debug)]
44pub struct BinanceDataClientFactory;
45
46impl BinanceDataClientFactory {
47    /// Creates a new [`BinanceDataClientFactory`] instance.
48    #[must_use]
49    pub const fn new() -> Self {
50        Self
51    }
52}
53
54impl Default for BinanceDataClientFactory {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl DataClientFactory for BinanceDataClientFactory {
61    fn create(
62        &self,
63        name: &str,
64        config: &dyn ClientConfig,
65        _cache: Rc<RefCell<Cache>>,
66        _clock: Rc<RefCell<dyn Clock>>,
67    ) -> anyhow::Result<Box<dyn DataClient>> {
68        let binance_config = config
69            .as_any()
70            .downcast_ref::<BinanceDataClientConfig>()
71            .ok_or_else(|| {
72                anyhow::anyhow!(
73                    "Invalid config type for BinanceDataClientFactory. Expected BinanceDataClientConfig, was {config:?}",
74                )
75            })?
76            .clone();
77
78        let client_id = ClientId::from(name);
79
80        let product_type = binance_config
81            .product_types
82            .first()
83            .copied()
84            .unwrap_or(BinanceProductType::Spot);
85
86        match product_type {
87            BinanceProductType::Spot => {
88                let client = BinanceSpotDataClient::new(client_id, binance_config)?;
89                Ok(Box::new(client))
90            }
91            BinanceProductType::UsdM | BinanceProductType::CoinM => {
92                let client =
93                    BinanceFuturesDataClient::new(client_id, binance_config, product_type)?;
94                Ok(Box::new(client))
95            }
96            _ => {
97                anyhow::bail!("Unsupported product type for Binance data client: {product_type:?}")
98            }
99        }
100    }
101
102    fn name(&self) -> &'static str {
103        BINANCE
104    }
105
106    fn config_type(&self) -> &'static str {
107        stringify!(BinanceDataClientConfig)
108    }
109}
110
111/// Factory for creating Binance Spot execution clients.
112#[derive(Debug)]
113pub struct BinanceExecutionClientFactory;
114
115impl BinanceExecutionClientFactory {
116    /// Creates a new [`BinanceExecutionClientFactory`] instance.
117    #[must_use]
118    pub const fn new() -> Self {
119        Self
120    }
121}
122
123impl Default for BinanceExecutionClientFactory {
124    fn default() -> Self {
125        Self::new()
126    }
127}
128
129impl ExecutionClientFactory for BinanceExecutionClientFactory {
130    fn create(
131        &self,
132        name: &str,
133        config: &dyn ClientConfig,
134        cache: Rc<RefCell<Cache>>,
135    ) -> anyhow::Result<Box<dyn ExecutionClient>> {
136        let binance_config = config
137            .as_any()
138            .downcast_ref::<BinanceExecClientConfig>()
139            .ok_or_else(|| {
140                anyhow::anyhow!(
141                    "Invalid config type for BinanceExecutionClientFactory. Expected BinanceExecClientConfig, was {config:?}",
142                )
143            })?
144            .clone();
145
146        let product_type = binance_config
147            .product_types
148            .first()
149            .copied()
150            .unwrap_or(BinanceProductType::Spot);
151
152        match product_type {
153            BinanceProductType::Spot => {
154                // Spot uses cash account type and hedging OMS
155                let account_type = AccountType::Cash;
156                let oms_type = OmsType::Hedging;
157
158                let core = ExecutionClientCore::new(
159                    binance_config.trader_id,
160                    ClientId::from(name),
161                    *BINANCE_VENUE,
162                    oms_type,
163                    binance_config.account_id,
164                    account_type,
165                    None, // base_currency
166                    cache,
167                );
168
169                let client = BinanceSpotExecutionClient::new(core, binance_config)?;
170                Ok(Box::new(client))
171            }
172            BinanceProductType::UsdM | BinanceProductType::CoinM => {
173                // Futures uses margin account type and netting OMS
174                let account_type = AccountType::Margin;
175                let oms_type = OmsType::Netting;
176
177                let core = ExecutionClientCore::new(
178                    binance_config.trader_id,
179                    ClientId::from(name),
180                    *BINANCE_VENUE,
181                    oms_type,
182                    binance_config.account_id,
183                    account_type,
184                    None, // base_currency
185                    cache,
186                );
187
188                let client = BinanceFuturesExecutionClient::new(core, binance_config)?;
189                Ok(Box::new(client))
190            }
191            _ => {
192                anyhow::bail!(
193                    "Unsupported product type for Binance execution client: {product_type:?}"
194                )
195            }
196        }
197    }
198
199    fn name(&self) -> &'static str {
200        BINANCE
201    }
202
203    fn config_type(&self) -> &'static str {
204        stringify!(BinanceExecClientConfig)
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use nautilus_system::factories::DataClientFactory;
211    use rstest::rstest;
212
213    use super::*;
214
215    #[rstest]
216    fn test_binance_data_client_factory_creation() {
217        let factory = BinanceDataClientFactory::new();
218        assert_eq!(factory.name(), "BINANCE");
219        assert_eq!(factory.config_type(), "BinanceDataClientConfig");
220    }
221
222    #[rstest]
223    fn test_binance_data_client_factory_default() {
224        let factory = BinanceDataClientFactory;
225        assert_eq!(factory.name(), "BINANCE");
226    }
227}