Skip to main content

nautilus_bybit/python/
config.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 Bybit configuration.
17
18use nautilus_core::{python::to_pyvalue_err, string::secret::SecretString};
19use nautilus_model::identifiers::AccountId;
20use nautilus_network::websocket::TransportBackend;
21use pyo3::{PyResult, pymethods};
22
23use crate::{
24    common::{
25        enums::{BybitEnvironment, BybitMarginMode, BybitProductType},
26        parse::parse_smp_type,
27    },
28    config::{BybitDataClientConfig, BybitExecutionClientConfig},
29};
30
31#[pymethods]
32#[pyo3_stub_gen::derive::gen_stub_pymethods]
33impl BybitDataClientConfig {
34    /// Configuration for the Bybit live data client.
35    #[new]
36    #[pyo3(signature = (
37        product_types = None,
38        environment = None,
39        api_key = None,
40        api_secret = None,
41        base_url_http = None,
42        base_url_ws_public = None,
43        base_url_ws_private = None,
44        proxy_url = None,
45        http_timeout_secs = None,
46        max_retries = None,
47        retry_delay_initial_ms = None,
48        retry_delay_max_ms = None,
49        heartbeat_interval_secs = None,
50        recv_window_ms = None,
51        update_instruments_interval_mins = None,
52        instrument_status_poll_secs = None,
53        transport_backend = None,
54        book_snapshot_timeout_secs = None,
55        bars_timestamp_on_close = None,
56    ))]
57    #[expect(clippy::too_many_arguments)]
58    fn py_new(
59        product_types: Option<Vec<BybitProductType>>,
60        environment: Option<BybitEnvironment>,
61        api_key: Option<String>,
62        api_secret: Option<String>,
63        base_url_http: Option<String>,
64        base_url_ws_public: Option<String>,
65        base_url_ws_private: Option<String>,
66        proxy_url: Option<String>,
67        http_timeout_secs: Option<u64>,
68        max_retries: Option<u32>,
69        retry_delay_initial_ms: Option<u64>,
70        retry_delay_max_ms: Option<u64>,
71        heartbeat_interval_secs: Option<u64>,
72        recv_window_ms: Option<u64>,
73        update_instruments_interval_mins: Option<u64>,
74        instrument_status_poll_secs: Option<u64>,
75        transport_backend: Option<TransportBackend>,
76        book_snapshot_timeout_secs: Option<u64>,
77        bars_timestamp_on_close: Option<bool>,
78    ) -> Self {
79        let defaults = Self::default();
80        Self {
81            api_key: api_key.map(SecretString::from),
82            api_secret: api_secret.map(SecretString::from),
83            product_types: product_types.unwrap_or(defaults.product_types),
84            environment: environment.unwrap_or(defaults.environment),
85            base_url_http,
86            base_url_ws_public,
87            base_url_ws_private,
88            proxy_url: proxy_url.map(SecretString::from),
89            http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
90            max_retries: max_retries.unwrap_or(defaults.max_retries),
91            retry_delay_initial_ms: retry_delay_initial_ms
92                .unwrap_or(defaults.retry_delay_initial_ms),
93            retry_delay_max_ms: retry_delay_max_ms.unwrap_or(defaults.retry_delay_max_ms),
94            heartbeat_interval_secs: heartbeat_interval_secs
95                .unwrap_or(defaults.heartbeat_interval_secs),
96            recv_window_ms: recv_window_ms.unwrap_or(defaults.recv_window_ms),
97            update_instruments_interval_mins: update_instruments_interval_mins
98                .or(defaults.update_instruments_interval_mins),
99            instrument_poll_interval_secs: instrument_status_poll_secs
100                .or(defaults.instrument_poll_interval_secs),
101            book_snapshot_timeout_secs: book_snapshot_timeout_secs
102                .unwrap_or(defaults.book_snapshot_timeout_secs),
103            transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
104            bars_timestamp_on_close: bars_timestamp_on_close
105                .unwrap_or(defaults.bars_timestamp_on_close),
106        }
107    }
108
109    #[getter]
110    const fn instrument_status_poll_secs(&self) -> Option<u64> {
111        self.instrument_poll_interval_secs
112    }
113
114    #[getter]
115    const fn has_proxy_url(&self) -> bool {
116        self.proxy_url.is_some()
117    }
118
119    fn __repr__(&self) -> String {
120        stringify!(BybitDataClientConfig).to_string()
121    }
122}
123
124#[pymethods]
125#[pyo3_stub_gen::derive::gen_stub_pymethods]
126impl BybitExecutionClientConfig {
127    /// Configuration for the Bybit live execution client.
128    #[new]
129    #[pyo3(signature = (
130        product_types = None,
131        environment = None,
132        api_key = None,
133        api_secret = None,
134        base_url_http = None,
135        base_url_ws_private = None,
136        base_url_ws_trade = None,
137        proxy_url = None,
138        http_timeout_secs = None,
139        max_retries = None,
140        retry_delay_initial_ms = None,
141        retry_delay_max_ms = None,
142        heartbeat_interval_secs = None,
143        auth_timeout_secs = None,
144        recv_window_ms = None,
145        account_id = None,
146        use_spot_position_reports = None,
147        auto_repay_spot_borrows = None,
148        margin_mode = None,
149        smp_type = None,
150        transport_backend = None,
151    ))]
152    #[expect(clippy::too_many_arguments)]
153    fn py_new(
154        product_types: Option<Vec<BybitProductType>>,
155        environment: Option<BybitEnvironment>,
156        api_key: Option<String>,
157        api_secret: Option<String>,
158        base_url_http: Option<String>,
159        base_url_ws_private: Option<String>,
160        base_url_ws_trade: Option<String>,
161        proxy_url: Option<String>,
162        http_timeout_secs: Option<u64>,
163        max_retries: Option<u32>,
164        retry_delay_initial_ms: Option<u64>,
165        retry_delay_max_ms: Option<u64>,
166        heartbeat_interval_secs: Option<u64>,
167        auth_timeout_secs: Option<u64>,
168        recv_window_ms: Option<u64>,
169        account_id: Option<AccountId>,
170        use_spot_position_reports: Option<bool>,
171        auto_repay_spot_borrows: Option<bool>,
172        margin_mode: Option<BybitMarginMode>,
173        smp_type: Option<String>,
174        transport_backend: Option<TransportBackend>,
175    ) -> PyResult<Self> {
176        let smp_type = smp_type
177            .map(|value| parse_smp_type(&value))
178            .transpose()
179            .map_err(to_pyvalue_err)?;
180
181        let defaults = Self::default();
182        Ok(Self {
183            api_key: api_key.map(SecretString::from),
184            api_secret: api_secret.map(SecretString::from),
185            product_types: product_types.unwrap_or(defaults.product_types),
186            environment: environment.unwrap_or(defaults.environment),
187            base_url_http,
188            base_url_ws_private,
189            base_url_ws_trade,
190            proxy_url: proxy_url.map(SecretString::from),
191            http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
192            max_retries: max_retries.unwrap_or(defaults.max_retries),
193            retry_delay_initial_ms: retry_delay_initial_ms
194                .unwrap_or(defaults.retry_delay_initial_ms),
195            retry_delay_max_ms: retry_delay_max_ms.unwrap_or(defaults.retry_delay_max_ms),
196            heartbeat_interval_secs: heartbeat_interval_secs
197                .unwrap_or(defaults.heartbeat_interval_secs),
198            auth_timeout_secs,
199            recv_window_ms: recv_window_ms.unwrap_or(defaults.recv_window_ms),
200            account_id,
201            use_spot_position_reports: use_spot_position_reports
202                .unwrap_or(defaults.use_spot_position_reports),
203            auto_repay_spot_borrows: auto_repay_spot_borrows
204                .unwrap_or(defaults.auto_repay_spot_borrows),
205            futures_leverages: None,
206            position_mode: None,
207            margin_mode,
208            smp_type,
209            transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
210        })
211    }
212
213    #[getter]
214    #[pyo3(name = "smp_type")]
215    fn py_smp_type(&self) -> Option<String> {
216        self.smp_type.map(|smp_type| smp_type.as_ref().to_string())
217    }
218
219    #[getter]
220    const fn has_proxy_url(&self) -> bool {
221        self.proxy_url.is_some()
222    }
223
224    fn __repr__(&self) -> String {
225        stringify!(BybitExecutionClientConfig).to_string()
226    }
227}