nautilus_bybit/python/
mod.rs1pub mod config;
19pub mod enums;
20pub mod factories;
21pub mod http;
22pub mod params;
23pub mod types;
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_model::enums::{BarAggregation, OrderSide};
30use nautilus_system::get_global_pyo3_registry;
31use pyo3::prelude::*;
32
33use crate::{
34 common::{
35 consts::BYBIT_NAUTILUS_BROKER_ID,
36 enums::{BybitOrderSide, BybitPositionIdx, BybitPositionMode},
37 parse::{bar_spec_to_bybit_interval, extract_raw_symbol},
38 symbol::BybitSymbol,
39 },
40 config::{BybitDataClientConfig, BybitExecClientConfig},
41 execution::resolve_position_idx,
42 factories::{BybitDataClientFactory, BybitExecutionClientFactory},
43};
44
45#[pyfunction]
52#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.bybit")]
53#[pyo3(name = "bybit_extract_raw_symbol")]
54fn py_bybit_extract_raw_symbol(symbol: &str) -> &str {
55 extract_raw_symbol(symbol)
56}
57
58#[pyfunction]
64#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.bybit")]
65#[pyo3(name = "bybit_bar_spec_to_interval")]
66fn py_bybit_bar_spec_to_interval(aggregation: u8, step: u64) -> PyResult<String> {
67 let aggregation = BarAggregation::from_repr(aggregation as usize)
68 .ok_or_else(|| to_pyvalue_err(format!("Invalid BarAggregation value: {aggregation}")))?;
69 let interval = bar_spec_to_bybit_interval(aggregation, step).map_err(to_pyvalue_err)?;
70 Ok(interval.to_string())
71}
72
73#[pyfunction]
85#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.bybit")]
86#[pyo3(name = "bybit_product_type_from_symbol")]
87fn py_bybit_product_type_from_symbol(
88 symbol: &str,
89) -> PyResult<crate::common::enums::BybitProductType> {
90 let bybit_symbol = BybitSymbol::new(symbol).map_err(to_pyvalue_err)?;
91 Ok(bybit_symbol.product_type())
92}
93
94#[pyfunction]
100#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.bybit")]
101#[pyo3(name = "bybit_resolve_position_idx")]
102#[pyo3(signature = (position_mode, order_side, is_reduce_only, manual_override=None))]
103fn py_bybit_resolve_position_idx(
104 position_mode: Option<BybitPositionMode>,
105 order_side: OrderSide,
106 is_reduce_only: bool,
107 manual_override: Option<BybitPositionIdx>,
108) -> PyResult<Option<BybitPositionIdx>> {
109 let bybit_side = BybitOrderSide::try_from(order_side).map_err(to_pyvalue_err)?;
110 Ok(resolve_position_idx(
111 position_mode,
112 bybit_side,
113 is_reduce_only,
114 manual_override,
115 ))
116}
117
118#[expect(clippy::needless_pass_by_value)]
119fn extract_bybit_data_factory(
120 py: Python<'_>,
121 factory: Py<PyAny>,
122) -> PyResult<Box<dyn DataClientFactory>> {
123 match factory.extract::<BybitDataClientFactory>(py) {
124 Ok(f) => Ok(Box::new(f)),
125 Err(e) => Err(to_pyvalue_err(format!(
126 "Failed to extract BybitDataClientFactory: {e}"
127 ))),
128 }
129}
130
131#[expect(clippy::needless_pass_by_value)]
132fn extract_bybit_exec_factory(
133 py: Python<'_>,
134 factory: Py<PyAny>,
135) -> PyResult<Box<dyn ExecutionClientFactory>> {
136 match factory.extract::<BybitExecutionClientFactory>(py) {
137 Ok(f) => Ok(Box::new(f)),
138 Err(e) => Err(to_pyvalue_err(format!(
139 "Failed to extract BybitExecutionClientFactory: {e}"
140 ))),
141 }
142}
143
144#[expect(clippy::needless_pass_by_value)]
145fn extract_bybit_data_config(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>> {
146 match config.extract::<BybitDataClientConfig>(py) {
147 Ok(c) => Ok(Box::new(c)),
148 Err(e) => Err(to_pyvalue_err(format!(
149 "Failed to extract BybitDataClientConfig: {e}"
150 ))),
151 }
152}
153
154#[expect(clippy::needless_pass_by_value)]
155fn extract_bybit_exec_config(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>> {
156 match config.extract::<BybitExecClientConfig>(py) {
157 Ok(c) => Ok(Box::new(c)),
158 Err(e) => Err(to_pyvalue_err(format!(
159 "Failed to extract BybitExecClientConfig: {e}"
160 ))),
161 }
162}
163
164#[pymodule]
170#[rustfmt::skip]
171pub fn bybit(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
172 m.add(stringify!(BYBIT_NAUTILUS_BROKER_ID), BYBIT_NAUTILUS_BROKER_ID)?;
173 m.add_class::<crate::common::enums::BybitAccountType>()?;
174 m.add_class::<crate::common::enums::BybitCancelType>()?;
175 m.add_class::<crate::common::enums::BybitEnvironment>()?;
176 m.add_class::<crate::common::enums::BybitMarginAction>()?;
177 m.add_class::<crate::common::enums::BybitMarginMode>()?;
178 m.add_class::<crate::common::enums::BybitOpenOnly>()?;
179 m.add_class::<crate::common::enums::BybitOrderFilter>()?;
180 m.add_class::<crate::common::enums::BybitOrderSide>()?;
181 m.add_class::<crate::common::enums::BybitOrderStatus>()?;
182 m.add_class::<crate::common::enums::BybitOrderType>()?;
183 m.add_class::<crate::common::enums::BybitPositionIdx>()?;
184 m.add_class::<crate::common::enums::BybitPositionMode>()?;
185 m.add_class::<crate::common::enums::BybitProductType>()?;
186 m.add_class::<crate::common::enums::BybitStopOrderType>()?;
187 m.add_class::<crate::common::enums::BybitTimeInForce>()?;
188 m.add_class::<crate::common::enums::BybitTpSlMode>()?;
189 m.add_class::<crate::common::enums::BybitTriggerDirection>()?;
190 m.add_class::<crate::common::enums::BybitTriggerType>()?;
191 m.add_class::<crate::http::client::BybitHttpClient>()?;
192 m.add_class::<crate::http::client::BybitRawHttpClient>()?;
193 m.add_class::<crate::http::models::BybitServerTime>()?;
194 m.add_class::<crate::http::models::BybitOrder>()?;
195 m.add_class::<crate::http::models::BybitOrderCursorList>()?;
196 m.add_class::<crate::http::models::BybitTickerData>()?;
197 m.add_class::<crate::common::types::BybitMarginBorrowResult>()?;
198 m.add_class::<crate::common::types::BybitMarginRepayResult>()?;
199 m.add_class::<crate::common::types::BybitMarginStatusResult>()?;
200 m.add_class::<crate::websocket::client::BybitWebSocketClient>()?;
201 m.add_class::<crate::websocket::messages::BybitWebSocketError>()?;
202 m.add_class::<params::BybitWsPlaceOrderParams>()?;
203 m.add_class::<params::BybitWsAmendOrderParams>()?;
204 m.add_class::<params::BybitWsCancelOrderParams>()?;
205 m.add_class::<params::BybitTickersParams>()?;
206 m.add_class::<BybitDataClientConfig>()?;
207 m.add_class::<BybitExecClientConfig>()?;
208 m.add_class::<BybitDataClientFactory>()?;
209 m.add_class::<BybitExecutionClientFactory>()?;
210 m.add_function(wrap_pyfunction!(urls::py_get_bybit_http_base_url, m)?)?;
211 m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_public, m)?)?;
212 m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_private, m)?)?;
213 m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_trade, m)?)?;
214 m.add_function(wrap_pyfunction!(py_bybit_extract_raw_symbol, m)?)?;
215 m.add_function(wrap_pyfunction!(py_bybit_bar_spec_to_interval, m)?)?;
216 m.add_function(wrap_pyfunction!(py_bybit_product_type_from_symbol, m)?)?;
217 m.add_function(wrap_pyfunction!(py_bybit_resolve_position_idx, m)?)?;
218
219 let registry = get_global_pyo3_registry();
220
221 if let Err(e) =
222 registry.register_factory_extractor("BYBIT".to_string(), extract_bybit_data_factory)
223 {
224 return Err(to_pyruntime_err(format!(
225 "Failed to register Bybit data factory extractor: {e}"
226 )));
227 }
228
229 if let Err(e) = registry
230 .register_exec_factory_extractor("BYBIT".to_string(), extract_bybit_exec_factory)
231 {
232 return Err(to_pyruntime_err(format!(
233 "Failed to register Bybit exec factory extractor: {e}"
234 )));
235 }
236
237 if let Err(e) = registry.register_config_extractor(
238 "BybitDataClientConfig".to_string(),
239 extract_bybit_data_config,
240 ) {
241 return Err(to_pyruntime_err(format!(
242 "Failed to register Bybit data config extractor: {e}"
243 )));
244 }
245
246 if let Err(e) = registry.register_config_extractor(
247 "BybitExecClientConfig".to_string(),
248 extract_bybit_exec_config,
249 ) {
250 return Err(to_pyruntime_err(format!(
251 "Failed to register Bybit exec config extractor: {e}"
252 )));
253 }
254
255 Ok(())
256}