Skip to main content

nautilus_binance/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 for the Binance adapter.
17
18pub mod config;
19pub mod enums;
20pub mod factories;
21pub mod types;
22
23#[cfg(feature = "arrow")]
24pub mod arrow;
25
26mod data;
27mod instruments;
28
29use nautilus_common::factories::{ClientConfig, DataClientFactory, ExecutionClientFactory};
30use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
31use nautilus_model::data::ensure_rust_extractor_registered;
32use nautilus_system::get_global_pyo3_registry;
33use pyo3::prelude::*;
34
35use crate::{
36    common::{
37        bar::BinanceBar,
38        consts::{
39            BINANCE, BINANCE_CLIENT_ID, BINANCE_NAUTILUS_FUTURES_BROKER_ID,
40            BINANCE_NAUTILUS_SPOT_BROKER_ID, BINANCE_VENUE,
41        },
42        encoder::decode_broker_id,
43        enums::{BinanceEnvironment, BinanceMarginType, BinancePositionSide, BinanceProductType},
44    },
45    config::{
46        BinanceDataClientConfig, BinanceExecutionClientConfig, BinanceInstrumentProviderConfig,
47        BinanceSpotMarketDataMode,
48    },
49    data_types::{
50        BinanceFuturesLiquidation, BinanceFuturesMarkPriceUpdate, BinanceFuturesOpenInterest,
51        BinanceFuturesOpenInterestHist, BinanceFuturesOpenInterestHistPoint, BinanceFuturesTicker,
52        BinanceSpotTicker, register_binance_custom_data,
53    },
54    factories::{BinanceDataClientFactory, BinanceExecutionClientFactory},
55};
56
57#[expect(clippy::needless_pass_by_value)]
58fn extract_binance_data_factory(
59    py: Python<'_>,
60    factory: Py<PyAny>,
61) -> PyResult<Box<dyn DataClientFactory>> {
62    match factory.extract::<BinanceDataClientFactory>(py) {
63        Ok(f) => Ok(Box::new(f)),
64        Err(e) => Err(to_pyvalue_err(format!(
65            "Failed to extract BinanceDataClientFactory: {e}"
66        ))),
67    }
68}
69
70#[expect(clippy::needless_pass_by_value)]
71fn extract_binance_exec_factory(
72    py: Python<'_>,
73    factory: Py<PyAny>,
74) -> PyResult<Box<dyn ExecutionClientFactory>> {
75    match factory.extract::<BinanceExecutionClientFactory>(py) {
76        Ok(f) => Ok(Box::new(f)),
77        Err(e) => Err(to_pyvalue_err(format!(
78            "Failed to extract BinanceExecutionClientFactory: {e}"
79        ))),
80    }
81}
82
83#[expect(clippy::needless_pass_by_value)]
84fn extract_binance_data_config(
85    py: Python<'_>,
86    config: Py<PyAny>,
87) -> PyResult<Box<dyn ClientConfig>> {
88    match config.extract::<BinanceDataClientConfig>(py) {
89        Ok(c) => Ok(Box::new(c)),
90        Err(e) => Err(to_pyvalue_err(format!(
91            "Failed to extract BinanceDataClientConfig: {e}"
92        ))),
93    }
94}
95
96#[expect(clippy::needless_pass_by_value)]
97fn extract_binance_exec_config(
98    py: Python<'_>,
99    config: Py<PyAny>,
100) -> PyResult<Box<dyn ClientConfig>> {
101    match config.extract::<BinanceExecutionClientConfig>(py) {
102        Ok(c) => Ok(Box::new(c)),
103        Err(e) => Err(to_pyvalue_err(format!(
104            "Failed to extract BinanceExecutionClientConfig: {e}"
105        ))),
106    }
107}
108
109/// Decodes a Binance Spot encoded `clientOrderId` back to the original value.
110///
111/// Binance Spot orders placed through the Rust execution client have their
112/// `ClientOrderId` encoded with a broker ID prefix for Link and Trade
113/// attribution. This function reverses that encoding.
114///
115/// Strings without the broker prefix are returned unchanged.
116#[pyfunction]
117#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.binance")]
118#[pyo3(name = "decode_binance_spot_client_order_id")]
119fn py_decode_binance_spot_client_order_id(encoded: &str) -> String {
120    decode_broker_id(encoded, BINANCE_NAUTILUS_SPOT_BROKER_ID)
121}
122
123/// Decodes a Binance Futures encoded `clientOrderId` back to the original value.
124///
125/// Binance Futures orders placed through the Rust execution client have their
126/// `ClientOrderId` encoded with a broker ID prefix for Link and Trade
127/// attribution. This function reverses that encoding.
128///
129/// Strings without the broker prefix are returned unchanged.
130#[pyfunction]
131#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.binance")]
132#[pyo3(name = "decode_binance_futures_client_order_id")]
133fn py_decode_binance_futures_client_order_id(encoded: &str) -> String {
134    decode_broker_id(encoded, BINANCE_NAUTILUS_FUTURES_BROKER_ID)
135}
136
137/// Binance adapter Python module.
138///
139/// Exposed through `nautilus_trader.adapters.binance`.
140///
141/// # Errors
142///
143/// Returns an error if module initialization fails.
144#[pymodule]
145pub fn binance(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
146    m.add(stringify!(BINANCE), BINANCE)?;
147    m.add(stringify!(BINANCE_CLIENT_ID), *BINANCE_CLIENT_ID)?;
148    m.add(stringify!(BINANCE_VENUE), *BINANCE_VENUE)?;
149    m.add_class::<BinanceProductType>()?;
150    m.add_class::<BinanceEnvironment>()?;
151    m.add_class::<BinanceMarginType>()?;
152    m.add_class::<BinancePositionSide>()?;
153    m.add_class::<BinanceBar>()?;
154    m.add_class::<BinanceFuturesLiquidation>()?;
155    m.add_class::<BinanceFuturesTicker>()?;
156    m.add_class::<BinanceSpotTicker>()?;
157    m.add_class::<BinanceFuturesMarkPriceUpdate>()?;
158    m.add_class::<BinanceFuturesOpenInterest>()?;
159    m.add_class::<BinanceFuturesOpenInterestHistPoint>()?;
160    m.add_class::<BinanceFuturesOpenInterestHist>()?;
161
162    #[cfg(feature = "arrow")]
163    {
164        m.add_function(wrap_pyfunction!(arrow::get_binance_arrow_schema_map, m)?)?;
165        m.add_function(wrap_pyfunction!(
166            arrow::py_binance_bar_to_arrow_record_batch_bytes,
167            m
168        )?)?;
169        m.add_function(wrap_pyfunction!(
170            arrow::py_binance_bar_from_arrow_record_batch_bytes,
171            m
172        )?)?;
173    }
174
175    m.add_class::<BinanceDataClientConfig>()?;
176    m.add_class::<BinanceDataClientFactory>()?;
177    m.add_class::<BinanceExecutionClientConfig>()?;
178    m.add_class::<BinanceExecutionClientFactory>()?;
179    m.add_class::<BinanceInstrumentProviderConfig>()?;
180    m.add_class::<BinanceSpotMarketDataMode>()?;
181    m.add_function(wrap_pyfunction!(py_decode_binance_spot_client_order_id, m)?)?;
182    m.add_function(wrap_pyfunction!(
183        py_decode_binance_futures_client_order_id,
184        m
185    )?)?;
186    m.add_function(wrap_pyfunction!(
187        instruments::py_load_binance_instruments,
188        m
189    )?)?;
190    m.add_function(wrap_pyfunction!(
191        data::py_load_binance_order_book_deltas,
192        m
193    )?)?;
194
195    register_binance_custom_data();
196    let _result = ensure_rust_extractor_registered::<BinanceBar>();
197    let _result = ensure_rust_extractor_registered::<BinanceFuturesLiquidation>();
198    let _result = ensure_rust_extractor_registered::<BinanceFuturesTicker>();
199    let _result = ensure_rust_extractor_registered::<BinanceSpotTicker>();
200    let _result = ensure_rust_extractor_registered::<BinanceFuturesMarkPriceUpdate>();
201    let _result = ensure_rust_extractor_registered::<BinanceFuturesOpenInterest>();
202    let _result = ensure_rust_extractor_registered::<BinanceFuturesOpenInterestHist>();
203
204    let registry = get_global_pyo3_registry();
205
206    if let Err(e) =
207        registry.register_factory_extractor(BINANCE.to_string(), extract_binance_data_factory)
208    {
209        return Err(to_pyruntime_err(format!(
210            "Failed to register Binance data factory extractor: {e}"
211        )));
212    }
213
214    if let Err(e) =
215        registry.register_exec_factory_extractor(BINANCE.to_string(), extract_binance_exec_factory)
216    {
217        return Err(to_pyruntime_err(format!(
218            "Failed to register Binance exec factory extractor: {e}"
219        )));
220    }
221
222    if let Err(e) = registry.register_config_extractor(
223        "BinanceDataClientConfig".to_string(),
224        extract_binance_data_config,
225    ) {
226        return Err(to_pyruntime_err(format!(
227            "Failed to register Binance data config extractor: {e}"
228        )));
229    }
230
231    if let Err(e) = registry.register_config_extractor(
232        "BinanceExecutionClientConfig".to_string(),
233        extract_binance_exec_config,
234    ) {
235        return Err(to_pyruntime_err(format!(
236            "Failed to register Binance exec config extractor: {e}"
237        )));
238    }
239
240    Ok(())
241}