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::{
30 enums::{BarAggregation, OrderSide},
31 identifiers::{InstrumentId, PositionId},
32};
33use nautilus_system::get_global_pyo3_registry;
34use pyo3::prelude::*;
35
36use crate::{
37 common::{
38 consts::{BYBIT, BYBIT_NAUTILUS_BROKER_ID},
39 enums::{BybitOrderSide, BybitPositionIdx, BybitPositionMode},
40 parse::{
41 bar_spec_to_bybit_interval, extract_raw_symbol, make_hedge_venue_position_id,
42 resolve_position_idx,
43 },
44 symbol::BybitSymbol,
45 },
46 config::{BybitDataClientConfig, BybitExecClientConfig},
47 factories::{BybitDataClientFactory, BybitExecutionClientFactory},
48};
49
50#[pyfunction]
57#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.bybit")]
58#[pyo3(name = "bybit_extract_raw_symbol")]
59fn py_bybit_extract_raw_symbol(symbol: &str) -> &str {
60 extract_raw_symbol(symbol)
61}
62
63#[pyfunction]
69#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.bybit")]
70#[pyo3(name = "bybit_bar_spec_to_interval")]
71fn py_bybit_bar_spec_to_interval(aggregation: u8, step: u64) -> PyResult<String> {
72 let aggregation = BarAggregation::from_repr(aggregation as usize)
73 .ok_or_else(|| to_pyvalue_err(format!("Invalid BarAggregation value: {aggregation}")))?;
74 let interval = bar_spec_to_bybit_interval(aggregation, step).map_err(to_pyvalue_err)?;
75 Ok(interval.to_string())
76}
77
78#[pyfunction]
90#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.bybit")]
91#[pyo3(name = "bybit_product_type_from_symbol")]
92fn py_bybit_product_type_from_symbol(
93 symbol: &str,
94) -> PyResult<crate::common::enums::BybitProductType> {
95 let bybit_symbol = BybitSymbol::new(symbol).map_err(to_pyvalue_err)?;
96 Ok(bybit_symbol.product_type())
97}
98
99#[pyfunction]
105#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.bybit")]
106#[pyo3(name = "bybit_resolve_position_idx")]
107#[pyo3(signature = (position_mode, order_side, is_reduce_only, manual_override=None))]
108fn py_bybit_resolve_position_idx(
109 position_mode: Option<BybitPositionMode>,
110 order_side: OrderSide,
111 is_reduce_only: bool,
112 manual_override: Option<BybitPositionIdx>,
113) -> PyResult<Option<BybitPositionIdx>> {
114 let bybit_side = BybitOrderSide::try_from(order_side).map_err(to_pyvalue_err)?;
115 Ok(resolve_position_idx(
116 position_mode,
117 bybit_side,
118 is_reduce_only,
119 manual_override,
120 ))
121}
122
123#[pyfunction]
125#[pyo3_stub_gen::derive::gen_stub_pyfunction(module = "nautilus_trader.adapters.bybit")]
126#[pyo3(name = "bybit_make_hedge_venue_position_id")]
127#[pyo3(signature = (instrument_id, position_idx=None))]
128fn py_bybit_make_hedge_venue_position_id(
129 instrument_id: InstrumentId,
130 position_idx: Option<BybitPositionIdx>,
131) -> Option<PositionId> {
132 position_idx.and_then(|idx| make_hedge_venue_position_id(instrument_id, idx as i32))
133}
134
135#[expect(clippy::needless_pass_by_value)]
136fn extract_bybit_data_factory(
137 py: Python<'_>,
138 factory: Py<PyAny>,
139) -> PyResult<Box<dyn DataClientFactory>> {
140 match factory.extract::<BybitDataClientFactory>(py) {
141 Ok(f) => Ok(Box::new(f)),
142 Err(e) => Err(to_pyvalue_err(format!(
143 "Failed to extract BybitDataClientFactory: {e}"
144 ))),
145 }
146}
147
148#[expect(clippy::needless_pass_by_value)]
149fn extract_bybit_exec_factory(
150 py: Python<'_>,
151 factory: Py<PyAny>,
152) -> PyResult<Box<dyn ExecutionClientFactory>> {
153 match factory.extract::<BybitExecutionClientFactory>(py) {
154 Ok(f) => Ok(Box::new(f)),
155 Err(e) => Err(to_pyvalue_err(format!(
156 "Failed to extract BybitExecutionClientFactory: {e}"
157 ))),
158 }
159}
160
161#[expect(clippy::needless_pass_by_value)]
162fn extract_bybit_data_config(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>> {
163 match config.extract::<BybitDataClientConfig>(py) {
164 Ok(c) => Ok(Box::new(c)),
165 Err(e) => Err(to_pyvalue_err(format!(
166 "Failed to extract BybitDataClientConfig: {e}"
167 ))),
168 }
169}
170
171#[expect(clippy::needless_pass_by_value)]
172fn extract_bybit_exec_config(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>> {
173 match config.extract::<BybitExecClientConfig>(py) {
174 Ok(c) => Ok(Box::new(c)),
175 Err(e) => Err(to_pyvalue_err(format!(
176 "Failed to extract BybitExecClientConfig: {e}"
177 ))),
178 }
179}
180
181#[pymodule]
187#[rustfmt::skip]
188pub fn bybit(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
189 m.add(stringify!(BYBIT_NAUTILUS_BROKER_ID), BYBIT_NAUTILUS_BROKER_ID)?;
190 m.add_class::<crate::common::enums::BybitAccountType>()?;
191 m.add_class::<crate::common::enums::BybitCancelType>()?;
192 m.add_class::<crate::common::enums::BybitEnvironment>()?;
193 m.add_class::<crate::common::enums::BybitMarginAction>()?;
194 m.add_class::<crate::common::enums::BybitMarginMode>()?;
195 m.add_class::<crate::common::enums::BybitOpenOnly>()?;
196 m.add_class::<crate::common::enums::BybitOrderFilter>()?;
197 m.add_class::<crate::common::enums::BybitOrderSide>()?;
198 m.add_class::<crate::common::enums::BybitOrderStatus>()?;
199 m.add_class::<crate::common::enums::BybitOrderType>()?;
200 m.add_class::<crate::common::enums::BybitPositionIdx>()?;
201 m.add_class::<crate::common::enums::BybitPositionMode>()?;
202 m.add_class::<crate::common::enums::BybitProductType>()?;
203 m.add_class::<crate::common::enums::BybitStopOrderType>()?;
204 m.add_class::<crate::common::enums::BybitTimeInForce>()?;
205 m.add_class::<crate::common::enums::BybitTpSlMode>()?;
206 m.add_class::<crate::common::enums::BybitTriggerDirection>()?;
207 m.add_class::<crate::common::enums::BybitTriggerType>()?;
208 m.add_class::<crate::http::client::BybitHttpClient>()?;
209 m.add_class::<crate::http::client::BybitRawHttpClient>()?;
210 m.add_class::<crate::http::models::BybitServerTime>()?;
211 m.add_class::<crate::http::models::BybitOrder>()?;
212 m.add_class::<crate::http::models::BybitOrderCursorList>()?;
213 m.add_class::<crate::http::models::BybitTickerData>()?;
214 m.add_class::<crate::common::types::BybitMarginBorrowResult>()?;
215 m.add_class::<crate::common::types::BybitMarginRepayResult>()?;
216 m.add_class::<crate::common::types::BybitMarginStatusResult>()?;
217 m.add_class::<crate::websocket::client::BybitWebSocketClient>()?;
218 m.add_class::<crate::websocket::messages::BybitWebSocketError>()?;
219 m.add_class::<params::BybitWsPlaceOrderParams>()?;
220 m.add_class::<params::BybitWsAmendOrderParams>()?;
221 m.add_class::<params::BybitWsCancelOrderParams>()?;
222 m.add_class::<params::BybitTickersParams>()?;
223 m.add_class::<params::BybitNativeTpSlParams>()?;
224 m.add_class::<BybitDataClientConfig>()?;
225 m.add_class::<BybitExecClientConfig>()?;
226 m.add_class::<BybitDataClientFactory>()?;
227 m.add_class::<BybitExecutionClientFactory>()?;
228 m.add_function(wrap_pyfunction!(urls::py_get_bybit_http_base_url, m)?)?;
229 m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_public, m)?)?;
230 m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_private, m)?)?;
231 m.add_function(wrap_pyfunction!(urls::py_get_bybit_ws_url_trade, m)?)?;
232 m.add_function(wrap_pyfunction!(py_bybit_extract_raw_symbol, m)?)?;
233 m.add_function(wrap_pyfunction!(py_bybit_bar_spec_to_interval, m)?)?;
234 m.add_function(wrap_pyfunction!(py_bybit_product_type_from_symbol, m)?)?;
235 m.add_function(wrap_pyfunction!(py_bybit_resolve_position_idx, m)?)?;
236 m.add_function(wrap_pyfunction!(py_bybit_make_hedge_venue_position_id, m)?)?;
237
238 let registry = get_global_pyo3_registry();
239
240 if let Err(e) =
241 registry.register_factory_extractor(BYBIT.to_string(), extract_bybit_data_factory)
242 {
243 return Err(to_pyruntime_err(format!(
244 "Failed to register Bybit data factory extractor: {e}"
245 )));
246 }
247
248 if let Err(e) = registry
249 .register_exec_factory_extractor(BYBIT.to_string(), extract_bybit_exec_factory)
250 {
251 return Err(to_pyruntime_err(format!(
252 "Failed to register Bybit exec factory extractor: {e}"
253 )));
254 }
255
256 if let Err(e) = registry.register_config_extractor(
257 "BybitDataClientConfig".to_string(),
258 extract_bybit_data_config,
259 ) {
260 return Err(to_pyruntime_err(format!(
261 "Failed to register Bybit data config extractor: {e}"
262 )));
263 }
264
265 if let Err(e) = registry.register_config_extractor(
266 "BybitExecClientConfig".to_string(),
267 extract_bybit_exec_config,
268 ) {
269 return Err(to_pyruntime_err(format!(
270 "Failed to register Bybit exec config extractor: {e}"
271 )));
272 }
273
274 Ok(())
275}