1use chrono::{DateTime, Utc};
19use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
20use nautilus_model::{
21 data::BarType,
22 enums::{OrderSide, OrderType, TimeInForce},
23 identifiers::{AccountId, ClientOrderId, InstrumentId, VenueOrderId},
24 python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
25 types::{Price, Quantity},
26};
27use pyo3::{conversion::IntoPyObjectExt, prelude::*, types::PyList};
28
29use crate::{
30 common::enums::{
31 BybitMarginMode, BybitOpenOnly, BybitOrderFilter, BybitPositionMode, BybitProductType,
32 },
33 http::{
34 client::{BybitHttpClient, BybitRawHttpClient},
35 error::BybitHttpError,
36 models::BybitOrderCursorList,
37 },
38};
39
40#[pymethods]
41impl BybitRawHttpClient {
42 #[new]
43 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, demo=false, testnet=false, timeout_secs=None, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None, recv_window_ms=None, proxy_url=None))]
44 #[allow(clippy::too_many_arguments)]
45 fn py_new(
46 api_key: Option<String>,
47 api_secret: Option<String>,
48 base_url: Option<String>,
49 demo: bool,
50 testnet: bool,
51 timeout_secs: Option<u64>,
52 max_retries: Option<u32>,
53 retry_delay_ms: Option<u64>,
54 retry_delay_max_ms: Option<u64>,
55 recv_window_ms: Option<u64>,
56 proxy_url: Option<String>,
57 ) -> PyResult<Self> {
58 Self::new_with_env(
59 api_key,
60 api_secret,
61 base_url,
62 demo,
63 testnet,
64 timeout_secs.or(Some(60)),
65 max_retries,
66 retry_delay_ms,
67 retry_delay_max_ms,
68 recv_window_ms,
69 proxy_url,
70 )
71 .map_err(to_pyvalue_err)
72 }
73
74 #[getter]
75 #[pyo3(name = "base_url")]
76 #[must_use]
77 pub fn py_base_url(&self) -> &str {
78 self.base_url()
79 }
80
81 #[getter]
82 #[pyo3(name = "api_key")]
83 #[must_use]
84 pub fn py_api_key(&self) -> Option<String> {
85 self.credential().map(|c| c.api_key().to_string())
86 }
87
88 #[getter]
89 #[pyo3(name = "recv_window_ms")]
90 #[must_use]
91 pub fn py_recv_window_ms(&self) -> u64 {
92 self.recv_window_ms()
93 }
94
95 #[pyo3(name = "cancel_all_requests")]
96 fn py_cancel_all_requests(&self) {
97 self.cancel_all_requests();
98 }
99
100 #[pyo3(name = "get_server_time")]
101 fn py_get_server_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
102 let client = self.clone();
103
104 pyo3_async_runtimes::tokio::future_into_py(py, async move {
105 let response = client.get_server_time().await.map_err(to_pyvalue_err)?;
106
107 Python::attach(|py| {
108 let server_time = Py::new(py, response.result)?;
109 Ok(server_time.into_any())
110 })
111 })
112 }
113
114 #[pyo3(name = "get_open_orders")]
115 #[pyo3(signature = (category, symbol=None, base_coin=None, settle_coin=None, order_id=None, order_link_id=None, open_only=None, order_filter=None, limit=None, cursor=None))]
116 #[allow(clippy::too_many_arguments)]
117 fn py_get_open_orders<'py>(
118 &self,
119 py: Python<'py>,
120 category: BybitProductType,
121 symbol: Option<String>,
122 base_coin: Option<String>,
123 settle_coin: Option<String>,
124 order_id: Option<String>,
125 order_link_id: Option<String>,
126 open_only: Option<BybitOpenOnly>,
127 order_filter: Option<BybitOrderFilter>,
128 limit: Option<u32>,
129 cursor: Option<String>,
130 ) -> PyResult<Bound<'py, PyAny>> {
131 let client = self.clone();
132
133 pyo3_async_runtimes::tokio::future_into_py(py, async move {
134 let response = client
135 .get_open_orders(
136 category,
137 symbol,
138 base_coin,
139 settle_coin,
140 order_id,
141 order_link_id,
142 open_only,
143 order_filter,
144 limit,
145 cursor,
146 )
147 .await
148 .map_err(to_pyvalue_err)?;
149
150 Python::attach(|py| {
151 let open_orders = BybitOrderCursorList::from(response.result);
152 let py_open_orders = Py::new(py, open_orders)?;
153 Ok(py_open_orders.into_any())
154 })
155 })
156 }
157}
158
159#[pymethods]
160impl BybitHttpClient {
161 #[new]
162 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, demo=false, testnet=false, timeout_secs=None, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None, recv_window_ms=None, proxy_url=None))]
163 #[allow(clippy::too_many_arguments)]
164 fn py_new(
165 api_key: Option<String>,
166 api_secret: Option<String>,
167 base_url: Option<String>,
168 demo: bool,
169 testnet: bool,
170 timeout_secs: Option<u64>,
171 max_retries: Option<u32>,
172 retry_delay_ms: Option<u64>,
173 retry_delay_max_ms: Option<u64>,
174 recv_window_ms: Option<u64>,
175 proxy_url: Option<String>,
176 ) -> PyResult<Self> {
177 Self::new_with_env(
178 api_key,
179 api_secret,
180 base_url,
181 demo,
182 testnet,
183 timeout_secs.or(Some(60)),
184 max_retries,
185 retry_delay_ms,
186 retry_delay_max_ms,
187 recv_window_ms,
188 proxy_url,
189 )
190 .map_err(to_pyvalue_err)
191 }
192
193 #[getter]
194 #[pyo3(name = "base_url")]
195 #[must_use]
196 pub fn py_base_url(&self) -> &str {
197 self.base_url()
198 }
199
200 #[getter]
201 #[pyo3(name = "api_key")]
202 #[must_use]
203 pub fn py_api_key(&self) -> Option<&str> {
204 self.credential().map(|c| c.api_key()).map(|u| u.as_str())
205 }
206
207 #[getter]
208 #[pyo3(name = "api_key_masked")]
209 #[must_use]
210 pub fn py_api_key_masked(&self) -> Option<String> {
211 self.credential().map(|c| c.api_key_masked())
212 }
213
214 #[pyo3(name = "cache_instrument")]
215 fn py_cache_instrument(&self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
216 let inst_any = pyobject_to_instrument_any(py, instrument)?;
217 self.cache_instrument(inst_any);
218 Ok(())
219 }
220
221 #[pyo3(name = "cancel_all_requests")]
222 fn py_cancel_all_requests(&self) {
223 self.cancel_all_requests();
224 }
225
226 #[pyo3(name = "set_use_spot_position_reports")]
227 fn py_set_use_spot_position_reports(&self, value: bool) {
228 self.set_use_spot_position_reports(value);
229 }
230
231 #[pyo3(name = "set_margin_mode")]
232 fn py_set_margin_mode<'py>(
233 &self,
234 py: Python<'py>,
235 margin_mode: BybitMarginMode,
236 ) -> PyResult<Bound<'py, PyAny>> {
237 let client = self.clone();
238
239 pyo3_async_runtimes::tokio::future_into_py(py, async move {
240 client
241 .set_margin_mode(margin_mode)
242 .await
243 .map_err(to_pyvalue_err)?;
244
245 Python::attach(|py| Ok(py.None()))
246 })
247 }
248
249 #[pyo3(name = "get_account_details")]
250 fn py_get_account_details<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
251 let client = self.clone();
252
253 pyo3_async_runtimes::tokio::future_into_py(py, async move {
254 let response = client.get_account_details().await.map_err(to_pyvalue_err)?;
255
256 Python::attach(|py| {
257 let account_details = Py::new(py, response.result)?;
258 Ok(account_details.into_any())
259 })
260 })
261 }
262
263 #[pyo3(name = "set_leverage")]
264 #[pyo3(signature = (product_type, symbol, buy_leverage, sell_leverage))]
265 fn py_set_leverage<'py>(
266 &self,
267 py: Python<'py>,
268 product_type: BybitProductType,
269 symbol: String,
270 buy_leverage: String,
271 sell_leverage: String,
272 ) -> PyResult<Bound<'py, PyAny>> {
273 let client = self.clone();
274
275 pyo3_async_runtimes::tokio::future_into_py(py, async move {
276 client
277 .set_leverage(product_type, &symbol, &buy_leverage, &sell_leverage)
278 .await
279 .map_err(to_pyvalue_err)?;
280
281 Python::attach(|py| Ok(py.None()))
282 })
283 }
284
285 #[pyo3(name = "switch_mode")]
286 #[pyo3(signature = (product_type, mode, symbol=None, coin=None))]
287 fn py_switch_mode<'py>(
288 &self,
289 py: Python<'py>,
290 product_type: BybitProductType,
291 mode: BybitPositionMode,
292 symbol: Option<String>,
293 coin: Option<String>,
294 ) -> PyResult<Bound<'py, PyAny>> {
295 let client = self.clone();
296
297 pyo3_async_runtimes::tokio::future_into_py(py, async move {
298 client
299 .switch_mode(product_type, mode, symbol, coin)
300 .await
301 .map_err(to_pyvalue_err)?;
302
303 Python::attach(|py| Ok(py.None()))
304 })
305 }
306
307 #[pyo3(name = "get_spot_borrow_amount")]
308 fn py_get_spot_borrow_amount<'py>(
309 &self,
310 py: Python<'py>,
311 coin: String,
312 ) -> PyResult<Bound<'py, PyAny>> {
313 let client = self.clone();
314
315 pyo3_async_runtimes::tokio::future_into_py(py, async move {
316 let borrow_amount = client
317 .get_spot_borrow_amount(&coin)
318 .await
319 .map_err(to_pyvalue_err)?;
320
321 Ok(borrow_amount)
322 })
323 }
324
325 #[pyo3(name = "borrow_spot")]
326 #[pyo3(signature = (coin, amount))]
327 fn py_borrow_spot<'py>(
328 &self,
329 py: Python<'py>,
330 coin: String,
331 amount: Quantity,
332 ) -> PyResult<Bound<'py, PyAny>> {
333 let client = self.clone();
334
335 pyo3_async_runtimes::tokio::future_into_py(py, async move {
336 client
337 .borrow_spot(&coin, amount)
338 .await
339 .map_err(to_pyvalue_err)?;
340
341 Python::attach(|py| Ok(py.None()))
342 })
343 }
344
345 #[pyo3(name = "repay_spot_borrow")]
346 #[pyo3(signature = (coin, amount=None))]
347 fn py_repay_spot_borrow<'py>(
348 &self,
349 py: Python<'py>,
350 coin: String,
351 amount: Option<Quantity>,
352 ) -> PyResult<Bound<'py, PyAny>> {
353 let client = self.clone();
354
355 pyo3_async_runtimes::tokio::future_into_py(py, async move {
356 client
357 .repay_spot_borrow(&coin, amount)
358 .await
359 .map_err(to_pyvalue_err)?;
360
361 Python::attach(|py| Ok(py.None()))
362 })
363 }
364
365 #[pyo3(name = "request_instruments")]
366 #[pyo3(signature = (product_type, symbol=None))]
367 fn py_request_instruments<'py>(
368 &self,
369 py: Python<'py>,
370 product_type: BybitProductType,
371 symbol: Option<String>,
372 ) -> PyResult<Bound<'py, PyAny>> {
373 let client = self.clone();
374
375 pyo3_async_runtimes::tokio::future_into_py(py, async move {
376 let instruments = client
377 .request_instruments(product_type, symbol)
378 .await
379 .map_err(to_pyvalue_err)?;
380
381 Python::attach(|py| {
382 let py_instruments: PyResult<Vec<_>> = instruments
383 .into_iter()
384 .map(|inst| instrument_any_to_pyobject(py, inst))
385 .collect();
386 let pylist = PyList::new(py, py_instruments?)
387 .unwrap()
388 .into_any()
389 .unbind();
390 Ok(pylist)
391 })
392 })
393 }
394
395 #[pyo3(name = "request_tickers")]
396 fn py_request_tickers<'py>(
397 &self,
398 py: Python<'py>,
399 params: crate::python::params::BybitTickersParams,
400 ) -> PyResult<Bound<'py, PyAny>> {
401 let client = self.clone();
402
403 pyo3_async_runtimes::tokio::future_into_py(py, async move {
404 let tickers = client
405 .request_tickers(¶ms.into())
406 .await
407 .map_err(to_pyvalue_err)?;
408
409 Python::attach(|py| {
410 let py_tickers: PyResult<Vec<_>> = tickers
411 .into_iter()
412 .map(|ticker| Py::new(py, ticker))
413 .collect();
414 let pylist = PyList::new(py, py_tickers?).unwrap().into_any().unbind();
415 Ok(pylist)
416 })
417 })
418 }
419
420 #[pyo3(name = "submit_order")]
421 #[pyo3(signature = (
422 account_id,
423 product_type,
424 instrument_id,
425 client_order_id,
426 order_side,
427 order_type,
428 quantity,
429 time_in_force,
430 price = None,
431 reduce_only = false,
432 is_leverage = false
433 ))]
434 #[allow(clippy::too_many_arguments)]
435 fn py_submit_order<'py>(
436 &self,
437 py: Python<'py>,
438 account_id: AccountId,
439 product_type: BybitProductType,
440 instrument_id: InstrumentId,
441 client_order_id: ClientOrderId,
442 order_side: OrderSide,
443 order_type: OrderType,
444 quantity: Quantity,
445 time_in_force: TimeInForce,
446 price: Option<Price>,
447 reduce_only: bool,
448 is_leverage: bool,
449 ) -> PyResult<Bound<'py, PyAny>> {
450 let client = self.clone();
451
452 pyo3_async_runtimes::tokio::future_into_py(py, async move {
453 let report = client
454 .submit_order(
455 account_id,
456 product_type,
457 instrument_id,
458 client_order_id,
459 order_side,
460 order_type,
461 quantity,
462 time_in_force,
463 price,
464 reduce_only,
465 is_leverage,
466 )
467 .await
468 .map_err(to_pyvalue_err)?;
469
470 Python::attach(|py| report.into_py_any(py))
471 })
472 }
473
474 #[pyo3(name = "modify_order")]
475 #[pyo3(signature = (
476 account_id,
477 product_type,
478 instrument_id,
479 client_order_id=None,
480 venue_order_id=None,
481 quantity=None,
482 price=None
483 ))]
484 #[allow(clippy::too_many_arguments)]
485 fn py_modify_order<'py>(
486 &self,
487 py: Python<'py>,
488 account_id: AccountId,
489 product_type: BybitProductType,
490 instrument_id: InstrumentId,
491 client_order_id: Option<ClientOrderId>,
492 venue_order_id: Option<VenueOrderId>,
493 quantity: Option<Quantity>,
494 price: Option<Price>,
495 ) -> PyResult<Bound<'py, PyAny>> {
496 let client = self.clone();
497
498 pyo3_async_runtimes::tokio::future_into_py(py, async move {
499 let report = client
500 .modify_order(
501 account_id,
502 product_type,
503 instrument_id,
504 client_order_id,
505 venue_order_id,
506 quantity,
507 price,
508 )
509 .await
510 .map_err(to_pyvalue_err)?;
511
512 Python::attach(|py| report.into_py_any(py))
513 })
514 }
515
516 #[pyo3(name = "cancel_order")]
517 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
518 fn py_cancel_order<'py>(
519 &self,
520 py: Python<'py>,
521 account_id: AccountId,
522 product_type: BybitProductType,
523 instrument_id: InstrumentId,
524 client_order_id: Option<ClientOrderId>,
525 venue_order_id: Option<VenueOrderId>,
526 ) -> PyResult<Bound<'py, PyAny>> {
527 let client = self.clone();
528
529 pyo3_async_runtimes::tokio::future_into_py(py, async move {
530 let report = client
531 .cancel_order(
532 account_id,
533 product_type,
534 instrument_id,
535 client_order_id,
536 venue_order_id,
537 )
538 .await
539 .map_err(to_pyvalue_err)?;
540
541 Python::attach(|py| report.into_py_any(py))
542 })
543 }
544
545 #[pyo3(name = "cancel_all_orders")]
546 fn py_cancel_all_orders<'py>(
547 &self,
548 py: Python<'py>,
549 account_id: AccountId,
550 product_type: BybitProductType,
551 instrument_id: InstrumentId,
552 ) -> PyResult<Bound<'py, PyAny>> {
553 let client = self.clone();
554
555 pyo3_async_runtimes::tokio::future_into_py(py, async move {
556 let reports = client
557 .cancel_all_orders(account_id, product_type, instrument_id)
558 .await
559 .map_err(to_pyvalue_err)?;
560
561 Python::attach(|py| {
562 let py_reports: PyResult<Vec<_>> = reports
563 .into_iter()
564 .map(|report| report.into_py_any(py))
565 .collect();
566 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
567 Ok(pylist)
568 })
569 })
570 }
571
572 #[pyo3(name = "query_order")]
573 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
574 fn py_query_order<'py>(
575 &self,
576 py: Python<'py>,
577 account_id: AccountId,
578 product_type: BybitProductType,
579 instrument_id: InstrumentId,
580 client_order_id: Option<ClientOrderId>,
581 venue_order_id: Option<VenueOrderId>,
582 ) -> PyResult<Bound<'py, PyAny>> {
583 let client = self.clone();
584
585 pyo3_async_runtimes::tokio::future_into_py(py, async move {
586 match client
587 .query_order(
588 account_id,
589 product_type,
590 instrument_id,
591 client_order_id,
592 venue_order_id,
593 )
594 .await
595 {
596 Ok(Some(report)) => Python::attach(|py| report.into_py_any(py)),
597 Ok(None) => Ok(Python::attach(|py| py.None())),
598 Err(e) => Err(to_pyvalue_err(e)),
599 }
600 })
601 }
602
603 #[pyo3(name = "request_trades")]
604 #[pyo3(signature = (product_type, instrument_id, limit=None))]
605 fn py_request_trades<'py>(
606 &self,
607 py: Python<'py>,
608 product_type: BybitProductType,
609 instrument_id: InstrumentId,
610 limit: Option<u32>,
611 ) -> PyResult<Bound<'py, PyAny>> {
612 let client = self.clone();
613
614 pyo3_async_runtimes::tokio::future_into_py(py, async move {
615 let trades = client
616 .request_trades(product_type, instrument_id, limit)
617 .await
618 .map_err(to_pyvalue_err)?;
619
620 Python::attach(|py| {
621 let py_trades: PyResult<Vec<_>> = trades
622 .into_iter()
623 .map(|trade| trade.into_py_any(py))
624 .collect();
625 let pylist = PyList::new(py, py_trades?).unwrap().into_any().unbind();
626 Ok(pylist)
627 })
628 })
629 }
630
631 #[pyo3(name = "request_bars")]
632 #[pyo3(signature = (product_type, bar_type, start=None, end=None, limit=None, timestamp_on_close=true))]
633 #[allow(clippy::too_many_arguments)]
634 fn py_request_bars<'py>(
635 &self,
636 py: Python<'py>,
637 product_type: BybitProductType,
638 bar_type: BarType,
639 start: Option<DateTime<Utc>>,
640 end: Option<DateTime<Utc>>,
641 limit: Option<u32>,
642 timestamp_on_close: bool,
643 ) -> PyResult<Bound<'py, PyAny>> {
644 let client = self.clone();
645
646 pyo3_async_runtimes::tokio::future_into_py(py, async move {
647 let bars = client
648 .request_bars(
649 product_type,
650 bar_type,
651 start,
652 end,
653 limit,
654 timestamp_on_close,
655 )
656 .await
657 .map_err(to_pyvalue_err)?;
658
659 Python::attach(|py| {
660 let py_bars: PyResult<Vec<_>> =
661 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
662 let pylist = PyList::new(py, py_bars?).unwrap().into_any().unbind();
663 Ok(pylist)
664 })
665 })
666 }
667
668 #[pyo3(name = "request_fee_rates")]
669 #[pyo3(signature = (product_type, symbol=None, base_coin=None))]
670 fn py_request_fee_rates<'py>(
671 &self,
672 py: Python<'py>,
673 product_type: BybitProductType,
674 symbol: Option<String>,
675 base_coin: Option<String>,
676 ) -> PyResult<Bound<'py, PyAny>> {
677 let client = self.clone();
678
679 pyo3_async_runtimes::tokio::future_into_py(py, async move {
680 let fee_rates = client
681 .request_fee_rates(product_type, symbol, base_coin)
682 .await
683 .map_err(to_pyvalue_err)?;
684
685 Python::attach(|py| {
686 let py_fee_rates: PyResult<Vec<_>> = fee_rates
687 .into_iter()
688 .map(|rate| Py::new(py, rate))
689 .collect();
690 let pylist = PyList::new(py, py_fee_rates?).unwrap().into_any().unbind();
691 Ok(pylist)
692 })
693 })
694 }
695
696 #[pyo3(name = "request_account_state")]
697 fn py_request_account_state<'py>(
698 &self,
699 py: Python<'py>,
700 account_type: crate::common::enums::BybitAccountType,
701 account_id: AccountId,
702 ) -> PyResult<Bound<'py, PyAny>> {
703 let client = self.clone();
704
705 pyo3_async_runtimes::tokio::future_into_py(py, async move {
706 let account_state = client
707 .request_account_state(account_type, account_id)
708 .await
709 .map_err(to_pyvalue_err)?;
710
711 Python::attach(|py| account_state.into_py_any(py))
712 })
713 }
714
715 #[pyo3(name = "request_order_status_reports")]
716 #[pyo3(signature = (account_id, product_type, instrument_id=None, open_only=false, start=None, end=None, limit=None))]
717 #[allow(clippy::too_many_arguments)]
718 fn py_request_order_status_reports<'py>(
719 &self,
720 py: Python<'py>,
721 account_id: AccountId,
722 product_type: BybitProductType,
723 instrument_id: Option<InstrumentId>,
724 open_only: bool,
725 start: Option<DateTime<Utc>>,
726 end: Option<DateTime<Utc>>,
727 limit: Option<u32>,
728 ) -> PyResult<Bound<'py, PyAny>> {
729 let client = self.clone();
730
731 pyo3_async_runtimes::tokio::future_into_py(py, async move {
732 let reports = client
733 .request_order_status_reports(
734 account_id,
735 product_type,
736 instrument_id,
737 open_only,
738 start,
739 end,
740 limit,
741 )
742 .await
743 .map_err(to_pyvalue_err)?;
744
745 Python::attach(|py| {
746 let py_reports: PyResult<Vec<_>> = reports
747 .into_iter()
748 .map(|report| report.into_py_any(py))
749 .collect();
750 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
751 Ok(pylist)
752 })
753 })
754 }
755
756 #[pyo3(name = "request_fill_reports")]
757 #[pyo3(signature = (account_id, product_type, instrument_id=None, start=None, end=None, limit=None))]
758 #[allow(clippy::too_many_arguments)]
759 fn py_request_fill_reports<'py>(
760 &self,
761 py: Python<'py>,
762 account_id: AccountId,
763 product_type: BybitProductType,
764 instrument_id: Option<InstrumentId>,
765 start: Option<i64>,
766 end: Option<i64>,
767 limit: Option<u32>,
768 ) -> PyResult<Bound<'py, PyAny>> {
769 let client = self.clone();
770
771 pyo3_async_runtimes::tokio::future_into_py(py, async move {
772 let reports = client
773 .request_fill_reports(account_id, product_type, instrument_id, start, end, limit)
774 .await
775 .map_err(to_pyvalue_err)?;
776
777 Python::attach(|py| {
778 let py_reports: PyResult<Vec<_>> = reports
779 .into_iter()
780 .map(|report| report.into_py_any(py))
781 .collect();
782 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
783 Ok(pylist)
784 })
785 })
786 }
787
788 #[pyo3(name = "request_position_status_reports")]
789 #[pyo3(signature = (account_id, product_type, instrument_id=None))]
790 fn py_request_position_status_reports<'py>(
791 &self,
792 py: Python<'py>,
793 account_id: AccountId,
794 product_type: BybitProductType,
795 instrument_id: Option<InstrumentId>,
796 ) -> PyResult<Bound<'py, PyAny>> {
797 let client = self.clone();
798
799 pyo3_async_runtimes::tokio::future_into_py(py, async move {
800 let reports = client
801 .request_position_status_reports(account_id, product_type, instrument_id)
802 .await
803 .map_err(to_pyvalue_err)?;
804
805 Python::attach(|py| {
806 let py_reports: PyResult<Vec<_>> = reports
807 .into_iter()
808 .map(|report| report.into_py_any(py))
809 .collect();
810 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
811 Ok(pylist)
812 })
813 })
814 }
815}
816
817impl From<BybitHttpError> for PyErr {
818 fn from(error: BybitHttpError) -> Self {
819 match error {
820 BybitHttpError::Canceled(msg) => to_pyruntime_err(format!("Request canceled: {msg}")),
822 BybitHttpError::NetworkError(msg) => to_pyruntime_err(format!("Network error: {msg}")),
823 BybitHttpError::UnexpectedStatus { status, body } => {
824 to_pyruntime_err(format!("Unexpected HTTP status code {status}: {body}"))
825 }
826 BybitHttpError::MissingCredentials => {
828 to_pyvalue_err("Missing credentials for authenticated request")
829 }
830 BybitHttpError::ValidationError(msg) => {
831 to_pyvalue_err(format!("Parameter validation error: {msg}"))
832 }
833 BybitHttpError::JsonError(msg) => to_pyvalue_err(format!("JSON error: {msg}")),
834 BybitHttpError::BuildError(e) => to_pyvalue_err(format!("Build error: {e}")),
835 BybitHttpError::BybitError {
836 error_code,
837 message,
838 } => to_pyvalue_err(format!("Bybit error {error_code}: {message}")),
839 }
840 }
841}