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 = None,
430 price = None,
431 trigger_price = None,
432 post_only = None,
433 reduce_only = false,
434 is_quote_quantity = false,
435 is_leverage = false
436 ))]
437 #[allow(clippy::too_many_arguments)]
438 fn py_submit_order<'py>(
439 &self,
440 py: Python<'py>,
441 account_id: AccountId,
442 product_type: BybitProductType,
443 instrument_id: InstrumentId,
444 client_order_id: ClientOrderId,
445 order_side: OrderSide,
446 order_type: OrderType,
447 quantity: Quantity,
448 time_in_force: Option<TimeInForce>,
449 price: Option<Price>,
450 trigger_price: Option<Price>,
451 post_only: Option<bool>,
452 reduce_only: bool,
453 is_quote_quantity: bool,
454 is_leverage: bool,
455 ) -> PyResult<Bound<'py, PyAny>> {
456 let client = self.clone();
457
458 pyo3_async_runtimes::tokio::future_into_py(py, async move {
459 let report = client
460 .submit_order(
461 account_id,
462 product_type,
463 instrument_id,
464 client_order_id,
465 order_side,
466 order_type,
467 quantity,
468 time_in_force,
469 price,
470 trigger_price,
471 post_only,
472 reduce_only,
473 is_quote_quantity,
474 is_leverage,
475 )
476 .await
477 .map_err(to_pyvalue_err)?;
478
479 Python::attach(|py| report.into_py_any(py))
480 })
481 }
482
483 #[pyo3(name = "modify_order")]
484 #[pyo3(signature = (
485 account_id,
486 product_type,
487 instrument_id,
488 client_order_id=None,
489 venue_order_id=None,
490 quantity=None,
491 price=None
492 ))]
493 #[allow(clippy::too_many_arguments)]
494 fn py_modify_order<'py>(
495 &self,
496 py: Python<'py>,
497 account_id: AccountId,
498 product_type: BybitProductType,
499 instrument_id: InstrumentId,
500 client_order_id: Option<ClientOrderId>,
501 venue_order_id: Option<VenueOrderId>,
502 quantity: Option<Quantity>,
503 price: Option<Price>,
504 ) -> PyResult<Bound<'py, PyAny>> {
505 let client = self.clone();
506
507 pyo3_async_runtimes::tokio::future_into_py(py, async move {
508 let report = client
509 .modify_order(
510 account_id,
511 product_type,
512 instrument_id,
513 client_order_id,
514 venue_order_id,
515 quantity,
516 price,
517 )
518 .await
519 .map_err(to_pyvalue_err)?;
520
521 Python::attach(|py| report.into_py_any(py))
522 })
523 }
524
525 #[pyo3(name = "cancel_order")]
526 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
527 fn py_cancel_order<'py>(
528 &self,
529 py: Python<'py>,
530 account_id: AccountId,
531 product_type: BybitProductType,
532 instrument_id: InstrumentId,
533 client_order_id: Option<ClientOrderId>,
534 venue_order_id: Option<VenueOrderId>,
535 ) -> PyResult<Bound<'py, PyAny>> {
536 let client = self.clone();
537
538 pyo3_async_runtimes::tokio::future_into_py(py, async move {
539 let report = client
540 .cancel_order(
541 account_id,
542 product_type,
543 instrument_id,
544 client_order_id,
545 venue_order_id,
546 )
547 .await
548 .map_err(to_pyvalue_err)?;
549
550 Python::attach(|py| report.into_py_any(py))
551 })
552 }
553
554 #[pyo3(name = "cancel_all_orders")]
555 fn py_cancel_all_orders<'py>(
556 &self,
557 py: Python<'py>,
558 account_id: AccountId,
559 product_type: BybitProductType,
560 instrument_id: InstrumentId,
561 ) -> PyResult<Bound<'py, PyAny>> {
562 let client = self.clone();
563
564 pyo3_async_runtimes::tokio::future_into_py(py, async move {
565 let reports = client
566 .cancel_all_orders(account_id, product_type, instrument_id)
567 .await
568 .map_err(to_pyvalue_err)?;
569
570 Python::attach(|py| {
571 let py_reports: PyResult<Vec<_>> = reports
572 .into_iter()
573 .map(|report| report.into_py_any(py))
574 .collect();
575 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
576 Ok(pylist)
577 })
578 })
579 }
580
581 #[pyo3(name = "query_order")]
582 #[pyo3(signature = (account_id, product_type, instrument_id, client_order_id=None, venue_order_id=None))]
583 fn py_query_order<'py>(
584 &self,
585 py: Python<'py>,
586 account_id: AccountId,
587 product_type: BybitProductType,
588 instrument_id: InstrumentId,
589 client_order_id: Option<ClientOrderId>,
590 venue_order_id: Option<VenueOrderId>,
591 ) -> PyResult<Bound<'py, PyAny>> {
592 let client = self.clone();
593
594 pyo3_async_runtimes::tokio::future_into_py(py, async move {
595 match client
596 .query_order(
597 account_id,
598 product_type,
599 instrument_id,
600 client_order_id,
601 venue_order_id,
602 )
603 .await
604 {
605 Ok(Some(report)) => Python::attach(|py| report.into_py_any(py)),
606 Ok(None) => Ok(Python::attach(|py| py.None())),
607 Err(e) => Err(to_pyvalue_err(e)),
608 }
609 })
610 }
611
612 #[pyo3(name = "request_trades")]
613 #[pyo3(signature = (product_type, instrument_id, limit=None))]
614 fn py_request_trades<'py>(
615 &self,
616 py: Python<'py>,
617 product_type: BybitProductType,
618 instrument_id: InstrumentId,
619 limit: Option<u32>,
620 ) -> PyResult<Bound<'py, PyAny>> {
621 let client = self.clone();
622
623 pyo3_async_runtimes::tokio::future_into_py(py, async move {
624 let trades = client
625 .request_trades(product_type, instrument_id, limit)
626 .await
627 .map_err(to_pyvalue_err)?;
628
629 Python::attach(|py| {
630 let py_trades: PyResult<Vec<_>> = trades
631 .into_iter()
632 .map(|trade| trade.into_py_any(py))
633 .collect();
634 let pylist = PyList::new(py, py_trades?).unwrap().into_any().unbind();
635 Ok(pylist)
636 })
637 })
638 }
639
640 #[pyo3(name = "request_funding_rates")]
641 #[pyo3(signature = (product_type, instrument_id, start=None, end=None, limit=None))]
642 fn py_request_funding_rates<'py>(
643 &self,
644 py: Python<'py>,
645 product_type: BybitProductType,
646 instrument_id: InstrumentId,
647 start: Option<DateTime<Utc>>,
648 end: Option<DateTime<Utc>>,
649 limit: Option<u32>,
650 ) -> PyResult<Bound<'py, PyAny>> {
651 let client = self.clone();
652
653 pyo3_async_runtimes::tokio::future_into_py(py, async move {
654 let funding_rates = client
655 .request_funding_rates(product_type, instrument_id, start, end, limit)
656 .await
657 .map_err(to_pyvalue_err)?;
658
659 Python::attach(|py| {
660 let py_funding_rates: PyResult<Vec<_>> = funding_rates
661 .into_iter()
662 .map(|funding_rate| funding_rate.into_py_any(py))
663 .collect();
664 let pylist = PyList::new(py, py_funding_rates?)
665 .unwrap()
666 .into_any()
667 .unbind();
668 Ok(pylist)
669 })
670 })
671 }
672
673 #[pyo3(name = "request_orderbook_snapshot")]
674 #[pyo3(signature = (product_type, instrument_id, limit=None))]
675 fn py_request_orderbook_snapshot<'py>(
676 &self,
677 py: Python<'py>,
678 product_type: BybitProductType,
679 instrument_id: InstrumentId,
680 limit: Option<u32>,
681 ) -> PyResult<Bound<'py, PyAny>> {
682 let client = self.clone();
683
684 pyo3_async_runtimes::tokio::future_into_py(py, async move {
685 let deltas = client
686 .request_orderbook_snapshot(product_type, instrument_id, limit)
687 .await
688 .map_err(to_pyvalue_err)?;
689
690 Python::attach(|py| Ok(deltas.into_py_any(py).unwrap()))
691 })
692 }
693
694 #[pyo3(name = "request_bars")]
695 #[pyo3(signature = (product_type, bar_type, start=None, end=None, limit=None, timestamp_on_close=true))]
696 #[allow(clippy::too_many_arguments)]
697 fn py_request_bars<'py>(
698 &self,
699 py: Python<'py>,
700 product_type: BybitProductType,
701 bar_type: BarType,
702 start: Option<DateTime<Utc>>,
703 end: Option<DateTime<Utc>>,
704 limit: Option<u32>,
705 timestamp_on_close: bool,
706 ) -> PyResult<Bound<'py, PyAny>> {
707 let client = self.clone();
708
709 pyo3_async_runtimes::tokio::future_into_py(py, async move {
710 let bars = client
711 .request_bars(
712 product_type,
713 bar_type,
714 start,
715 end,
716 limit,
717 timestamp_on_close,
718 )
719 .await
720 .map_err(to_pyvalue_err)?;
721
722 Python::attach(|py| {
723 let py_bars: PyResult<Vec<_>> =
724 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
725 let pylist = PyList::new(py, py_bars?).unwrap().into_any().unbind();
726 Ok(pylist)
727 })
728 })
729 }
730
731 #[pyo3(name = "request_fee_rates")]
732 #[pyo3(signature = (product_type, symbol=None, base_coin=None))]
733 fn py_request_fee_rates<'py>(
734 &self,
735 py: Python<'py>,
736 product_type: BybitProductType,
737 symbol: Option<String>,
738 base_coin: Option<String>,
739 ) -> PyResult<Bound<'py, PyAny>> {
740 let client = self.clone();
741
742 pyo3_async_runtimes::tokio::future_into_py(py, async move {
743 let fee_rates = client
744 .request_fee_rates(product_type, symbol, base_coin)
745 .await
746 .map_err(to_pyvalue_err)?;
747
748 Python::attach(|py| {
749 let py_fee_rates: PyResult<Vec<_>> = fee_rates
750 .into_iter()
751 .map(|rate| Py::new(py, rate))
752 .collect();
753 let pylist = PyList::new(py, py_fee_rates?).unwrap().into_any().unbind();
754 Ok(pylist)
755 })
756 })
757 }
758
759 #[pyo3(name = "request_account_state")]
760 fn py_request_account_state<'py>(
761 &self,
762 py: Python<'py>,
763 account_type: crate::common::enums::BybitAccountType,
764 account_id: AccountId,
765 ) -> PyResult<Bound<'py, PyAny>> {
766 let client = self.clone();
767
768 pyo3_async_runtimes::tokio::future_into_py(py, async move {
769 let account_state = client
770 .request_account_state(account_type, account_id)
771 .await
772 .map_err(to_pyvalue_err)?;
773
774 Python::attach(|py| account_state.into_py_any(py))
775 })
776 }
777
778 #[pyo3(name = "request_order_status_reports")]
779 #[pyo3(signature = (account_id, product_type, instrument_id=None, open_only=false, start=None, end=None, limit=None))]
780 #[allow(clippy::too_many_arguments)]
781 fn py_request_order_status_reports<'py>(
782 &self,
783 py: Python<'py>,
784 account_id: AccountId,
785 product_type: BybitProductType,
786 instrument_id: Option<InstrumentId>,
787 open_only: bool,
788 start: Option<DateTime<Utc>>,
789 end: Option<DateTime<Utc>>,
790 limit: Option<u32>,
791 ) -> PyResult<Bound<'py, PyAny>> {
792 let client = self.clone();
793
794 pyo3_async_runtimes::tokio::future_into_py(py, async move {
795 let reports = client
796 .request_order_status_reports(
797 account_id,
798 product_type,
799 instrument_id,
800 open_only,
801 start,
802 end,
803 limit,
804 )
805 .await
806 .map_err(to_pyvalue_err)?;
807
808 Python::attach(|py| {
809 let py_reports: PyResult<Vec<_>> = reports
810 .into_iter()
811 .map(|report| report.into_py_any(py))
812 .collect();
813 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
814 Ok(pylist)
815 })
816 })
817 }
818
819 #[pyo3(name = "request_fill_reports")]
820 #[pyo3(signature = (account_id, product_type, instrument_id=None, start=None, end=None, limit=None))]
821 #[allow(clippy::too_many_arguments)]
822 fn py_request_fill_reports<'py>(
823 &self,
824 py: Python<'py>,
825 account_id: AccountId,
826 product_type: BybitProductType,
827 instrument_id: Option<InstrumentId>,
828 start: Option<i64>,
829 end: Option<i64>,
830 limit: Option<u32>,
831 ) -> PyResult<Bound<'py, PyAny>> {
832 let client = self.clone();
833
834 pyo3_async_runtimes::tokio::future_into_py(py, async move {
835 let reports = client
836 .request_fill_reports(account_id, product_type, instrument_id, start, end, limit)
837 .await
838 .map_err(to_pyvalue_err)?;
839
840 Python::attach(|py| {
841 let py_reports: PyResult<Vec<_>> = reports
842 .into_iter()
843 .map(|report| report.into_py_any(py))
844 .collect();
845 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
846 Ok(pylist)
847 })
848 })
849 }
850
851 #[pyo3(name = "request_position_status_reports")]
852 #[pyo3(signature = (account_id, product_type, instrument_id=None))]
853 fn py_request_position_status_reports<'py>(
854 &self,
855 py: Python<'py>,
856 account_id: AccountId,
857 product_type: BybitProductType,
858 instrument_id: Option<InstrumentId>,
859 ) -> PyResult<Bound<'py, PyAny>> {
860 let client = self.clone();
861
862 pyo3_async_runtimes::tokio::future_into_py(py, async move {
863 let reports = client
864 .request_position_status_reports(account_id, product_type, instrument_id)
865 .await
866 .map_err(to_pyvalue_err)?;
867
868 Python::attach(|py| {
869 let py_reports: PyResult<Vec<_>> = reports
870 .into_iter()
871 .map(|report| report.into_py_any(py))
872 .collect();
873 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
874 Ok(pylist)
875 })
876 })
877 }
878}
879
880impl From<BybitHttpError> for PyErr {
881 fn from(error: BybitHttpError) -> Self {
882 match error {
883 BybitHttpError::Canceled(msg) => to_pyruntime_err(format!("Request canceled: {msg}")),
885 BybitHttpError::NetworkError(msg) => to_pyruntime_err(format!("Network error: {msg}")),
886 BybitHttpError::UnexpectedStatus { status, body } => {
887 to_pyruntime_err(format!("Unexpected HTTP status code {status}: {body}"))
888 }
889 BybitHttpError::MissingCredentials => {
891 to_pyvalue_err("Missing credentials for authenticated request")
892 }
893 BybitHttpError::ValidationError(msg) => {
894 to_pyvalue_err(format!("Parameter validation error: {msg}"))
895 }
896 BybitHttpError::JsonError(msg) => to_pyvalue_err(format!("JSON error: {msg}")),
897 BybitHttpError::BuildError(e) => to_pyvalue_err(format!("Build error: {e}")),
898 BybitHttpError::BybitError {
899 error_code,
900 message,
901 } => to_pyvalue_err(format!("Bybit error {error_code}: {message}")),
902 }
903 }
904}