1use std::time::Duration;
19
20use nautilus_common::live::get_runtime;
21use nautilus_core::python::{
22 IntoPyObjectNautilusExt, call_python_threadsafe, to_pyruntime_err, to_pyvalue_err,
23};
24use nautilus_model::{
25 data::{BarType, Data, OrderBookDeltas_API},
26 enums::{OrderSide, OrderType, TimeInForce},
27 identifiers::{AccountId, ClientOrderId, InstrumentId, VenueOrderId},
28 orders::OrderAny,
29 python::{
30 data::data_to_pycapsule, instruments::pyobject_to_instrument_any,
31 orders::pyobject_to_order_any,
32 },
33 types::{Price, Quantity},
34};
35use nautilus_network::websocket::TransportBackend;
36use pyo3::{conversion::IntoPyObjectExt, prelude::*, types::PyList};
37
38use crate::{
39 common::enums::HyperliquidEnvironment,
40 http::client::HyperliquidHttpClient,
41 websocket::{
42 HyperliquidWebSocketClient,
43 messages::{ExecutionReport, NautilusWsMessage},
44 },
45};
46
47fn ws_data_to_pyobject(py: Python<'_>, data: Data) -> PyResult<Py<PyAny>> {
48 match data {
49 Data::Custom(custom) => Py::new(py, custom).map(|obj| obj.into_any()),
50 Data::OptionGreeks(greeks) => Py::new(py, greeks).map(|obj| obj.into_any()),
51 other => Ok(data_to_pycapsule(py, other)),
52 }
53}
54
55#[pymethods]
56#[pyo3_stub_gen::derive::gen_stub_pymethods]
57impl HyperliquidWebSocketClient {
58 #[new]
63 #[pyo3(signature = (url=None, environment=HyperliquidEnvironment::Mainnet, account_id=None, proxy_url=None))]
64 fn py_new(
65 url: Option<String>,
66 environment: HyperliquidEnvironment,
67 account_id: Option<String>,
68 proxy_url: Option<String>,
69 ) -> Self {
70 let account_id = account_id.map(|s| AccountId::from(s.as_str()));
71 Self::new(
72 url,
73 environment,
74 account_id,
75 TransportBackend::default(),
76 proxy_url,
77 )
78 }
79
80 #[getter]
82 #[pyo3(name = "url")]
83 #[must_use]
84 pub fn py_url(&self) -> String {
85 self.url().to_string()
86 }
87
88 #[pyo3(name = "is_active")]
90 fn py_is_active(&self) -> bool {
91 self.is_active()
92 }
93
94 #[pyo3(name = "is_closed")]
95 fn py_is_closed(&self) -> bool {
96 !self.is_active()
97 }
98
99 #[pyo3(name = "set_post_timeout")]
101 fn py_set_post_timeout(&mut self, timeout_secs: u64) {
102 self.set_post_timeout(Duration::from_secs(timeout_secs));
103 }
104
105 #[pyo3(name = "submit_order", signature = (
116 signer,
117 instrument_id,
118 client_order_id,
119 order_side,
120 order_type,
121 quantity,
122 time_in_force,
123 price=None,
124 trigger_price=None,
125 post_only=false,
126 reduce_only=false,
127 ))]
128 #[expect(clippy::too_many_arguments)]
129 fn py_submit_order<'py>(
130 &self,
131 py: Python<'py>,
132 signer: &HyperliquidHttpClient,
133 instrument_id: InstrumentId,
134 client_order_id: ClientOrderId,
135 order_side: OrderSide,
136 order_type: OrderType,
137 quantity: Quantity,
138 time_in_force: TimeInForce,
139 price: Option<Price>,
140 trigger_price: Option<Price>,
141 post_only: bool,
142 reduce_only: bool,
143 ) -> PyResult<Bound<'py, PyAny>> {
144 let client = self.clone();
145 let signer = signer.clone();
146
147 pyo3_async_runtimes::tokio::future_into_py(py, async move {
148 let report = client
149 .submit_order(
150 &signer,
151 instrument_id,
152 client_order_id,
153 order_side,
154 order_type,
155 quantity,
156 time_in_force,
157 price,
158 trigger_price,
159 post_only,
160 reduce_only,
161 )
162 .await
163 .map_err(to_pyvalue_err)?;
164
165 Python::attach(|py| match report {
166 Some(r) => Ok(r.into_py_any_unwrap(py)),
167 None => Ok(py.None()),
168 })
169 })
170 }
171
172 #[pyo3(name = "submit_orders")]
179 fn py_submit_orders<'py>(
180 &self,
181 py: Python<'py>,
182 signer: &HyperliquidHttpClient,
183 orders: Vec<Py<PyAny>>,
184 ) -> PyResult<Bound<'py, PyAny>> {
185 let client = self.clone();
186 let signer = signer.clone();
187
188 pyo3_async_runtimes::tokio::future_into_py(py, async move {
189 let order_anys: Vec<OrderAny> = Python::attach(|py| {
190 orders
191 .into_iter()
192 .map(|order| pyobject_to_order_any(py, order))
193 .collect::<PyResult<Vec<_>>>()
194 .map_err(to_pyvalue_err)
195 })?;
196 let order_refs: Vec<&OrderAny> = order_anys.iter().collect();
197
198 let reports = client
199 .submit_orders(&signer, &order_refs)
200 .await
201 .map_err(to_pyvalue_err)?;
202
203 Python::attach(|py| {
204 let pylist =
205 PyList::new(py, reports.into_iter().map(|r| r.into_py_any_unwrap(py)))?;
206 Ok(pylist.into_py_any_unwrap(py))
207 })
208 })
209 }
210
211 #[pyo3(name = "cancel_order", signature = (
213 signer,
214 instrument_id,
215 client_order_id=None,
216 venue_order_id=None,
217 ))]
218 fn py_cancel_order<'py>(
219 &self,
220 py: Python<'py>,
221 signer: &HyperliquidHttpClient,
222 instrument_id: InstrumentId,
223 client_order_id: Option<ClientOrderId>,
224 venue_order_id: Option<VenueOrderId>,
225 ) -> PyResult<Bound<'py, PyAny>> {
226 let client = self.clone();
227 let signer = signer.clone();
228
229 pyo3_async_runtimes::tokio::future_into_py(py, async move {
230 client
231 .cancel_order(&signer, instrument_id, client_order_id, venue_order_id)
232 .await
233 .map_err(to_pyvalue_err)?;
234 Ok(())
235 })
236 }
237
238 #[pyo3(name = "cancel_orders")]
240 fn py_cancel_orders<'py>(
241 &self,
242 py: Python<'py>,
243 signer: &HyperliquidHttpClient,
244 cancels: Vec<(InstrumentId, ClientOrderId, Option<VenueOrderId>)>,
245 ) -> PyResult<Bound<'py, PyAny>> {
246 let client = self.clone();
247 let signer = signer.clone();
248
249 pyo3_async_runtimes::tokio::future_into_py(py, async move {
250 client
251 .cancel_orders(&signer, &cancels)
252 .await
253 .map_err(to_pyvalue_err)
254 })
255 }
256
257 #[pyo3(name = "modify_order")]
259 #[expect(clippy::too_many_arguments)]
260 fn py_modify_order<'py>(
261 &self,
262 py: Python<'py>,
263 signer: &HyperliquidHttpClient,
264 instrument_id: InstrumentId,
265 venue_order_id: VenueOrderId,
266 order_side: OrderSide,
267 order_type: OrderType,
268 price: Price,
269 quantity: Quantity,
270 trigger_price: Option<Price>,
271 reduce_only: bool,
272 post_only: bool,
273 time_in_force: TimeInForce,
274 client_order_id: Option<ClientOrderId>,
275 ) -> PyResult<Bound<'py, PyAny>> {
276 let client = self.clone();
277 let signer = signer.clone();
278
279 pyo3_async_runtimes::tokio::future_into_py(py, async move {
280 client
281 .modify_order(
282 &signer,
283 instrument_id,
284 venue_order_id,
285 order_side,
286 order_type,
287 price,
288 quantity,
289 trigger_price,
290 reduce_only,
291 post_only,
292 time_in_force,
293 client_order_id,
294 )
295 .await
296 .map_err(to_pyvalue_err)?;
297 Ok(())
298 })
299 }
300
301 #[pyo3(name = "cache_spot_fill_coins")]
307 fn py_cache_spot_fill_coins(&self, mapping: std::collections::HashMap<String, String>) {
308 let ahash_mapping: ahash::AHashMap<ustr::Ustr, ustr::Ustr> = mapping
309 .into_iter()
310 .map(|(k, v)| (ustr::Ustr::from(&k), ustr::Ustr::from(&v)))
311 .collect();
312 self.cache_spot_fill_coins(ahash_mapping);
313 }
314
315 #[pyo3(name = "cache_all_dex_asset_ctxs_instrument_ids")]
317 fn py_cache_all_dex_asset_ctxs_instrument_ids(
318 &self,
319 mapping: std::collections::HashMap<String, Vec<Option<InstrumentId>>>,
320 ) {
321 let ahash_mapping: ahash::AHashMap<ustr::Ustr, Vec<Option<InstrumentId>>> = mapping
322 .into_iter()
323 .map(|(dex, instrument_ids)| (ustr::Ustr::from(&dex), instrument_ids))
324 .collect();
325 self.cache_all_dex_asset_ctxs_instrument_ids(ahash_mapping);
326 }
327
328 #[pyo3(name = "cache_cloid_mapping")]
336 fn py_cache_cloid_mapping(&self, cloid: &str, client_order_id: ClientOrderId) {
337 self.cache_cloid_mapping(ustr::Ustr::from(cloid), client_order_id);
338 }
339
340 #[pyo3(name = "remove_cloid_mapping")]
345 fn py_remove_cloid_mapping(&self, cloid: &str) {
346 self.remove_cloid_mapping(&ustr::Ustr::from(cloid));
347 }
348
349 #[pyo3(name = "clear_cloid_cache")]
353 fn py_clear_cloid_cache(&self) {
354 self.clear_cloid_cache();
355 }
356
357 #[pyo3(name = "cloid_cache_len")]
359 fn py_cloid_cache_len(&self) -> usize {
360 self.cloid_cache_len()
361 }
362
363 #[pyo3(name = "get_cloid_mapping")]
367 fn py_get_cloid_mapping(&self, cloid: &str) -> Option<ClientOrderId> {
368 self.get_cloid_mapping(&ustr::Ustr::from(cloid))
369 }
370
371 #[pyo3(name = "connect")]
373 #[expect(clippy::needless_pass_by_value)]
374 fn py_connect<'py>(
375 &self,
376 py: Python<'py>,
377 loop_: Py<PyAny>,
378 instruments: Vec<Py<PyAny>>,
379 callback: Py<PyAny>,
380 ) -> PyResult<Bound<'py, PyAny>> {
381 let call_soon: Py<PyAny> = loop_.getattr(py, "call_soon_threadsafe")?;
382
383 for inst in instruments {
384 let inst_any = pyobject_to_instrument_any(py, inst)?;
385 self.cache_instrument(inst_any);
386 }
387
388 let mut client = self.clone();
389
390 pyo3_async_runtimes::tokio::future_into_py(py, async move {
391 client.connect().await.map_err(to_pyruntime_err)?;
392
393 get_runtime().spawn(async move {
394 loop {
395 let event = client.next_event().await;
396
397 match event {
398 Some(msg) => {
399 log::trace!("Received WebSocket message: {msg:?}");
400
401 match msg {
402 NautilusWsMessage::Trades(trade_ticks) => {
403 Python::attach(|py| {
404 for tick in trade_ticks {
405 let py_obj = data_to_pycapsule(py, Data::Trade(tick));
406 call_python_threadsafe(py, &call_soon, &callback, py_obj);
407 }
408 });
409 }
410 NautilusWsMessage::Quote(quote_tick) => {
411 Python::attach(|py| {
412 let py_obj = data_to_pycapsule(py, Data::Quote(quote_tick));
413 call_python_threadsafe(py, &call_soon, &callback, py_obj);
414 });
415 }
416 NautilusWsMessage::Deltas(deltas) => {
417 Python::attach(|py| {
418 let py_obj = data_to_pycapsule(
419 py,
420 Data::Deltas(OrderBookDeltas_API::new(deltas)),
421 );
422 call_python_threadsafe(py, &call_soon, &callback, py_obj);
423 });
424 }
425 NautilusWsMessage::Depth10(depth) => {
426 Python::attach(|py| {
427 let py_obj = data_to_pycapsule(py, Data::Depth10(depth));
428 call_python_threadsafe(py, &call_soon, &callback, py_obj);
429 });
430 }
431 NautilusWsMessage::Candle(bar) => {
432 Python::attach(|py| {
433 let py_obj = data_to_pycapsule(py, Data::Bar(bar));
434 call_python_threadsafe(py, &call_soon, &callback, py_obj);
435 });
436 }
437 NautilusWsMessage::MarkPrice(mark_price) => {
438 Python::attach(|py| {
439 let py_obj = data_to_pycapsule(
440 py,
441 Data::MarkPriceUpdate(mark_price),
442 );
443 call_python_threadsafe(py, &call_soon, &callback, py_obj);
444 });
445 }
446 NautilusWsMessage::IndexPrice(index_price) => {
447 Python::attach(|py| {
448 let py_obj = data_to_pycapsule(
449 py,
450 Data::IndexPriceUpdate(index_price),
451 );
452 call_python_threadsafe(py, &call_soon, &callback, py_obj);
453 });
454 }
455 NautilusWsMessage::FundingRate(funding_rate) => {
456 Python::attach(|py| {
457 if let Ok(py_obj) = funding_rate.into_py_any(py) {
458 call_python_threadsafe(py, &call_soon, &callback, py_obj);
459 }
460 });
461 }
462 NautilusWsMessage::CustomData(data) => {
463 Python::attach(|py| match ws_data_to_pyobject(py, data) {
464 Ok(py_obj) => {
465 call_python_threadsafe(py, &call_soon, &callback, py_obj);
466 }
467 Err(e) => {
468 log::error!(
469 "Error converting CustomData to Python object: {e}"
470 );
471 }
472 });
473 }
474 NautilusWsMessage::ExecutionReports(reports) => {
475 Python::attach(|py| {
476 for report in reports {
477 match report {
478 ExecutionReport::Order(order_report) => {
479 log::debug!(
480 "Forwarding order status report: order_id={}, status={:?}",
481 order_report.venue_order_id,
482 order_report.order_status
483 );
484
485 match Py::new(py, order_report) {
486 Ok(py_obj) => {
487 call_python_threadsafe(py, &call_soon, &callback, py_obj.into_any());
488 }
489 Err(e) => {
490 log::error!("Error converting OrderStatusReport to Python: {e}");
491 }
492 }
493 }
494 ExecutionReport::Fill(fill_report) => {
495 log::debug!(
496 "Forwarding fill report: trade_id={}, side={:?}, qty={}, price={}",
497 fill_report.trade_id,
498 fill_report.order_side,
499 fill_report.last_qty,
500 fill_report.last_px
501 );
502
503 match Py::new(py, fill_report) {
504 Ok(py_obj) => {
505 call_python_threadsafe(py, &call_soon, &callback, py_obj.into_any());
506 }
507 Err(e) => {
508 log::error!("Error converting FillReport to Python: {e}");
509 }
510 }
511 }
512 }
513 }
514 });
515 }
516 _ => {
517 log::debug!("Unhandled message type: {msg:?}");
518 }
519 }
520 }
521 None => {
522 log::debug!("WebSocket connection closed");
523 break;
524 }
525 }
526 }
527 });
528
529 Ok(())
530 })
531 }
532
533 #[pyo3(name = "wait_until_active")]
534 fn py_wait_until_active<'py>(
535 &self,
536 py: Python<'py>,
537 timeout_secs: f64,
538 ) -> PyResult<Bound<'py, PyAny>> {
539 let client = self.clone();
540
541 pyo3_async_runtimes::tokio::future_into_py(py, async move {
542 let start = std::time::Instant::now();
543
544 loop {
545 if client.is_active() {
546 return Ok(());
547 }
548
549 if start.elapsed().as_secs_f64() >= timeout_secs {
550 return Err(to_pyruntime_err(format!(
551 "WebSocket connection did not become active within {timeout_secs} seconds"
552 )));
553 }
554
555 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
556 }
557 })
558 }
559
560 #[pyo3(name = "close")]
561 fn py_close<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
562 let mut client = self.clone();
563
564 pyo3_async_runtimes::tokio::future_into_py(py, async move {
565 if let Err(e) = client.disconnect().await {
566 log::warn!("Error on close: {e}");
567 }
568 Ok(())
569 })
570 }
571
572 #[pyo3(name = "subscribe_trades")]
574 fn py_subscribe_trades<'py>(
575 &self,
576 py: Python<'py>,
577 instrument_id: InstrumentId,
578 ) -> PyResult<Bound<'py, PyAny>> {
579 let client = self.clone();
580
581 pyo3_async_runtimes::tokio::future_into_py(py, async move {
582 client
583 .subscribe_trades(instrument_id)
584 .await
585 .map_err(to_pyruntime_err)?;
586 Ok(())
587 })
588 }
589
590 #[pyo3(name = "unsubscribe_trades")]
592 fn py_unsubscribe_trades<'py>(
593 &self,
594 py: Python<'py>,
595 instrument_id: InstrumentId,
596 ) -> PyResult<Bound<'py, PyAny>> {
597 let client = self.clone();
598
599 pyo3_async_runtimes::tokio::future_into_py(py, async move {
600 client
601 .unsubscribe_trades(instrument_id)
602 .await
603 .map_err(to_pyruntime_err)?;
604 Ok(())
605 })
606 }
607
608 #[pyo3(name = "subscribe_all_mids")]
610 fn py_subscribe_all_mids<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
611 let client = self.clone();
612
613 pyo3_async_runtimes::tokio::future_into_py(py, async move {
614 client
615 .subscribe_all_mids()
616 .await
617 .map_err(to_pyruntime_err)?;
618 Ok(())
619 })
620 }
621
622 #[pyo3(name = "subscribe_all_dexs_asset_ctxs")]
624 fn py_subscribe_all_dexs_asset_ctxs<'py>(
625 &self,
626 py: Python<'py>,
627 ) -> PyResult<Bound<'py, PyAny>> {
628 let client = self.clone();
629
630 pyo3_async_runtimes::tokio::future_into_py(py, async move {
631 client
632 .subscribe_all_dexs_asset_ctxs()
633 .await
634 .map_err(to_pyruntime_err)?;
635 Ok(())
636 })
637 }
638
639 #[pyo3(name = "subscribe_all_mids_with_dex")]
641 fn py_subscribe_all_mids_with_dex<'py>(
642 &self,
643 py: Python<'py>,
644 dex: Option<String>,
645 ) -> PyResult<Bound<'py, PyAny>> {
646 let client = self.clone();
647
648 pyo3_async_runtimes::tokio::future_into_py(py, async move {
649 client
650 .subscribe_all_mids_with_dex(dex.as_deref())
651 .await
652 .map_err(to_pyruntime_err)?;
653 Ok(())
654 })
655 }
656
657 #[pyo3(name = "subscribe_book")]
659 fn py_subscribe_book<'py>(
660 &self,
661 py: Python<'py>,
662 instrument_id: InstrumentId,
663 ) -> PyResult<Bound<'py, PyAny>> {
664 let client = self.clone();
665
666 pyo3_async_runtimes::tokio::future_into_py(py, async move {
667 client
668 .subscribe_book(instrument_id)
669 .await
670 .map_err(to_pyruntime_err)?;
671 Ok(())
672 })
673 }
674
675 #[pyo3(name = "unsubscribe_book")]
677 fn py_unsubscribe_book<'py>(
678 &self,
679 py: Python<'py>,
680 instrument_id: InstrumentId,
681 ) -> PyResult<Bound<'py, PyAny>> {
682 let client = self.clone();
683
684 pyo3_async_runtimes::tokio::future_into_py(py, async move {
685 client
686 .unsubscribe_book(instrument_id)
687 .await
688 .map_err(to_pyruntime_err)?;
689 Ok(())
690 })
691 }
692
693 #[pyo3(name = "subscribe_book_deltas")]
694 fn py_subscribe_book_deltas<'py>(
695 &self,
696 py: Python<'py>,
697 instrument_id: InstrumentId,
698 _book_type: u8,
699 _depth: u64,
700 ) -> PyResult<Bound<'py, PyAny>> {
701 let client = self.clone();
702
703 pyo3_async_runtimes::tokio::future_into_py(py, async move {
704 client
705 .subscribe_book(instrument_id)
706 .await
707 .map_err(to_pyruntime_err)?;
708 Ok(())
709 })
710 }
711
712 #[pyo3(name = "unsubscribe_book_deltas")]
713 fn py_unsubscribe_book_deltas<'py>(
714 &self,
715 py: Python<'py>,
716 instrument_id: InstrumentId,
717 ) -> PyResult<Bound<'py, PyAny>> {
718 let client = self.clone();
719
720 pyo3_async_runtimes::tokio::future_into_py(py, async move {
721 client
722 .unsubscribe_book(instrument_id)
723 .await
724 .map_err(to_pyruntime_err)?;
725 Ok(())
726 })
727 }
728
729 #[pyo3(name = "subscribe_book_snapshots")]
730 fn py_subscribe_book_snapshots<'py>(
731 &self,
732 py: Python<'py>,
733 instrument_id: InstrumentId,
734 _book_type: u8,
735 _depth: u64,
736 ) -> PyResult<Bound<'py, PyAny>> {
737 let client = self.clone();
738
739 pyo3_async_runtimes::tokio::future_into_py(py, async move {
740 client
741 .subscribe_book(instrument_id)
742 .await
743 .map_err(to_pyruntime_err)?;
744 Ok(())
745 })
746 }
747
748 #[pyo3(name = "subscribe_quotes")]
750 fn py_subscribe_quotes<'py>(
751 &self,
752 py: Python<'py>,
753 instrument_id: InstrumentId,
754 ) -> PyResult<Bound<'py, PyAny>> {
755 let client = self.clone();
756
757 pyo3_async_runtimes::tokio::future_into_py(py, async move {
758 client
759 .subscribe_quotes(instrument_id)
760 .await
761 .map_err(to_pyruntime_err)?;
762 Ok(())
763 })
764 }
765
766 #[pyo3(name = "unsubscribe_quotes")]
768 fn py_unsubscribe_quotes<'py>(
769 &self,
770 py: Python<'py>,
771 instrument_id: InstrumentId,
772 ) -> PyResult<Bound<'py, PyAny>> {
773 let client = self.clone();
774
775 pyo3_async_runtimes::tokio::future_into_py(py, async move {
776 client
777 .unsubscribe_quotes(instrument_id)
778 .await
779 .map_err(to_pyruntime_err)?;
780 Ok(())
781 })
782 }
783
784 #[pyo3(name = "subscribe_bars")]
786 fn py_subscribe_bars<'py>(
787 &self,
788 py: Python<'py>,
789 bar_type: BarType,
790 ) -> PyResult<Bound<'py, PyAny>> {
791 let client = self.clone();
792
793 pyo3_async_runtimes::tokio::future_into_py(py, async move {
794 client
795 .subscribe_bars(bar_type)
796 .await
797 .map_err(to_pyruntime_err)?;
798 Ok(())
799 })
800 }
801
802 #[pyo3(name = "unsubscribe_bars")]
804 fn py_unsubscribe_bars<'py>(
805 &self,
806 py: Python<'py>,
807 bar_type: BarType,
808 ) -> PyResult<Bound<'py, PyAny>> {
809 let client = self.clone();
810
811 pyo3_async_runtimes::tokio::future_into_py(py, async move {
812 client
813 .unsubscribe_bars(bar_type)
814 .await
815 .map_err(to_pyruntime_err)?;
816 Ok(())
817 })
818 }
819
820 #[pyo3(name = "unsubscribe_all_mids")]
822 fn py_unsubscribe_all_mids<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
823 let client = self.clone();
824
825 pyo3_async_runtimes::tokio::future_into_py(py, async move {
826 client
827 .unsubscribe_all_mids()
828 .await
829 .map_err(to_pyruntime_err)?;
830 Ok(())
831 })
832 }
833
834 #[pyo3(name = "unsubscribe_all_dexs_asset_ctxs")]
836 fn py_unsubscribe_all_dexs_asset_ctxs<'py>(
837 &self,
838 py: Python<'py>,
839 ) -> PyResult<Bound<'py, PyAny>> {
840 let client = self.clone();
841
842 pyo3_async_runtimes::tokio::future_into_py(py, async move {
843 client
844 .unsubscribe_all_dexs_asset_ctxs()
845 .await
846 .map_err(to_pyruntime_err)?;
847 Ok(())
848 })
849 }
850
851 #[pyo3(name = "unsubscribe_all_mids_with_dex")]
853 fn py_unsubscribe_all_mids_with_dex<'py>(
854 &self,
855 py: Python<'py>,
856 dex: Option<String>,
857 ) -> PyResult<Bound<'py, PyAny>> {
858 let client = self.clone();
859
860 pyo3_async_runtimes::tokio::future_into_py(py, async move {
861 client
862 .unsubscribe_all_mids_with_dex(dex.as_deref())
863 .await
864 .map_err(to_pyruntime_err)?;
865 Ok(())
866 })
867 }
868
869 #[pyo3(name = "subscribe_order_updates")]
871 fn py_subscribe_order_updates<'py>(
872 &self,
873 py: Python<'py>,
874 user: String,
875 ) -> PyResult<Bound<'py, PyAny>> {
876 let client = self.clone();
877
878 pyo3_async_runtimes::tokio::future_into_py(py, async move {
879 client
880 .subscribe_order_updates(&user)
881 .await
882 .map_err(to_pyruntime_err)?;
883 Ok(())
884 })
885 }
886
887 #[pyo3(name = "subscribe_user_events")]
889 fn py_subscribe_user_events<'py>(
890 &self,
891 py: Python<'py>,
892 user: String,
893 ) -> PyResult<Bound<'py, PyAny>> {
894 let client = self.clone();
895
896 pyo3_async_runtimes::tokio::future_into_py(py, async move {
897 client
898 .subscribe_user_events(&user)
899 .await
900 .map_err(to_pyruntime_err)?;
901 Ok(())
902 })
903 }
904
905 #[pyo3(name = "subscribe_user_fills")]
910 fn py_subscribe_user_fills<'py>(
911 &self,
912 py: Python<'py>,
913 user: String,
914 ) -> PyResult<Bound<'py, PyAny>> {
915 let client = self.clone();
916
917 pyo3_async_runtimes::tokio::future_into_py(py, async move {
918 client
919 .subscribe_user_fills(&user)
920 .await
921 .map_err(to_pyruntime_err)?;
922 Ok(())
923 })
924 }
925
926 #[pyo3(name = "subscribe_mark_prices")]
928 fn py_subscribe_mark_prices<'py>(
929 &self,
930 py: Python<'py>,
931 instrument_id: InstrumentId,
932 ) -> PyResult<Bound<'py, PyAny>> {
933 let client = self.clone();
934
935 pyo3_async_runtimes::tokio::future_into_py(py, async move {
936 client
937 .subscribe_mark_prices(instrument_id)
938 .await
939 .map_err(to_pyruntime_err)?;
940 Ok(())
941 })
942 }
943
944 #[pyo3(name = "unsubscribe_mark_prices")]
946 fn py_unsubscribe_mark_prices<'py>(
947 &self,
948 py: Python<'py>,
949 instrument_id: InstrumentId,
950 ) -> PyResult<Bound<'py, PyAny>> {
951 let client = self.clone();
952
953 pyo3_async_runtimes::tokio::future_into_py(py, async move {
954 client
955 .unsubscribe_mark_prices(instrument_id)
956 .await
957 .map_err(to_pyruntime_err)?;
958 Ok(())
959 })
960 }
961
962 #[pyo3(name = "subscribe_index_prices")]
964 fn py_subscribe_index_prices<'py>(
965 &self,
966 py: Python<'py>,
967 instrument_id: InstrumentId,
968 ) -> PyResult<Bound<'py, PyAny>> {
969 let client = self.clone();
970
971 pyo3_async_runtimes::tokio::future_into_py(py, async move {
972 client
973 .subscribe_index_prices(instrument_id)
974 .await
975 .map_err(to_pyruntime_err)?;
976 Ok(())
977 })
978 }
979
980 #[pyo3(name = "unsubscribe_index_prices")]
982 fn py_unsubscribe_index_prices<'py>(
983 &self,
984 py: Python<'py>,
985 instrument_id: InstrumentId,
986 ) -> PyResult<Bound<'py, PyAny>> {
987 let client = self.clone();
988
989 pyo3_async_runtimes::tokio::future_into_py(py, async move {
990 client
991 .unsubscribe_index_prices(instrument_id)
992 .await
993 .map_err(to_pyruntime_err)?;
994 Ok(())
995 })
996 }
997
998 #[pyo3(name = "subscribe_funding_rates")]
1000 fn py_subscribe_funding_rates<'py>(
1001 &self,
1002 py: Python<'py>,
1003 instrument_id: InstrumentId,
1004 ) -> PyResult<Bound<'py, PyAny>> {
1005 let client = self.clone();
1006
1007 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1008 client
1009 .subscribe_funding_rates(instrument_id)
1010 .await
1011 .map_err(to_pyruntime_err)?;
1012 Ok(())
1013 })
1014 }
1015
1016 #[pyo3(name = "subscribe_open_interest")]
1018 fn py_subscribe_open_interest<'py>(
1019 &self,
1020 py: Python<'py>,
1021 instrument_id: InstrumentId,
1022 ) -> PyResult<Bound<'py, PyAny>> {
1023 let client = self.clone();
1024
1025 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1026 client
1027 .subscribe_open_interest(instrument_id)
1028 .await
1029 .map_err(to_pyruntime_err)?;
1030 Ok(())
1031 })
1032 }
1033
1034 #[pyo3(name = "unsubscribe_funding_rates")]
1036 fn py_unsubscribe_funding_rates<'py>(
1037 &self,
1038 py: Python<'py>,
1039 instrument_id: InstrumentId,
1040 ) -> PyResult<Bound<'py, PyAny>> {
1041 let client = self.clone();
1042
1043 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1044 client
1045 .unsubscribe_funding_rates(instrument_id)
1046 .await
1047 .map_err(to_pyruntime_err)?;
1048 Ok(())
1049 })
1050 }
1051
1052 #[pyo3(name = "unsubscribe_open_interest")]
1054 fn py_unsubscribe_open_interest<'py>(
1055 &self,
1056 py: Python<'py>,
1057 instrument_id: InstrumentId,
1058 ) -> PyResult<Bound<'py, PyAny>> {
1059 let client = self.clone();
1060
1061 pyo3_async_runtimes::tokio::future_into_py(py, async move {
1062 client
1063 .unsubscribe_open_interest(instrument_id)
1064 .await
1065 .map_err(to_pyruntime_err)?;
1066 Ok(())
1067 })
1068 }
1069}