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