nautilus_bybit/python/
mod.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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 enums;
19pub mod http;
20pub mod urls;
21pub mod websocket;
22
23use nautilus_core::python::to_pyvalue_err;
24use nautilus_model::enums::BarAggregation;
25use pyo3::prelude::*;
26
27use crate::common::{
28    consts::BYBIT_NAUTILUS_BROKER_ID,
29    parse::{bar_spec_to_bybit_interval, extract_raw_symbol},
30};
31
32/// Extracts the raw symbol from a Bybit symbol by removing the product type suffix.
33///
34/// # Examples
35/// - `"ETHUSDT-LINEAR"` → `"ETHUSDT"`
36/// - `"BTCUSDT-SPOT"` → `"BTCUSDT"`
37/// - `"ETHUSDT"` → `"ETHUSDT"` (no suffix)
38#[pyfunction]
39#[pyo3(name = "extract_raw_symbol")]
40fn py_extract_raw_symbol(symbol: &str) -> &str {
41    extract_raw_symbol(symbol)
42}
43
44/// Converts a Nautilus bar aggregation and step to a Bybit kline interval string.
45///
46/// # Errors
47///
48/// Returns an error if the aggregation type or step is not supported by Bybit.
49#[pyfunction]
50#[pyo3(name = "bar_spec_to_bybit_interval")]
51fn py_bar_spec_to_bybit_interval(aggregation: u8, step: u64) -> PyResult<String> {
52    let aggregation = BarAggregation::from_repr(aggregation as usize).ok_or_else(|| {
53        pyo3::exceptions::PyValueError::new_err(format!(
54            "Invalid BarAggregation value: {aggregation}"
55        ))
56    })?;
57    bar_spec_to_bybit_interval(aggregation, step).map_err(to_pyvalue_err)
58}
59
60/// Loaded as `nautilus_pyo3.bybit`.
61///
62/// # Errors
63///
64/// Returns an error if any bindings fail to register with the Python module.
65#[pymodule]
66#[rustfmt::skip]
67pub fn bybit(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
68    m.add(stringify!(BYBIT_NAUTILUS_BROKER_ID), BYBIT_NAUTILUS_BROKER_ID)?;
69    m.add_class::<crate::common::enums::BybitAccountType>()?;
70    m.add_class::<crate::common::enums::BybitMarginMode>()?;
71    m.add_class::<crate::common::enums::BybitPositionMode>()?;
72    m.add_class::<crate::common::enums::BybitProductType>()?;
73    m.add_class::<crate::common::enums::BybitEnvironment>()?;
74    m.add_class::<crate::http::client::BybitHttpClient>()?;
75    m.add_class::<crate::http::models::BybitTickerData>()?;
76    m.add_class::<crate::websocket::client::BybitWebSocketClient>()?;
77    m.add_class::<crate::websocket::messages::BybitWebSocketError>()?;
78    m.add_function(wrap_pyfunction!(urls::py_get_bybit_http_base_url, m)?)?;
79    m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_public, m)?)?;
80    m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_private, m)?)?;
81    m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_trade, m)?)?;
82    m.add_function(wrap_pyfunction!(py_extract_raw_symbol, m)?)?;
83    m.add_function(wrap_pyfunction!(py_bar_spec_to_bybit_interval, m)?)?;
84    Ok(())
85}