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