Skip to main content

nautilus_bitmex/
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 BitMEX clients and components.
17
18use std::{any::Any, cell::RefCell, rc::Rc};
19
20use nautilus_common::{
21    cache::CacheView,
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::{AccountId, ClientId, TraderId},
30};
31
32use crate::{
33    common::consts::{BITMEX, BITMEX_VENUE},
34    config::{BitmexDataClientConfig, BitmexExecClientConfig},
35    data::BitmexDataClient,
36    execution::BitmexExecutionClient,
37};
38
39impl ClientConfig for BitmexDataClientConfig {
40    fn as_any(&self) -> &dyn Any {
41        self
42    }
43}
44
45/// Configuration for creating BitMEX execution clients via factory.
46///
47/// This wraps [`BitmexExecClientConfig`] with the additional trader and account
48/// identifiers required by the [`ExecutionClientCore`].
49#[derive(Clone, Debug)]
50#[cfg_attr(
51    feature = "python",
52    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.bitmex", from_py_object)
53)]
54#[cfg_attr(
55    feature = "python",
56    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.bitmex")
57)]
58pub struct BitmexExecFactoryConfig {
59    /// The trader ID for the execution client.
60    pub trader_id: TraderId,
61    /// The account ID for the execution client.
62    pub account_id: AccountId,
63    /// The underlying execution client configuration.
64    pub config: BitmexExecClientConfig,
65}
66
67#[cfg(feature = "python")]
68nautilus_core::impl_pyo3_config_getters!(BitmexExecFactoryConfig {
69    trader_id: TraderId,
70    account_id: AccountId,
71    config: BitmexExecClientConfig,
72});
73
74impl BitmexExecFactoryConfig {
75    /// Creates a new [`BitmexExecFactoryConfig`].
76    ///
77    /// The `account_id` defaults to `BITMEX-001` and is overridden once the
78    /// real account number is detected from the API.
79    #[must_use]
80    pub fn new(trader_id: TraderId, config: BitmexExecClientConfig) -> Self {
81        Self {
82            trader_id,
83            account_id: AccountId::from("BITMEX-001"),
84            config,
85        }
86    }
87}
88
89impl ClientConfig for BitmexExecFactoryConfig {
90    fn as_any(&self) -> &dyn Any {
91        self
92    }
93}
94
95/// Factory for creating BitMEX data clients.
96#[derive(Debug, Clone)]
97#[cfg_attr(
98    feature = "python",
99    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.bitmex", from_py_object)
100)]
101#[cfg_attr(
102    feature = "python",
103    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.bitmex")
104)]
105pub struct BitmexDataClientFactory;
106
107impl BitmexDataClientFactory {
108    /// Creates a new [`BitmexDataClientFactory`] instance.
109    #[must_use]
110    pub const fn new() -> Self {
111        Self
112    }
113}
114
115impl Default for BitmexDataClientFactory {
116    fn default() -> Self {
117        Self::new()
118    }
119}
120
121impl DataClientFactory for BitmexDataClientFactory {
122    fn create(
123        &self,
124        name: &str,
125        config: &dyn ClientConfig,
126        _cache: CacheView,
127        _clock: Rc<RefCell<dyn Clock>>,
128    ) -> anyhow::Result<Box<dyn DataClient>> {
129        let bitmex_config = config
130            .as_any()
131            .downcast_ref::<BitmexDataClientConfig>()
132            .ok_or_else(|| {
133                anyhow::anyhow!(
134                    "Invalid config type for BitmexDataClientFactory. Expected BitmexDataClientConfig, was {config:?}",
135                )
136            })?
137            .clone();
138
139        let client_id = ClientId::from(name);
140        let client = BitmexDataClient::new(client_id, bitmex_config)?;
141        Ok(Box::new(client))
142    }
143
144    fn name(&self) -> &'static str {
145        BITMEX
146    }
147
148    fn config_type(&self) -> &'static str {
149        "BitmexDataClientConfig"
150    }
151}
152
153/// Factory for creating BitMEX execution clients.
154#[derive(Debug, Clone)]
155#[cfg_attr(
156    feature = "python",
157    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.bitmex", from_py_object)
158)]
159#[cfg_attr(
160    feature = "python",
161    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.bitmex")
162)]
163pub struct BitmexExecutionClientFactory;
164
165impl BitmexExecutionClientFactory {
166    /// Creates a new [`BitmexExecutionClientFactory`] instance.
167    #[must_use]
168    pub const fn new() -> Self {
169        Self
170    }
171}
172
173impl Default for BitmexExecutionClientFactory {
174    fn default() -> Self {
175        Self::new()
176    }
177}
178
179impl ExecutionClientFactory for BitmexExecutionClientFactory {
180    fn create(
181        &self,
182        name: &str,
183        config: &dyn ClientConfig,
184        cache: CacheView,
185    ) -> anyhow::Result<Box<dyn ExecutionClient>> {
186        let factory_config = config
187            .as_any()
188            .downcast_ref::<BitmexExecFactoryConfig>()
189            .ok_or_else(|| {
190                anyhow::anyhow!(
191                    "Invalid config type for BitmexExecutionClientFactory. Expected BitmexExecFactoryConfig, was {config:?}",
192                )
193            })?
194            .clone();
195
196        let mut bitmex_config = factory_config.config;
197        bitmex_config.account_id = Some(factory_config.account_id);
198
199        let core = ExecutionClientCore::new(
200            factory_config.trader_id,
201            ClientId::from(name),
202            *BITMEX_VENUE,
203            OmsType::Netting,
204            factory_config.account_id,
205            AccountType::Margin,
206            None, // base_currency
207            cache,
208        );
209
210        let client = BitmexExecutionClient::new(core, bitmex_config)?;
211        Ok(Box::new(client))
212    }
213
214    fn name(&self) -> &'static str {
215        BITMEX
216    }
217
218    fn config_type(&self) -> &'static str {
219        "BitmexExecFactoryConfig"
220    }
221}