1use jiff::Timestamp;
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::{
28 conversion::IntoPyObjectExt,
29 prelude::*,
30 types::{PyDict, PyList},
31};
32use ustr::Ustr;
33
34use crate::{
35 common::{
36 enums::{
37 BybitMarginMode, BybitOpenOnly, BybitOrderFilter, BybitPositionIdx, BybitPositionMode,
38 BybitProductType,
39 },
40 parse::{parse_bbo_level, parse_bbo_side_type, parse_smp_type},
41 },
42 http::{
43 client::{BybitHttpClient, BybitRawHttpClient},
44 error::BybitHttpError,
45 models::BybitOrderCursorList,
46 query::BybitNativeTpSlParams as RustNativeTpSlParams,
47 },
48 python::params::BybitNativeTpSlParams,
49};
50
51#[pymethods]
52#[pyo3_stub_gen::derive::gen_stub_pymethods]
53impl BybitRawHttpClient {
54 #[new]
59 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, demo=false, testnet=false, timeout_secs=60, max_retries=3, retry_delay_ms=1000, retry_delay_max_ms=10_000, recv_window_ms=5_000, proxy_url=None))]
60 #[expect(clippy::too_many_arguments)]
61 fn py_new(
62 api_key: Option<String>,
63 api_secret: Option<String>,
64 base_url: Option<String>,
65 demo: bool,
66 testnet: bool,
67 timeout_secs: u64,
68 max_retries: u32,
69 retry_delay_ms: u64,
70 retry_delay_max_ms: u64,
71 recv_window_ms: u64,
72 proxy_url: Option<String>,
73 ) -> PyResult<Self> {
74 Self::new_with_env(
75 api_key,
76 api_secret,
77 base_url,
78 demo,
79 testnet,
80 timeout_secs,
81 max_retries,
82 retry_delay_ms,
83 retry_delay_max_ms,
84 recv_window_ms,
85 proxy_url,
86 )
87 .map_err(to_pyvalue_err)
88 }
89
90 #[getter]
92 #[pyo3(name = "base_url")]
93 #[must_use]
94 pub fn py_base_url(&self) -> &str {
95 self.base_url()
96 }
97
98 #[getter]
99 #[pyo3(name = "api_key")]
100 #[must_use]
101 pub fn py_api_key(&self) -> Option<String> {
102 self.credential().map(|c| c.api_key().to_string())
103 }
104
105 #[getter]
107 #[pyo3(name = "recv_window_ms")]
108 #[must_use]
109 pub fn py_recv_window_ms(&self) -> u64 {
110 self.recv_window_ms()
111 }
112
113 #[pyo3(name = "cancel_all_requests")]
115 fn py_cancel_all_requests(&self) {
116 self.cancel_all_requests();
117 }
118
119 #[pyo3(name = "get_server_time")]
129 fn py_get_server_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
130 let client = self.clone();
131
132 pyo3_async_runtimes::tokio::future_into_py(py, async move {
133 let response = client.get_server_time().await.map_err(to_pyvalue_err)?;
134
135 Python::attach(|py| {
136 let server_time = Py::new(py, response.result)?;
137 Ok(server_time.into_any())
138 })
139 })
140 }
141
142 #[pyo3(name = "get_open_orders")]
152 #[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))]
153 #[expect(clippy::too_many_arguments)]
154 fn py_get_open_orders<'py>(
155 &self,
156 py: Python<'py>,
157 category: BybitProductType,
158 symbol: Option<String>,
159 base_coin: Option<String>,
160 settle_coin: Option<String>,
161 order_id: Option<String>,
162 order_link_id: Option<String>,
163 open_only: Option<BybitOpenOnly>,
164 order_filter: Option<BybitOrderFilter>,
165 limit: Option<u32>,
166 cursor: Option<String>,
167 ) -> PyResult<Bound<'py, PyAny>> {
168 let client = self.clone();
169
170 pyo3_async_runtimes::tokio::future_into_py(py, async move {
171 let response = client
172 .get_open_orders(
173 category,
174 symbol,
175 base_coin,
176 settle_coin,
177 order_id,
178 order_link_id,
179 open_only,
180 order_filter,
181 limit,
182 cursor,
183 )
184 .await
185 .map_err(to_pyvalue_err)?;
186
187 Python::attach(|py| {
188 let open_orders = BybitOrderCursorList::from(response.result);
189 let py_open_orders = Py::new(py, open_orders)?;
190 Ok(py_open_orders.into_any())
191 })
192 })
193 }
194}
195
196#[pymethods]
197#[pyo3_stub_gen::derive::gen_stub_pymethods]
198impl BybitHttpClient {
199 #[new]
205 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, demo=false, testnet=false, timeout_secs=60, max_retries=3, retry_delay_ms=1000, retry_delay_max_ms=10_000, recv_window_ms=5_000, proxy_url=None))]
206 #[expect(clippy::too_many_arguments)]
207 fn py_new(
208 api_key: Option<String>,
209 api_secret: Option<String>,
210 base_url: Option<String>,
211 demo: bool,
212 testnet: bool,
213 timeout_secs: u64,
214 max_retries: u32,
215 retry_delay_ms: u64,
216 retry_delay_max_ms: u64,
217 recv_window_ms: u64,
218 proxy_url: Option<String>,
219 ) -> PyResult<Self> {
220 Self::new_with_env(
221 api_key,
222 api_secret,
223 base_url,
224 demo,
225 testnet,
226 timeout_secs,
227 max_retries,
228 retry_delay_ms,
229 retry_delay_max_ms,
230 recv_window_ms,
231 proxy_url,
232 )
233 .map_err(to_pyvalue_err)
234 }
235
236 #[getter]
237 #[pyo3(name = "base_url")]
238 #[must_use]
239 pub fn py_base_url(&self) -> &str {
240 self.base_url()
241 }
242
243 #[getter]
244 #[pyo3(name = "api_key")]
245 #[must_use]
246 pub fn py_api_key(&self) -> Option<&str> {
247 self.credential().map(|c| c.api_key())
248 }
249
250 #[getter]
251 #[pyo3(name = "api_key_masked")]
252 #[must_use]
253 pub fn py_api_key_masked(&self) -> Option<String> {
254 self.credential().map(|c| c.api_key_masked())
255 }
256
257 #[pyo3(name = "cache_instrument")]
259 fn py_cache_instrument(&self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
260 let inst_any = pyobject_to_instrument_any(py, instrument)?;
261 self.cache_instrument(inst_any);
262 Ok(())
263 }
264
265 #[pyo3(name = "cancel_all_requests")]
266 fn py_cancel_all_requests(&self) {
267 self.cancel_all_requests();
268 }
269
270 #[pyo3(name = "set_use_spot_position_reports")]
271 fn py_set_use_spot_position_reports(&self, value: bool) {
272 self.set_use_spot_position_reports(value);
273 }
274
275 #[pyo3(name = "set_margin_mode")]
288 fn py_set_margin_mode<'py>(
289 &self,
290 py: Python<'py>,
291 margin_mode: BybitMarginMode,
292 ) -> PyResult<Bound<'py, PyAny>> {
293 let client = self.clone();
294
295 pyo3_async_runtimes::tokio::future_into_py(py, async move {
296 client
297 .set_margin_mode(margin_mode)
298 .await
299 .map_err(to_pyvalue_err)?;
300
301 Python::attach(|py| Ok(py.None()))
302 })
303 }
304
305 #[pyo3(name = "get_account_details")]
317 fn py_get_account_details<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
318 let client = self.clone();
319
320 pyo3_async_runtimes::tokio::future_into_py(py, async move {
321 let response = client.get_account_details().await.map_err(to_pyvalue_err)?;
322
323 Python::attach(|py| {
324 let account_details = Py::new(py, response.result)?;
325 Ok(account_details.into_any())
326 })
327 })
328 }
329
330 #[pyo3(name = "set_leverage")]
343 #[pyo3(signature = (product_type, symbol, buy_leverage, sell_leverage))]
344 fn py_set_leverage<'py>(
345 &self,
346 py: Python<'py>,
347 product_type: BybitProductType,
348 symbol: String,
349 buy_leverage: String,
350 sell_leverage: String,
351 ) -> PyResult<Bound<'py, PyAny>> {
352 let client = self.clone();
353
354 pyo3_async_runtimes::tokio::future_into_py(py, async move {
355 client
356 .set_leverage(product_type, &symbol, &buy_leverage, &sell_leverage)
357 .await
358 .map_err(to_pyvalue_err)?;
359
360 Python::attach(|py| Ok(py.None()))
361 })
362 }
363
364 #[pyo3(name = "switch_mode")]
377 #[pyo3(signature = (product_type, mode, symbol=None, coin=None))]
378 fn py_switch_mode<'py>(
379 &self,
380 py: Python<'py>,
381 product_type: BybitProductType,
382 mode: BybitPositionMode,
383 symbol: Option<String>,
384 coin: Option<String>,
385 ) -> PyResult<Bound<'py, PyAny>> {
386 let client = self.clone();
387
388 pyo3_async_runtimes::tokio::future_into_py(py, async move {
389 client
390 .switch_mode(product_type, mode, symbol, coin)
391 .await
392 .map_err(to_pyvalue_err)?;
393
394 Python::attach(|py| Ok(py.None()))
395 })
396 }
397
398 #[pyo3(name = "get_spot_borrow_amount")]
413 fn py_get_spot_borrow_amount<'py>(
414 &self,
415 py: Python<'py>,
416 coin: String,
417 ) -> PyResult<Bound<'py, PyAny>> {
418 let client = self.clone();
419
420 pyo3_async_runtimes::tokio::future_into_py(py, async move {
421 let borrow_amount = client
422 .get_spot_borrow_amount(&coin)
423 .await
424 .map_err(to_pyvalue_err)?;
425
426 Ok(borrow_amount)
427 })
428 }
429
430 #[pyo3(name = "borrow_spot")]
446 #[pyo3(signature = (coin, amount))]
447 fn py_borrow_spot<'py>(
448 &self,
449 py: Python<'py>,
450 coin: String,
451 amount: Quantity,
452 ) -> PyResult<Bound<'py, PyAny>> {
453 let client = self.clone();
454
455 pyo3_async_runtimes::tokio::future_into_py(py, async move {
456 client
457 .borrow_spot(&coin, amount)
458 .await
459 .map_err(to_pyvalue_err)?;
460
461 Python::attach(|py| Ok(py.None()))
462 })
463 }
464
465 #[pyo3(name = "repay_spot_borrow")]
482 #[pyo3(signature = (coin, amount=None))]
483 fn py_repay_spot_borrow<'py>(
484 &self,
485 py: Python<'py>,
486 coin: String,
487 amount: Option<Quantity>,
488 ) -> PyResult<Bound<'py, PyAny>> {
489 let client = self.clone();
490
491 pyo3_async_runtimes::tokio::future_into_py(py, async move {
492 client
493 .repay_spot_borrow(&coin, amount)
494 .await
495 .map_err(to_pyvalue_err)?;
496
497 Python::attach(|py| Ok(py.None()))
498 })
499 }
500
501 #[pyo3(name = "repay_spot_borrow_with_conversion")]
519 #[pyo3(signature = (coin, amount=None))]
520 fn py_repay_spot_borrow_with_conversion<'py>(
521 &self,
522 py: Python<'py>,
523 coin: String,
524 amount: Option<Quantity>,
525 ) -> PyResult<Bound<'py, PyAny>> {
526 let client = self.clone();
527
528 pyo3_async_runtimes::tokio::future_into_py(py, async move {
529 client
530 .repay_spot_borrow_with_conversion(&coin, amount)
531 .await
532 .map_err(to_pyvalue_err)?;
533
534 Python::attach(|py| Ok(py.None()))
535 })
536 }
537
538 #[pyo3(name = "request_instruments")]
548 #[pyo3(signature = (product_type, symbol=None, base_coin=None))]
549 fn py_request_instruments<'py>(
550 &self,
551 py: Python<'py>,
552 product_type: BybitProductType,
553 symbol: Option<String>,
554 base_coin: Option<String>,
555 ) -> PyResult<Bound<'py, PyAny>> {
556 let client = self.clone();
557 let base_coin = base_coin.map(|s| Ustr::from(&s));
558
559 pyo3_async_runtimes::tokio::future_into_py(py, async move {
560 let instruments = client
561 .request_instruments(product_type, symbol, base_coin)
562 .await
563 .map_err(to_pyvalue_err)?;
564
565 Python::attach(|py| {
566 let py_instruments: PyResult<Vec<_>> = instruments
567 .into_iter()
568 .map(|inst| instrument_any_to_pyobject(py, inst))
569 .collect();
570 let pylist = PyList::new(py, py_instruments?)?.into_any().unbind();
571 Ok(pylist)
572 })
573 })
574 }
575
576 #[pyo3(name = "request_instrument_statuses")]
586 fn py_request_instrument_statuses<'py>(
587 &self,
588 py: Python<'py>,
589 product_type: BybitProductType,
590 ) -> PyResult<Bound<'py, PyAny>> {
591 let client = self.clone();
592
593 pyo3_async_runtimes::tokio::future_into_py(py, async move {
594 let statuses = client
595 .request_instrument_statuses(product_type)
596 .await
597 .map_err(to_pyvalue_err)?;
598
599 Python::attach(|py| {
600 let dict = PyDict::new(py);
601 for (instrument_id, action) in statuses {
602 dict.set_item(
603 instrument_id.into_bound_py_any(py)?,
604 action.into_bound_py_any(py)?,
605 )?;
606 }
607 Ok(dict.into_any().unbind())
608 })
609 })
610 }
611
612 #[pyo3(name = "request_tickers")]
625 fn py_request_tickers<'py>(
626 &self,
627 py: Python<'py>,
628 params: crate::python::params::BybitTickersParams,
629 ) -> PyResult<Bound<'py, PyAny>> {
630 let client = self.clone();
631
632 pyo3_async_runtimes::tokio::future_into_py(py, async move {
633 let tickers = client
634 .request_tickers(¶ms.into())
635 .await
636 .map_err(to_pyvalue_err)?;
637
638 Python::attach(|py| {
639 let py_tickers: PyResult<Vec<_>> = tickers
640 .into_iter()
641 .map(|ticker| Py::new(py, ticker))
642 .collect();
643 let pylist = PyList::new(py, py_tickers?)?.into_any().unbind();
644 Ok(pylist)
645 })
646 })
647 }
648
649 #[pyo3(name = "submit_order")]
660 #[pyo3(signature = (
661 account_id,
662 product_type,
663 instrument_id,
664 client_order_id,
665 order_side,
666 order_type,
667 quantity,
668 time_in_force = None,
669 price = None,
670 trigger_price = None,
671 post_only = None,
672 reduce_only = false,
673 is_quote_quantity = false,
674 is_leverage = false,
675 position_idx = None,
676 bbo_side_type = None,
677 bbo_level = None,
678 smp_type = None,
679 native_tp_sl = None,
680 ))]
681 #[expect(clippy::too_many_arguments)]
682 fn py_submit_order<'py>(
683 &self,
684 py: Python<'py>,
685 account_id: AccountId,
686 product_type: BybitProductType,
687 instrument_id: InstrumentId,
688 client_order_id: ClientOrderId,
689 order_side: OrderSide,
690 order_type: OrderType,
691 quantity: Quantity,
692 time_in_force: Option<TimeInForce>,
693 price: Option<Price>,
694 trigger_price: Option<Price>,
695 post_only: Option<bool>,
696 reduce_only: bool,
697 is_quote_quantity: bool,
698 is_leverage: bool,
699 position_idx: Option<BybitPositionIdx>,
700 bbo_side_type: Option<String>,
701 bbo_level: Option<String>,
702 smp_type: Option<String>,
703 native_tp_sl: Option<BybitNativeTpSlParams>,
704 ) -> PyResult<Bound<'py, PyAny>> {
705 let client = self.clone();
706 let bbo_side_type = bbo_side_type
707 .map(|value| parse_bbo_side_type(&value))
708 .transpose()
709 .map_err(to_pyvalue_err)?;
710 let bbo_level = bbo_level
711 .map(parse_bbo_level)
712 .transpose()
713 .map_err(to_pyvalue_err)?;
714 if bbo_side_type.is_some() != bbo_level.is_some() {
715 return Err(to_pyvalue_err(anyhow::anyhow!(
716 "'bbo_side_type' and 'bbo_level' must be provided together"
717 )));
718 }
719
720 let smp_type = smp_type
721 .map(|value| parse_smp_type(&value))
722 .transpose()
723 .map_err(to_pyvalue_err)?;
724
725 let native_tp_sl: Option<RustNativeTpSlParams> = native_tp_sl
726 .map(RustNativeTpSlParams::try_from)
727 .transpose()
728 .map_err(to_pyvalue_err)?;
729
730 pyo3_async_runtimes::tokio::future_into_py(py, async move {
731 let report = client
732 .submit_order(
733 account_id,
734 product_type,
735 instrument_id,
736 client_order_id,
737 order_side,
738 order_type,
739 quantity,
740 time_in_force,
741 price,
742 trigger_price,
743 post_only,
744 reduce_only,
745 is_quote_quantity,
746 is_leverage,
747 position_idx,
748 bbo_side_type,
749 bbo_level,
750 smp_type,
751 native_tp_sl.as_ref(),
752 )
753 .await
754 .map_err(to_pyvalue_err)?;
755
756 Python::attach(|py| report.into_py_any(py))
757 })
758 }
759
760 #[pyo3(name = "modify_order")]
771 #[pyo3(signature = (
772 account_id,
773 product_type,
774 instrument_id,
775 client_order_id=None,
776 venue_order_id=None,
777 quantity=None,
778 price=None
779 ))]
780 #[expect(clippy::too_many_arguments)]
781 fn py_modify_order<'py>(
782 &self,
783 py: Python<'py>,
784 account_id: AccountId,
785 product_type: BybitProductType,
786 instrument_id: InstrumentId,
787 client_order_id: Option<ClientOrderId>,
788 venue_order_id: Option<VenueOrderId>,
789 quantity: Option<Quantity>,
790 price: Option<Price>,
791 ) -> PyResult<Bound<'py, PyAny>> {
792 let client = self.clone();
793
794 pyo3_async_runtimes::tokio::future_into_py(py, async move {
795 let report = client
796 .modify_order(
797 account_id,
798 product_type,
799 instrument_id,
800 client_order_id,
801 venue_order_id,
802 quantity,
803 price,
804 )
805 .await
806 .map_err(to_pyvalue_err)?;
807
808 Python::attach(|py| report.into_py_any(py))
809 })
810 }
811
812 #[pyo3(name = "cancel_order")]
822 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
823 fn py_cancel_order<'py>(
824 &self,
825 py: Python<'py>,
826 account_id: AccountId,
827 product_type: BybitProductType,
828 instrument_id: InstrumentId,
829 client_order_id: Option<ClientOrderId>,
830 venue_order_id: Option<VenueOrderId>,
831 ) -> PyResult<Bound<'py, PyAny>> {
832 let client = self.clone();
833
834 pyo3_async_runtimes::tokio::future_into_py(py, async move {
835 let report = client
836 .cancel_order(
837 account_id,
838 product_type,
839 instrument_id,
840 client_order_id,
841 venue_order_id,
842 )
843 .await
844 .map_err(to_pyvalue_err)?;
845
846 Python::attach(|py| report.into_py_any(py))
847 })
848 }
849
850 #[pyo3(name = "cancel_all_orders")]
859 fn py_cancel_all_orders<'py>(
860 &self,
861 py: Python<'py>,
862 account_id: AccountId,
863 product_type: BybitProductType,
864 instrument_id: InstrumentId,
865 ) -> PyResult<Bound<'py, PyAny>> {
866 let client = self.clone();
867
868 pyo3_async_runtimes::tokio::future_into_py(py, async move {
869 let reports = client
870 .cancel_all_orders(account_id, product_type, instrument_id)
871 .await
872 .map_err(to_pyvalue_err)?;
873
874 Python::attach(|py| {
875 let py_reports: PyResult<Vec<_>> = reports
876 .into_iter()
877 .map(|report| report.into_py_any(py))
878 .collect();
879 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
880 Ok(pylist)
881 })
882 })
883 }
884
885 #[pyo3(name = "query_order")]
894 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
895 fn py_query_order<'py>(
896 &self,
897 py: Python<'py>,
898 account_id: AccountId,
899 product_type: BybitProductType,
900 instrument_id: InstrumentId,
901 client_order_id: Option<ClientOrderId>,
902 venue_order_id: Option<VenueOrderId>,
903 ) -> PyResult<Bound<'py, PyAny>> {
904 let client = self.clone();
905
906 pyo3_async_runtimes::tokio::future_into_py(py, async move {
907 match client
908 .query_order(
909 account_id,
910 product_type,
911 instrument_id,
912 client_order_id,
913 venue_order_id,
914 )
915 .await
916 {
917 Ok(Some(report)) => Python::attach(|py| report.into_py_any(py)),
918 Ok(None) => Ok(Python::attach(|py| py.None())),
919 Err(e) => Err(to_pyvalue_err(e)),
920 }
921 })
922 }
923
924 #[pyo3(name = "request_trades")]
944 #[pyo3(signature = (product_type, instrument_id, limit=None))]
945 fn py_request_trades<'py>(
946 &self,
947 py: Python<'py>,
948 product_type: BybitProductType,
949 instrument_id: InstrumentId,
950 limit: Option<u32>,
951 ) -> PyResult<Bound<'py, PyAny>> {
952 let client = self.clone();
953
954 pyo3_async_runtimes::tokio::future_into_py(py, async move {
955 let trades = client
956 .request_trades(product_type, instrument_id, limit)
957 .await
958 .map_err(to_pyvalue_err)?;
959
960 Python::attach(|py| {
961 let py_trades: PyResult<Vec<_>> = trades
962 .into_iter()
963 .map(|trade| trade.into_py_any(py))
964 .collect();
965 let pylist = PyList::new(py, py_trades?)?.into_any().unbind();
966 Ok(pylist)
967 })
968 })
969 }
970
971 #[pyo3(name = "request_funding_rates")]
984 #[pyo3(signature = (product_type, instrument_id, start=None, end=None, limit=None))]
985 fn py_request_funding_rates<'py>(
986 &self,
987 py: Python<'py>,
988 product_type: BybitProductType,
989 instrument_id: InstrumentId,
990 start: Option<Timestamp>,
991 end: Option<Timestamp>,
992 limit: Option<u32>,
993 ) -> PyResult<Bound<'py, PyAny>> {
994 let client = self.clone();
995
996 pyo3_async_runtimes::tokio::future_into_py(py, async move {
997 let funding_rates = client
998 .request_funding_rates(product_type, instrument_id, start, end, limit)
999 .await
1000 .map_err(to_pyvalue_err)?;
1001
1002 Python::attach(|py| {
1003 let py_funding_rates: PyResult<Vec<_>> = funding_rates
1004 .into_iter()
1005 .map(|funding_rate| funding_rate.into_py_any(py))
1006 .collect();
1007 let pylist = PyList::new(py, py_funding_rates?)?.into_any().unbind();
1008 Ok(pylist)
1009 })
1010 })
1011 }
1012
1013 #[pyo3(name = "request_orderbook_snapshot")]
1031 #[pyo3(signature = (product_type, instrument_id, limit=None))]
1032 fn py_request_orderbook_snapshot<'py>(
1033 &self,
1034 py: Python<'py>,
1035 product_type: BybitProductType,
1036 instrument_id: InstrumentId,
1037 limit: Option<u32>,
1038 ) -> PyResult<Bound<'py, PyAny>> {
1039 let client = self.clone();
1040
1041 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1042 let deltas = client
1043 .request_orderbook_snapshot(product_type, instrument_id, limit)
1044 .await
1045 .map_err(to_pyvalue_err)?;
1046
1047 Python::attach(|py| deltas.into_py_any(py))
1048 })
1049 }
1050
1051 #[pyo3(name = "request_bars")]
1064 #[pyo3(signature = (product_type, bar_type, start=None, end=None, limit=None, timestamp_on_close=true))]
1065 #[expect(clippy::too_many_arguments)]
1066 fn py_request_bars<'py>(
1067 &self,
1068 py: Python<'py>,
1069 product_type: BybitProductType,
1070 bar_type: BarType,
1071 start: Option<Timestamp>,
1072 end: Option<Timestamp>,
1073 limit: Option<u32>,
1074 timestamp_on_close: bool,
1075 ) -> PyResult<Bound<'py, PyAny>> {
1076 let client = self.clone();
1077
1078 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1079 let bars = client
1080 .request_bars(
1081 product_type,
1082 bar_type,
1083 start,
1084 end,
1085 limit,
1086 timestamp_on_close,
1087 )
1088 .await
1089 .map_err(to_pyvalue_err)?;
1090
1091 Python::attach(|py| {
1092 let py_bars: PyResult<Vec<_>> =
1093 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
1094 let pylist = PyList::new(py, py_bars?)?.into_any().unbind();
1095 Ok(pylist)
1096 })
1097 })
1098 }
1099
1100 #[pyo3(name = "request_fee_rates")]
1112 #[pyo3(signature = (product_type, symbol=None, base_coin=None))]
1113 fn py_request_fee_rates<'py>(
1114 &self,
1115 py: Python<'py>,
1116 product_type: BybitProductType,
1117 symbol: Option<String>,
1118 base_coin: Option<String>,
1119 ) -> PyResult<Bound<'py, PyAny>> {
1120 let client = self.clone();
1121
1122 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1123 let fee_rates = client
1124 .request_fee_rates(product_type, symbol, base_coin)
1125 .await
1126 .map_err(to_pyvalue_err)?;
1127
1128 Python::attach(|py| {
1129 let py_fee_rates: PyResult<Vec<_>> = fee_rates
1130 .into_iter()
1131 .map(|rate| Py::new(py, rate))
1132 .collect();
1133 let pylist = PyList::new(py, py_fee_rates?)?.into_any().unbind();
1134 Ok(pylist)
1135 })
1136 })
1137 }
1138
1139 #[pyo3(name = "request_account_state")]
1151 fn py_request_account_state<'py>(
1152 &self,
1153 py: Python<'py>,
1154 account_type: crate::common::enums::BybitAccountType,
1155 account_id: AccountId,
1156 ) -> PyResult<Bound<'py, PyAny>> {
1157 let client = self.clone();
1158
1159 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1160 let account_state = client
1161 .request_account_state(account_type, account_id)
1162 .await
1163 .map_err(to_pyvalue_err)?;
1164
1165 Python::attach(|py| account_state.into_py_any(py))
1166 })
1167 }
1168
1169 #[pyo3(name = "request_order_status_reports")]
1185 #[pyo3(signature = (account_id, product_type, instrument_id=None, open_only=false, start=None, end=None, limit=None))]
1186 #[expect(clippy::too_many_arguments)]
1187 fn py_request_order_status_reports<'py>(
1188 &self,
1189 py: Python<'py>,
1190 account_id: AccountId,
1191 product_type: BybitProductType,
1192 instrument_id: Option<InstrumentId>,
1193 open_only: bool,
1194 start: Option<Timestamp>,
1195 end: Option<Timestamp>,
1196 limit: Option<u32>,
1197 ) -> PyResult<Bound<'py, PyAny>> {
1198 let client = self.clone();
1199
1200 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1201 let reports = client
1202 .request_order_status_reports(
1203 account_id,
1204 product_type,
1205 instrument_id,
1206 open_only,
1207 start,
1208 end,
1209 limit,
1210 )
1211 .await
1212 .map_err(to_pyvalue_err)?;
1213
1214 Python::attach(|py| {
1215 let py_reports: PyResult<Vec<_>> = reports
1216 .into_iter()
1217 .map(|report| report.into_py_any(py))
1218 .collect();
1219 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
1220 Ok(pylist)
1221 })
1222 })
1223 }
1224
1225 #[pyo3(name = "request_fill_reports")]
1237 #[pyo3(signature = (account_id, product_type, instrument_id=None, start=None, end=None, limit=None))]
1238 #[expect(clippy::too_many_arguments)]
1239 fn py_request_fill_reports<'py>(
1240 &self,
1241 py: Python<'py>,
1242 account_id: AccountId,
1243 product_type: BybitProductType,
1244 instrument_id: Option<InstrumentId>,
1245 start: Option<i64>,
1246 end: Option<i64>,
1247 limit: Option<u32>,
1248 ) -> PyResult<Bound<'py, PyAny>> {
1249 let client = self.clone();
1250
1251 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1252 let reports = client
1253 .request_fill_reports(account_id, product_type, instrument_id, start, end, limit)
1254 .await
1255 .map_err(to_pyvalue_err)?;
1256
1257 Python::attach(|py| {
1258 let py_reports: PyResult<Vec<_>> = reports
1259 .into_iter()
1260 .map(|report| report.into_py_any(py))
1261 .collect();
1262 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
1263 Ok(pylist)
1264 })
1265 })
1266 }
1267
1268 #[pyo3(name = "request_position_status_reports")]
1281 #[pyo3(signature = (account_id, product_type, instrument_id=None))]
1282 fn py_request_position_status_reports<'py>(
1283 &self,
1284 py: Python<'py>,
1285 account_id: AccountId,
1286 product_type: BybitProductType,
1287 instrument_id: Option<InstrumentId>,
1288 ) -> PyResult<Bound<'py, PyAny>> {
1289 let client = self.clone();
1290
1291 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1292 let reports = client
1293 .request_position_status_reports(account_id, product_type, instrument_id)
1294 .await
1295 .map_err(to_pyvalue_err)?;
1296
1297 Python::attach(|py| {
1298 let py_reports: PyResult<Vec<_>> = reports
1299 .into_iter()
1300 .map(|report| report.into_py_any(py))
1301 .collect();
1302 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
1303 Ok(pylist)
1304 })
1305 })
1306 }
1307}
1308
1309impl From<BybitHttpError> for PyErr {
1310 fn from(error: BybitHttpError) -> Self {
1311 match error {
1312 BybitHttpError::Canceled(msg) => to_pyruntime_err(format!("Request canceled: {msg}")),
1314 BybitHttpError::NetworkError(msg) => to_pyruntime_err(format!("Network error: {msg}")),
1315 BybitHttpError::UnexpectedStatus { status, body } => {
1316 to_pyruntime_err(format!("Unexpected HTTP status code {status}: {body}"))
1317 }
1318 BybitHttpError::MissingCredentials => {
1320 to_pyvalue_err("Missing credentials for authenticated request")
1321 }
1322 BybitHttpError::ValidationError(msg) => {
1323 to_pyvalue_err(format!("Parameter validation error: {msg}"))
1324 }
1325 BybitHttpError::JsonError(msg) => to_pyvalue_err(format!("JSON error: {msg}")),
1326 BybitHttpError::BuildError(e) => to_pyvalue_err(format!("Build error: {e}")),
1327 BybitHttpError::BybitError {
1328 error_code,
1329 message,
1330 } => to_pyvalue_err(format!("Bybit error {error_code}: {message}")),
1331 }
1332 }
1333}