Skip to main content

nautilus_bitmex/python/
mod.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//! Python bindings from `pyo3`.
17
18pub mod canceller;
19pub mod config;
20pub mod enums;
21pub mod factories;
22pub mod http;
23pub mod submitter;
24pub mod urls;
25pub mod websocket;
26
27use nautilus_common::factories::{ClientConfig, DataClientFactory, ExecutionClientFactory};
28use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
29use nautilus_system::get_global_pyo3_registry;
30use pyo3::prelude::*;
31
32use crate::{
33    common::consts::BITMEX,
34    config::{BitmexDataClientConfig, BitmexExecClientConfig},
35    factories::{BitmexDataClientFactory, BitmexExecFactoryConfig, BitmexExecutionClientFactory},
36};
37
38#[expect(clippy::needless_pass_by_value)]
39fn extract_bitmex_data_factory(
40    py: Python<'_>,
41    factory: Py<PyAny>,
42) -> PyResult<Box<dyn DataClientFactory>> {
43    match factory.extract::<BitmexDataClientFactory>(py) {
44        Ok(f) => Ok(Box::new(f)),
45        Err(e) => Err(to_pyvalue_err(format!(
46            "Failed to extract BitmexDataClientFactory: {e}"
47        ))),
48    }
49}
50
51#[expect(clippy::needless_pass_by_value)]
52fn extract_bitmex_exec_factory(
53    py: Python<'_>,
54    factory: Py<PyAny>,
55) -> PyResult<Box<dyn ExecutionClientFactory>> {
56    match factory.extract::<BitmexExecutionClientFactory>(py) {
57        Ok(f) => Ok(Box::new(f)),
58        Err(e) => Err(to_pyvalue_err(format!(
59            "Failed to extract BitmexExecutionClientFactory: {e}"
60        ))),
61    }
62}
63
64#[expect(clippy::needless_pass_by_value)]
65fn extract_bitmex_data_config(
66    py: Python<'_>,
67    config: Py<PyAny>,
68) -> PyResult<Box<dyn ClientConfig>> {
69    match config.extract::<BitmexDataClientConfig>(py) {
70        Ok(c) => Ok(Box::new(c)),
71        Err(e) => Err(to_pyvalue_err(format!(
72            "Failed to extract BitmexDataClientConfig: {e}"
73        ))),
74    }
75}
76
77#[expect(clippy::needless_pass_by_value)]
78fn extract_bitmex_exec_config(
79    py: Python<'_>,
80    config: Py<PyAny>,
81) -> PyResult<Box<dyn ClientConfig>> {
82    match config.extract::<BitmexExecFactoryConfig>(py) {
83        Ok(c) => Ok(Box::new(c)),
84        Err(e) => Err(to_pyvalue_err(format!(
85            "Failed to extract BitmexExecFactoryConfig: {e}"
86        ))),
87    }
88}
89
90/// Loaded as `nautilus_pyo3.bitmex`.
91///
92/// # Errors
93///
94/// Returns an error if the module registration fails or if adding functions/classes fails.
95#[pymodule]
96pub fn bitmex(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
97    m.add("BITMEX_HTTP_URL", crate::common::consts::BITMEX_HTTP_URL)?;
98    m.add("BITMEX_WS_URL", crate::common::consts::BITMEX_WS_URL)?;
99    m.add_class::<crate::common::enums::BitmexEnvironment>()?;
100    m.add_class::<crate::http::client::BitmexHttpClient>()?;
101    m.add_class::<crate::broadcast::canceller::CancelBroadcaster>()?;
102    m.add_class::<crate::broadcast::submitter::SubmitBroadcaster>()?;
103    m.add_class::<websocket::PyBitmexWebSocketClient>()?;
104    m.add_class::<BitmexDataClientConfig>()?;
105    m.add_class::<BitmexExecClientConfig>()?;
106    m.add_class::<BitmexExecFactoryConfig>()?;
107    m.add_class::<BitmexDataClientFactory>()?;
108    m.add_class::<BitmexExecutionClientFactory>()?;
109    m.add_function(wrap_pyfunction!(urls::get_bitmex_http_base_url, m)?)?;
110    m.add_function(wrap_pyfunction!(urls::get_bitmex_ws_url, m)?)?;
111
112    let registry = get_global_pyo3_registry();
113
114    if let Err(e) =
115        registry.register_factory_extractor(BITMEX.to_string(), extract_bitmex_data_factory)
116    {
117        return Err(to_pyruntime_err(format!(
118            "Failed to register BitMEX data factory extractor: {e}"
119        )));
120    }
121
122    if let Err(e) =
123        registry.register_exec_factory_extractor(BITMEX.to_string(), extract_bitmex_exec_factory)
124    {
125        return Err(to_pyruntime_err(format!(
126            "Failed to register BitMEX exec factory extractor: {e}"
127        )));
128    }
129
130    if let Err(e) = registry.register_config_extractor(
131        "BitmexDataClientConfig".to_string(),
132        extract_bitmex_data_config,
133    ) {
134        return Err(to_pyruntime_err(format!(
135            "Failed to register BitMEX data config extractor: {e}"
136        )));
137    }
138
139    if let Err(e) = registry.register_config_extractor(
140        "BitmexExecFactoryConfig".to_string(),
141        extract_bitmex_exec_config,
142    ) {
143        return Err(to_pyruntime_err(format!(
144            "Failed to register BitMEX exec config extractor: {e}"
145        )));
146    }
147
148    Ok(())
149}