1use nautilus_core::python::to_pyvalue_err;
19use nautilus_model::{
20 enums::{OrderSide, OrderType, TimeInForce},
21 identifiers::{AccountId, ClientOrderId, InstrumentId, VenueOrderId},
22 python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
23 types::{Price, Quantity},
24};
25use pyo3::{conversion::IntoPyObjectExt, prelude::*, types::PyList};
26
27use crate::{common::enums::BybitProductType, http::client::BybitHttpClient};
28
29#[pymethods]
30impl BybitHttpClient {
31 #[new]
32 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, timeout_secs=None, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None))]
33 #[allow(clippy::too_many_arguments)]
34 fn py_new(
35 api_key: Option<String>,
36 api_secret: Option<String>,
37 base_url: Option<String>,
38 timeout_secs: Option<u64>,
39 max_retries: Option<u32>,
40 retry_delay_ms: Option<u64>,
41 retry_delay_max_ms: Option<u64>,
42 ) -> PyResult<Self> {
43 let timeout = timeout_secs.or(Some(60));
44
45 let key = api_key.or_else(|| std::env::var("BYBIT_API_KEY").ok());
47 let secret = api_secret.or_else(|| std::env::var("BYBIT_API_SECRET").ok());
48
49 if let (Some(k), Some(s)) = (key, secret) {
50 Self::with_credentials(
51 k,
52 s,
53 base_url,
54 timeout,
55 max_retries,
56 retry_delay_ms,
57 retry_delay_max_ms,
58 )
59 .map_err(to_pyvalue_err)
60 } else {
61 Self::new(
62 base_url,
63 timeout,
64 max_retries,
65 retry_delay_ms,
66 retry_delay_max_ms,
67 )
68 .map_err(to_pyvalue_err)
69 }
70 }
71
72 #[getter]
73 #[pyo3(name = "base_url")]
74 #[must_use]
75 pub fn py_base_url(&self) -> &str {
76 self.base_url()
77 }
78
79 #[getter]
80 #[pyo3(name = "api_key")]
81 #[must_use]
82 pub fn py_api_key(&self) -> Option<&str> {
83 self.credential().map(|c| c.api_key()).map(|u| u.as_str())
84 }
85
86 #[pyo3(name = "add_instrument")]
87 fn py_add_instrument(&self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
88 let inst_any = pyobject_to_instrument_any(py, instrument)?;
89 self.add_instrument(inst_any);
90 Ok(())
91 }
92
93 #[pyo3(name = "cancel_all_requests")]
94 fn py_cancel_all_requests(&self) {
95 self.cancel_all_requests();
96 }
97
98 #[pyo3(name = "request_instruments")]
99 #[pyo3(signature = (product_type, symbol=None))]
100 fn py_request_instruments<'py>(
101 &self,
102 py: Python<'py>,
103 product_type: BybitProductType,
104 symbol: Option<String>,
105 ) -> PyResult<Bound<'py, PyAny>> {
106 let client = self.clone();
107
108 pyo3_async_runtimes::tokio::future_into_py(py, async move {
109 let instruments = client
110 .request_instruments(product_type, symbol)
111 .await
112 .map_err(to_pyvalue_err)?;
113
114 Python::attach(|py| {
115 let py_instruments: PyResult<Vec<_>> = instruments
116 .into_iter()
117 .map(|inst| instrument_any_to_pyobject(py, inst))
118 .collect();
119 let pylist = PyList::new(py, py_instruments?)
120 .unwrap()
121 .into_any()
122 .unbind();
123 Ok(pylist)
124 })
125 })
126 }
127
128 #[pyo3(name = "submit_order")]
129 #[pyo3(signature = (
130 product_type,
131 instrument_id,
132 client_order_id,
133 order_side,
134 order_type,
135 quantity,
136 time_in_force,
137 price = None,
138 reduce_only = false
139 ))]
140 #[allow(clippy::too_many_arguments)]
141 fn py_submit_order<'py>(
142 &self,
143 py: Python<'py>,
144 product_type: BybitProductType,
145 instrument_id: InstrumentId,
146 client_order_id: ClientOrderId,
147 order_side: OrderSide,
148 order_type: OrderType,
149 quantity: Quantity,
150 time_in_force: TimeInForce,
151 price: Option<Price>,
152 reduce_only: bool,
153 ) -> PyResult<Bound<'py, PyAny>> {
154 let client = self.clone();
155
156 pyo3_async_runtimes::tokio::future_into_py(py, async move {
157 let report = client
158 .submit_order(
159 product_type,
160 instrument_id,
161 client_order_id,
162 order_side,
163 order_type,
164 quantity,
165 time_in_force,
166 price,
167 reduce_only,
168 )
169 .await
170 .map_err(to_pyvalue_err)?;
171
172 Python::attach(|py| report.into_py_any(py))
173 })
174 }
175
176 #[pyo3(name = "modify_order")]
177 #[pyo3(signature = (
178 product_type,
179 instrument_id,
180 client_order_id=None,
181 venue_order_id=None,
182 quantity=None,
183 price=None
184 ))]
185 #[allow(clippy::too_many_arguments)]
186 fn py_modify_order<'py>(
187 &self,
188 py: Python<'py>,
189 product_type: BybitProductType,
190 instrument_id: InstrumentId,
191 client_order_id: Option<ClientOrderId>,
192 venue_order_id: Option<VenueOrderId>,
193 quantity: Option<Quantity>,
194 price: Option<Price>,
195 ) -> PyResult<Bound<'py, PyAny>> {
196 let client = self.clone();
197
198 pyo3_async_runtimes::tokio::future_into_py(py, async move {
199 let report = client
200 .modify_order(
201 product_type,
202 instrument_id,
203 client_order_id,
204 venue_order_id,
205 quantity,
206 price,
207 )
208 .await
209 .map_err(to_pyvalue_err)?;
210
211 Python::attach(|py| report.into_py_any(py))
212 })
213 }
214
215 #[pyo3(name = "cancel_order")]
216 #[pyo3(signature = (product_type, instrument_id, client_order_id=None, venue_order_id=None))]
217 fn py_cancel_order<'py>(
218 &self,
219 py: Python<'py>,
220 product_type: BybitProductType,
221 instrument_id: InstrumentId,
222 client_order_id: Option<ClientOrderId>,
223 venue_order_id: Option<VenueOrderId>,
224 ) -> PyResult<Bound<'py, PyAny>> {
225 let client = self.clone();
226
227 pyo3_async_runtimes::tokio::future_into_py(py, async move {
228 let report = client
229 .cancel_order(product_type, instrument_id, client_order_id, venue_order_id)
230 .await
231 .map_err(to_pyvalue_err)?;
232
233 Python::attach(|py| report.into_py_any(py))
234 })
235 }
236
237 #[pyo3(name = "cancel_all_orders")]
238 fn py_cancel_all_orders<'py>(
239 &self,
240 py: Python<'py>,
241 product_type: BybitProductType,
242 instrument_id: InstrumentId,
243 ) -> PyResult<Bound<'py, PyAny>> {
244 let client = self.clone();
245
246 pyo3_async_runtimes::tokio::future_into_py(py, async move {
247 let reports = client
248 .cancel_all_orders(product_type, instrument_id)
249 .await
250 .map_err(to_pyvalue_err)?;
251
252 Python::attach(|py| {
253 let py_reports: PyResult<Vec<_>> = reports
254 .into_iter()
255 .map(|report| report.into_py_any(py))
256 .collect();
257 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
258 Ok(pylist)
259 })
260 })
261 }
262
263 #[pyo3(name = "query_order")]
264 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
265 fn py_query_order<'py>(
266 &self,
267 py: Python<'py>,
268 account_id: AccountId,
269 product_type: BybitProductType,
270 instrument_id: InstrumentId,
271 client_order_id: Option<ClientOrderId>,
272 venue_order_id: Option<VenueOrderId>,
273 ) -> PyResult<Bound<'py, PyAny>> {
274 let client = self.clone();
275
276 pyo3_async_runtimes::tokio::future_into_py(py, async move {
277 match client
278 .query_order(
279 account_id,
280 product_type,
281 instrument_id,
282 client_order_id,
283 venue_order_id,
284 )
285 .await
286 {
287 Ok(Some(report)) => Python::attach(|py| report.into_py_any(py)),
288 Ok(None) => Ok(Python::attach(|py| py.None())),
289 Err(e) => Err(to_pyvalue_err(e)),
290 }
291 })
292 }
293
294 #[pyo3(name = "request_trades")]
295 #[pyo3(signature = (product_type, instrument_id, limit=None))]
296 fn py_request_trades<'py>(
297 &self,
298 py: Python<'py>,
299 product_type: BybitProductType,
300 instrument_id: InstrumentId,
301 limit: Option<u32>,
302 ) -> PyResult<Bound<'py, PyAny>> {
303 let client = self.clone();
304
305 pyo3_async_runtimes::tokio::future_into_py(py, async move {
306 let trades = client
307 .request_trades(product_type, instrument_id, limit)
308 .await
309 .map_err(to_pyvalue_err)?;
310
311 Python::attach(|py| {
312 let py_trades: PyResult<Vec<_>> = trades
313 .into_iter()
314 .map(|trade| trade.into_py_any(py))
315 .collect();
316 let pylist = PyList::new(py, py_trades?).unwrap().into_any().unbind();
317 Ok(pylist)
318 })
319 })
320 }
321
322 #[pyo3(name = "request_bars")]
323 #[pyo3(signature = (product_type, bar_type, start=None, end=None, limit=None))]
324 fn py_request_bars<'py>(
325 &self,
326 py: Python<'py>,
327 product_type: BybitProductType,
328 bar_type: nautilus_model::data::BarType,
329 start: Option<i64>,
330 end: Option<i64>,
331 limit: Option<u32>,
332 ) -> PyResult<Bound<'py, PyAny>> {
333 let client = self.clone();
334
335 pyo3_async_runtimes::tokio::future_into_py(py, async move {
336 let bars = client
337 .request_bars(product_type, bar_type, start, end, limit)
338 .await
339 .map_err(to_pyvalue_err)?;
340
341 Python::attach(|py| {
342 let py_bars: PyResult<Vec<_>> =
343 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
344 let pylist = PyList::new(py, py_bars?).unwrap().into_any().unbind();
345 Ok(pylist)
346 })
347 })
348 }
349
350 #[pyo3(name = "request_fee_rates")]
351 #[pyo3(signature = (product_type, symbol=None, base_coin=None))]
352 fn py_request_fee_rates<'py>(
353 &self,
354 py: Python<'py>,
355 product_type: BybitProductType,
356 symbol: Option<String>,
357 base_coin: Option<String>,
358 ) -> PyResult<Bound<'py, PyAny>> {
359 let client = self.clone();
360
361 pyo3_async_runtimes::tokio::future_into_py(py, async move {
362 let fee_rates = client
363 .request_fee_rates(product_type, symbol, base_coin)
364 .await
365 .map_err(to_pyvalue_err)?;
366
367 Python::attach(|py| {
368 let py_fee_rates: PyResult<Vec<_>> = fee_rates
369 .into_iter()
370 .map(|rate| Py::new(py, rate))
371 .collect();
372 let pylist = PyList::new(py, py_fee_rates?).unwrap().into_any().unbind();
373 Ok(pylist)
374 })
375 })
376 }
377
378 #[pyo3(name = "request_account_state")]
379 fn py_request_account_state<'py>(
380 &self,
381 py: Python<'py>,
382 account_type: crate::common::enums::BybitAccountType,
383 account_id: AccountId,
384 ) -> PyResult<Bound<'py, PyAny>> {
385 let client = self.clone();
386
387 pyo3_async_runtimes::tokio::future_into_py(py, async move {
388 let account_state = client
389 .request_account_state(account_type, account_id)
390 .await
391 .map_err(to_pyvalue_err)?;
392
393 Python::attach(|py| account_state.into_py_any(py))
394 })
395 }
396
397 #[pyo3(name = "request_order_status_reports")]
398 #[pyo3(signature = (account_id, product_type, instrument_id=None, open_only=false, limit=None))]
399 fn py_request_order_status_reports<'py>(
400 &self,
401 py: Python<'py>,
402 account_id: AccountId,
403 product_type: BybitProductType,
404 instrument_id: Option<InstrumentId>,
405 open_only: bool,
406 limit: Option<u32>,
407 ) -> PyResult<Bound<'py, PyAny>> {
408 let client = self.clone();
409
410 pyo3_async_runtimes::tokio::future_into_py(py, async move {
411 let reports = client
412 .request_order_status_reports(
413 account_id,
414 product_type,
415 instrument_id,
416 open_only,
417 limit,
418 )
419 .await
420 .map_err(to_pyvalue_err)?;
421
422 Python::attach(|py| {
423 let py_reports: PyResult<Vec<_>> = reports
424 .into_iter()
425 .map(|report| report.into_py_any(py))
426 .collect();
427 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
428 Ok(pylist)
429 })
430 })
431 }
432
433 #[pyo3(name = "request_fill_reports")]
434 #[pyo3(signature = (account_id, product_type, instrument_id=None, start=None, end=None, limit=None))]
435 #[allow(clippy::too_many_arguments)]
436 fn py_request_fill_reports<'py>(
437 &self,
438 py: Python<'py>,
439 account_id: AccountId,
440 product_type: BybitProductType,
441 instrument_id: Option<InstrumentId>,
442 start: Option<i64>,
443 end: Option<i64>,
444 limit: Option<u32>,
445 ) -> PyResult<Bound<'py, PyAny>> {
446 let client = self.clone();
447
448 pyo3_async_runtimes::tokio::future_into_py(py, async move {
449 let reports = client
450 .request_fill_reports(account_id, product_type, instrument_id, start, end, limit)
451 .await
452 .map_err(to_pyvalue_err)?;
453
454 Python::attach(|py| {
455 let py_reports: PyResult<Vec<_>> = reports
456 .into_iter()
457 .map(|report| report.into_py_any(py))
458 .collect();
459 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
460 Ok(pylist)
461 })
462 })
463 }
464
465 #[pyo3(name = "request_position_status_reports")]
466 #[pyo3(signature = (account_id, product_type, instrument_id=None))]
467 fn py_request_position_status_reports<'py>(
468 &self,
469 py: Python<'py>,
470 account_id: AccountId,
471 product_type: BybitProductType,
472 instrument_id: Option<InstrumentId>,
473 ) -> PyResult<Bound<'py, PyAny>> {
474 let client = self.clone();
475
476 pyo3_async_runtimes::tokio::future_into_py(py, async move {
477 let reports = client
478 .request_position_status_reports(account_id, product_type, instrument_id)
479 .await
480 .map_err(to_pyvalue_err)?;
481
482 Python::attach(|py| {
483 let py_reports: PyResult<Vec<_>> = reports
484 .into_iter()
485 .map(|report| report.into_py_any(py))
486 .collect();
487 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
488 Ok(pylist)
489 })
490 })
491 }
492}