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