1use futures_util::StreamExt;
39use nautilus_common::live::get_runtime;
40use nautilus_core::python::{call_python, to_pyruntime_err, to_pyvalue_err};
41use nautilus_model::{
42 data::{BarType, Data, OrderBookDeltas_API},
43 enums::{OrderSide, OrderType, TimeInForce},
44 identifiers::{AccountId, ClientOrderId, InstrumentId, StrategyId, TraderId},
45 python::{
46 data::data_to_pycapsule,
47 instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
48 },
49 types::{Price, Quantity},
50};
51use pyo3::{IntoPyObjectExt, prelude::*};
52
53use crate::{
54 common::enums::DeribitTimeInForce,
55 websocket::{
56 client::DeribitWebSocketClient,
57 enums::DeribitUpdateInterval,
58 messages::{DeribitOrderParams, NautilusWsMessage},
59 },
60};
61
62fn call_python_with_data<F>(callback: &Py<PyAny>, data_converter: F)
63where
64 F: FnOnce(Python) -> PyResult<Py<PyAny>>,
65{
66 Python::attach(|py| match data_converter(py) {
67 Ok(py_obj) => call_python(py, callback, py_obj),
68 Err(e) => log::error!("Failed to convert data to Python object: {e}"),
69 });
70}
71
72#[pymethods]
73impl DeribitWebSocketClient {
74 #[new]
75 #[pyo3(signature = (
76 url=None,
77 api_key=None,
78 api_secret=None,
79 heartbeat_interval=None,
80 is_testnet=false,
81 ))]
82 fn py_new(
83 url: Option<String>,
84 api_key: Option<String>,
85 api_secret: Option<String>,
86 heartbeat_interval: Option<u64>,
87 is_testnet: bool,
88 ) -> PyResult<Self> {
89 Self::new(url, api_key, api_secret, heartbeat_interval, is_testnet).map_err(to_pyvalue_err)
90 }
91
92 #[staticmethod]
93 #[pyo3(name = "new_public")]
94 fn py_new_public(is_testnet: bool) -> PyResult<Self> {
95 Self::new_public(is_testnet).map_err(to_pyvalue_err)
96 }
97
98 #[staticmethod]
99 #[pyo3(name = "with_credentials", signature = (is_testnet, account_id = None))]
100 fn py_with_credentials(is_testnet: bool, account_id: Option<AccountId>) -> PyResult<Self> {
101 let mut client = Self::with_credentials(is_testnet).map_err(to_pyvalue_err)?;
102 if let Some(id) = account_id {
103 client.set_account_id(id);
104 }
105 Ok(client)
106 }
107
108 #[getter]
109 #[pyo3(name = "url")]
110 #[must_use]
111 pub fn py_url(&self) -> String {
112 self.url().to_string()
113 }
114
115 #[getter]
116 #[pyo3(name = "is_testnet")]
117 #[must_use]
118 pub fn py_is_testnet(&self) -> bool {
119 self.url().contains("test")
121 }
122
123 #[pyo3(name = "is_active")]
124 #[must_use]
125 fn py_is_active(&self) -> bool {
126 self.is_active()
127 }
128
129 #[pyo3(name = "is_closed")]
130 #[must_use]
131 fn py_is_closed(&self) -> bool {
132 self.is_closed()
133 }
134
135 #[pyo3(name = "has_credentials")]
136 #[must_use]
137 fn py_has_credentials(&self) -> bool {
138 self.has_credentials()
139 }
140
141 #[pyo3(name = "is_authenticated")]
142 #[must_use]
143 fn py_is_authenticated(&self) -> bool {
144 self.is_authenticated()
145 }
146
147 #[pyo3(name = "cancel_all_requests")]
148 pub fn py_cancel_all_requests(&self) {
149 self.cancel_all_requests();
150 }
151
152 #[pyo3(name = "cache_instruments")]
156 pub fn py_cache_instruments(
157 &self,
158 py: Python<'_>,
159 instruments: Vec<Py<PyAny>>,
160 ) -> PyResult<()> {
161 let instruments: Result<Vec<_>, _> = instruments
162 .into_iter()
163 .map(|inst| pyobject_to_instrument_any(py, inst))
164 .collect();
165 self.cache_instruments(instruments?);
166 Ok(())
167 }
168
169 #[pyo3(name = "cache_instrument")]
173 pub fn py_cache_instrument(&self, py: Python<'_>, instrument: Py<PyAny>) -> PyResult<()> {
174 let inst = pyobject_to_instrument_any(py, instrument)?;
175 self.cache_instrument(inst);
176 Ok(())
177 }
178
179 #[pyo3(name = "set_account_id")]
180 pub fn py_set_account_id(&mut self, account_id: AccountId) {
181 self.set_account_id(account_id);
182 }
183
184 #[pyo3(name = "set_bars_timestamp_on_close")]
185 pub fn py_set_bars_timestamp_on_close(&mut self, value: bool) {
186 self.set_bars_timestamp_on_close(value);
187 }
188
189 #[pyo3(name = "connect")]
190 fn py_connect<'py>(
191 &mut self,
192 py: Python<'py>,
193 instruments: Vec<Py<PyAny>>,
194 callback: Py<PyAny>,
195 ) -> PyResult<Bound<'py, PyAny>> {
196 let mut instruments_any = Vec::new();
197 for inst in instruments {
198 let inst_any = pyobject_to_instrument_any(py, inst)?;
199 instruments_any.push(inst_any);
200 }
201
202 self.cache_instruments(instruments_any);
203
204 let mut client = self.clone();
205
206 pyo3_async_runtimes::tokio::future_into_py(py, async move {
207 client.connect().await.map_err(to_pyruntime_err)?;
208
209 let stream = client.stream();
210
211 get_runtime().spawn(async move {
213 let _client = client;
214 tokio::pin!(stream);
215
216 while let Some(msg) = stream.next().await {
217 match msg {
218 NautilusWsMessage::Instrument(msg) => {
219 call_python_with_data(&callback, |py| {
220 instrument_any_to_pyobject(py, *msg)
221 });
222 }
223 NautilusWsMessage::Data(msg) => Python::attach(|py| {
224 for data in msg {
225 let py_obj = data_to_pycapsule(py, data);
226 call_python(py, &callback, py_obj);
227 }
228 }),
229 NautilusWsMessage::Deltas(msg) => Python::attach(|py| {
230 let py_obj =
231 data_to_pycapsule(py, Data::Deltas(OrderBookDeltas_API::new(msg)));
232 call_python(py, &callback, py_obj);
233 }),
234 NautilusWsMessage::Error(err) => {
235 log::error!("WebSocket error: {err}");
236 }
237 NautilusWsMessage::Reconnected => {
238 log::info!("WebSocket reconnected");
239 }
240 NautilusWsMessage::Authenticated(auth_result) => {
241 log::info!("WebSocket authenticated (scope: {})", auth_result.scope);
242 }
243 NautilusWsMessage::Raw(msg) => {
244 log::debug!("Received raw message, skipping: {msg}");
245 }
246 NautilusWsMessage::FundingRates(funding_rates) => Python::attach(|py| {
247 for funding_rate in funding_rates {
248 match Py::new(py, funding_rate) {
249 Ok(py_obj) => call_python(py, &callback, py_obj.into_any()),
250 Err(e) => {
251 log::error!("Failed to create FundingRateUpdate: {e}");
252 }
253 }
254 }
255 }),
256 NautilusWsMessage::OrderStatusReports(reports) => Python::attach(|py| {
258 for report in reports {
259 match Py::new(py, report) {
260 Ok(py_obj) => call_python(py, &callback, py_obj.into_any()),
261 Err(e) => {
262 log::error!("Failed to create OrderStatusReport: {e}");
263 }
264 }
265 }
266 }),
267 NautilusWsMessage::FillReports(reports) => Python::attach(|py| {
268 for report in reports {
269 match Py::new(py, report) {
270 Ok(py_obj) => call_python(py, &callback, py_obj.into_any()),
271 Err(e) => log::error!("Failed to create FillReport: {e}"),
272 }
273 }
274 }),
275 NautilusWsMessage::OrderRejected(msg) => {
276 call_python_with_data(&callback, |py| msg.into_py_any(py));
277 }
278 NautilusWsMessage::OrderAccepted(msg) => {
279 call_python_with_data(&callback, |py| msg.into_py_any(py));
280 }
281 NautilusWsMessage::OrderCanceled(msg) => {
282 call_python_with_data(&callback, |py| msg.into_py_any(py));
283 }
284 NautilusWsMessage::OrderExpired(msg) => {
285 call_python_with_data(&callback, |py| msg.into_py_any(py));
286 }
287 NautilusWsMessage::OrderUpdated(msg) => {
288 call_python_with_data(&callback, |py| msg.into_py_any(py));
289 }
290 NautilusWsMessage::OrderCancelRejected(msg) => {
291 call_python_with_data(&callback, |py| msg.into_py_any(py));
292 }
293 NautilusWsMessage::OrderModifyRejected(msg) => {
294 call_python_with_data(&callback, |py| msg.into_py_any(py));
295 }
296 NautilusWsMessage::AccountState(msg) => {
297 call_python_with_data(&callback, |py| msg.into_py_any(py));
298 }
299 }
300 }
301 });
302
303 Ok(())
304 })
305 }
306
307 #[pyo3(name = "wait_until_active")]
308 fn py_wait_until_active<'py>(
309 &self,
310 py: Python<'py>,
311 timeout_secs: f64,
312 ) -> PyResult<Bound<'py, PyAny>> {
313 let client = self.clone();
314
315 pyo3_async_runtimes::tokio::future_into_py(py, async move {
316 client
317 .wait_until_active(timeout_secs)
318 .await
319 .map_err(to_pyruntime_err)?;
320 Ok(())
321 })
322 }
323
324 #[pyo3(name = "close")]
325 fn py_close<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
326 let client = self.clone();
327
328 pyo3_async_runtimes::tokio::future_into_py(py, async move {
329 if let Err(e) = client.close().await {
330 log::error!("Error on close: {e}");
331 }
332 Ok(())
333 })
334 }
335
336 #[pyo3(name = "authenticate")]
337 #[pyo3(signature = (session_name=None))]
338 fn py_authenticate<'py>(
339 &self,
340 py: Python<'py>,
341 session_name: Option<String>,
342 ) -> PyResult<Bound<'py, PyAny>> {
343 let client = self.clone();
344
345 pyo3_async_runtimes::tokio::future_into_py(py, async move {
346 client
347 .authenticate(session_name.as_deref())
348 .await
349 .map_err(to_pyruntime_err)?;
350 Ok(())
351 })
352 }
353
354 #[pyo3(name = "authenticate_session")]
355 fn py_authenticate_session<'py>(
356 &self,
357 py: Python<'py>,
358 session_name: String,
359 ) -> PyResult<Bound<'py, PyAny>> {
360 let client = self.clone();
361
362 pyo3_async_runtimes::tokio::future_into_py(py, async move {
363 client
364 .authenticate_session(&session_name)
365 .await
366 .map_err(|e| {
367 to_pyruntime_err(format!(
368 "Failed to authenticate Deribit websocket session '{session_name}': {e}"
369 ))
370 })?;
371 Ok(())
372 })
373 }
374
375 #[pyo3(name = "subscribe_trades")]
376 #[pyo3(signature = (instrument_id, interval=None))]
377 fn py_subscribe_trades<'py>(
378 &self,
379 py: Python<'py>,
380 instrument_id: InstrumentId,
381 interval: Option<DeribitUpdateInterval>,
382 ) -> PyResult<Bound<'py, PyAny>> {
383 let client = self.clone();
384
385 pyo3_async_runtimes::tokio::future_into_py(py, async move {
386 client
387 .subscribe_trades(instrument_id, interval)
388 .await
389 .map_err(to_pyvalue_err)
390 })
391 }
392
393 #[pyo3(name = "unsubscribe_trades")]
394 #[pyo3(signature = (instrument_id, interval=None))]
395 fn py_unsubscribe_trades<'py>(
396 &self,
397 py: Python<'py>,
398 instrument_id: InstrumentId,
399 interval: Option<DeribitUpdateInterval>,
400 ) -> PyResult<Bound<'py, PyAny>> {
401 let client = self.clone();
402
403 pyo3_async_runtimes::tokio::future_into_py(py, async move {
404 client
405 .unsubscribe_trades(instrument_id, interval)
406 .await
407 .map_err(to_pyvalue_err)
408 })
409 }
410
411 #[pyo3(name = "subscribe_book")]
412 #[pyo3(signature = (instrument_id, interval=None, depth=None))]
413 fn py_subscribe_book<'py>(
414 &self,
415 py: Python<'py>,
416 instrument_id: InstrumentId,
417 interval: Option<DeribitUpdateInterval>,
418 depth: Option<u32>,
419 ) -> PyResult<Bound<'py, PyAny>> {
420 let client = self.clone();
421
422 pyo3_async_runtimes::tokio::future_into_py(py, async move {
423 if let Some(d) = depth {
424 client
425 .subscribe_book_grouped(instrument_id, "none", d, interval)
426 .await
427 .map_err(to_pyvalue_err)
428 } else {
429 client
430 .subscribe_book(instrument_id, interval)
431 .await
432 .map_err(to_pyvalue_err)
433 }
434 })
435 }
436
437 #[pyo3(name = "unsubscribe_book")]
438 #[pyo3(signature = (instrument_id, interval=None, depth=None))]
439 fn py_unsubscribe_book<'py>(
440 &self,
441 py: Python<'py>,
442 instrument_id: InstrumentId,
443 interval: Option<DeribitUpdateInterval>,
444 depth: Option<u32>,
445 ) -> PyResult<Bound<'py, PyAny>> {
446 let client = self.clone();
447
448 pyo3_async_runtimes::tokio::future_into_py(py, async move {
449 if let Some(d) = depth {
450 client
451 .unsubscribe_book_grouped(instrument_id, "none", d, interval)
452 .await
453 .map_err(to_pyvalue_err)
454 } else {
455 client
456 .unsubscribe_book(instrument_id, interval)
457 .await
458 .map_err(to_pyvalue_err)
459 }
460 })
461 }
462
463 #[pyo3(name = "subscribe_book_grouped")]
464 #[pyo3(signature = (instrument_id, group, depth, interval=None))]
465 fn py_subscribe_book_grouped<'py>(
466 &self,
467 py: Python<'py>,
468 instrument_id: InstrumentId,
469 group: String,
470 depth: u32,
471 interval: Option<DeribitUpdateInterval>,
472 ) -> PyResult<Bound<'py, PyAny>> {
473 let client = self.clone();
474
475 pyo3_async_runtimes::tokio::future_into_py(py, async move {
476 client
477 .subscribe_book_grouped(instrument_id, &group, depth, interval)
478 .await
479 .map_err(to_pyvalue_err)
480 })
481 }
482
483 #[pyo3(name = "unsubscribe_book_grouped")]
484 #[pyo3(signature = (instrument_id, group, depth, interval=None))]
485 fn py_unsubscribe_book_grouped<'py>(
486 &self,
487 py: Python<'py>,
488 instrument_id: InstrumentId,
489 group: String,
490 depth: u32,
491 interval: Option<DeribitUpdateInterval>,
492 ) -> PyResult<Bound<'py, PyAny>> {
493 let client = self.clone();
494
495 pyo3_async_runtimes::tokio::future_into_py(py, async move {
496 client
497 .unsubscribe_book_grouped(instrument_id, &group, depth, interval)
498 .await
499 .map_err(to_pyvalue_err)
500 })
501 }
502
503 #[pyo3(name = "subscribe_ticker")]
504 #[pyo3(signature = (instrument_id, interval=None))]
505 fn py_subscribe_ticker<'py>(
506 &self,
507 py: Python<'py>,
508 instrument_id: InstrumentId,
509 interval: Option<DeribitUpdateInterval>,
510 ) -> PyResult<Bound<'py, PyAny>> {
511 let client = self.clone();
512
513 pyo3_async_runtimes::tokio::future_into_py(py, async move {
514 client
515 .subscribe_ticker(instrument_id, interval)
516 .await
517 .map_err(to_pyvalue_err)
518 })
519 }
520
521 #[pyo3(name = "unsubscribe_ticker")]
522 #[pyo3(signature = (instrument_id, interval=None))]
523 fn py_unsubscribe_ticker<'py>(
524 &self,
525 py: Python<'py>,
526 instrument_id: InstrumentId,
527 interval: Option<DeribitUpdateInterval>,
528 ) -> PyResult<Bound<'py, PyAny>> {
529 let client = self.clone();
530
531 pyo3_async_runtimes::tokio::future_into_py(py, async move {
532 client
533 .unsubscribe_ticker(instrument_id, interval)
534 .await
535 .map_err(to_pyvalue_err)
536 })
537 }
538
539 #[pyo3(name = "subscribe_quotes")]
540 fn py_subscribe_quotes<'py>(
541 &self,
542 py: Python<'py>,
543 instrument_id: InstrumentId,
544 ) -> PyResult<Bound<'py, PyAny>> {
545 let client = self.clone();
546
547 pyo3_async_runtimes::tokio::future_into_py(py, async move {
548 client
549 .subscribe_quotes(instrument_id)
550 .await
551 .map_err(to_pyvalue_err)
552 })
553 }
554
555 #[pyo3(name = "unsubscribe_quotes")]
556 fn py_unsubscribe_quotes<'py>(
557 &self,
558 py: Python<'py>,
559 instrument_id: InstrumentId,
560 ) -> PyResult<Bound<'py, PyAny>> {
561 let client = self.clone();
562
563 pyo3_async_runtimes::tokio::future_into_py(py, async move {
564 client
565 .unsubscribe_quotes(instrument_id)
566 .await
567 .map_err(to_pyvalue_err)
568 })
569 }
570
571 #[pyo3(name = "subscribe_user_orders")]
572 fn py_subscribe_user_orders<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
573 let client = self.clone();
574
575 pyo3_async_runtimes::tokio::future_into_py(py, async move {
576 client.subscribe_user_orders().await.map_err(to_pyvalue_err)
577 })
578 }
579
580 #[pyo3(name = "unsubscribe_user_orders")]
581 fn py_unsubscribe_user_orders<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
582 let client = self.clone();
583
584 pyo3_async_runtimes::tokio::future_into_py(py, async move {
585 client
586 .unsubscribe_user_orders()
587 .await
588 .map_err(to_pyvalue_err)
589 })
590 }
591
592 #[pyo3(name = "subscribe_user_trades")]
593 fn py_subscribe_user_trades<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
594 let client = self.clone();
595
596 pyo3_async_runtimes::tokio::future_into_py(py, async move {
597 client.subscribe_user_trades().await.map_err(to_pyvalue_err)
598 })
599 }
600
601 #[pyo3(name = "unsubscribe_user_trades")]
602 fn py_unsubscribe_user_trades<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
603 let client = self.clone();
604
605 pyo3_async_runtimes::tokio::future_into_py(py, async move {
606 client
607 .unsubscribe_user_trades()
608 .await
609 .map_err(to_pyvalue_err)
610 })
611 }
612
613 #[pyo3(name = "subscribe_user_portfolio")]
614 fn py_subscribe_user_portfolio<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
615 let client = self.clone();
616
617 pyo3_async_runtimes::tokio::future_into_py(py, async move {
618 client
619 .subscribe_user_portfolio()
620 .await
621 .map_err(to_pyvalue_err)
622 })
623 }
624
625 #[pyo3(name = "unsubscribe_user_portfolio")]
626 fn py_unsubscribe_user_portfolio<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
627 let client = self.clone();
628
629 pyo3_async_runtimes::tokio::future_into_py(py, async move {
630 client
631 .unsubscribe_user_portfolio()
632 .await
633 .map_err(to_pyvalue_err)
634 })
635 }
636
637 #[pyo3(name = "subscribe")]
638 fn py_subscribe<'py>(
639 &self,
640 py: Python<'py>,
641 channels: Vec<String>,
642 ) -> PyResult<Bound<'py, PyAny>> {
643 let client = self.clone();
644
645 pyo3_async_runtimes::tokio::future_into_py(py, async move {
646 client.subscribe(channels).await.map_err(to_pyvalue_err)
647 })
648 }
649
650 #[pyo3(name = "unsubscribe")]
651 fn py_unsubscribe<'py>(
652 &self,
653 py: Python<'py>,
654 channels: Vec<String>,
655 ) -> PyResult<Bound<'py, PyAny>> {
656 let client = self.clone();
657
658 pyo3_async_runtimes::tokio::future_into_py(py, async move {
659 client.unsubscribe(channels).await.map_err(to_pyvalue_err)
660 })
661 }
662
663 #[pyo3(name = "subscribe_instrument_state")]
664 fn py_subscribe_instrument_state<'py>(
665 &self,
666 py: Python<'py>,
667 kind: String,
668 currency: String,
669 ) -> PyResult<Bound<'py, PyAny>> {
670 let client = self.clone();
671
672 pyo3_async_runtimes::tokio::future_into_py(py, async move {
673 client
674 .subscribe_instrument_state(&kind, ¤cy)
675 .await
676 .map_err(to_pyvalue_err)
677 })
678 }
679
680 #[pyo3(name = "unsubscribe_instrument_state")]
681 fn py_unsubscribe_instrument_state<'py>(
682 &self,
683 py: Python<'py>,
684 kind: String,
685 currency: String,
686 ) -> PyResult<Bound<'py, PyAny>> {
687 let client = self.clone();
688
689 pyo3_async_runtimes::tokio::future_into_py(py, async move {
690 client
691 .unsubscribe_instrument_state(&kind, ¤cy)
692 .await
693 .map_err(to_pyvalue_err)
694 })
695 }
696
697 #[pyo3(name = "subscribe_perpetual_interest_rates")]
698 #[pyo3(signature = (instrument_id, interval=None))]
699 fn py_subscribe_perpetual_interest_rates<'py>(
700 &self,
701 py: Python<'py>,
702 instrument_id: InstrumentId,
703 interval: Option<DeribitUpdateInterval>,
704 ) -> PyResult<Bound<'py, PyAny>> {
705 let client = self.clone();
706
707 pyo3_async_runtimes::tokio::future_into_py(py, async move {
708 client
709 .subscribe_perpetual_interests_rates_updates(instrument_id, interval)
710 .await
711 .map_err(to_pyvalue_err)
712 })
713 }
714
715 #[pyo3(name = "unsubscribe_perpetual_interest_rates")]
716 #[pyo3(signature = (instrument_id, interval=None))]
717 fn py_unsubscribe_perpetual_interest_rates<'py>(
718 &self,
719 py: Python<'py>,
720 instrument_id: InstrumentId,
721 interval: Option<DeribitUpdateInterval>,
722 ) -> PyResult<Bound<'py, PyAny>> {
723 let client = self.clone();
724
725 pyo3_async_runtimes::tokio::future_into_py(py, async move {
726 client
727 .unsubscribe_perpetual_interest_rates_updates(instrument_id, interval)
728 .await
729 .map_err(to_pyvalue_err)
730 })
731 }
732
733 #[pyo3(name = "subscribe_chart")]
734 fn py_subscribe_chart<'py>(
735 &self,
736 py: Python<'py>,
737 instrument_id: InstrumentId,
738 resolution: String,
739 ) -> PyResult<Bound<'py, PyAny>> {
740 let client = self.clone();
741
742 pyo3_async_runtimes::tokio::future_into_py(py, async move {
743 client
744 .subscribe_chart(instrument_id, &resolution)
745 .await
746 .map_err(to_pyvalue_err)
747 })
748 }
749
750 #[pyo3(name = "unsubscribe_chart")]
751 fn py_unsubscribe_chart<'py>(
752 &self,
753 py: Python<'py>,
754 instrument_id: InstrumentId,
755 resolution: String,
756 ) -> PyResult<Bound<'py, PyAny>> {
757 let client = self.clone();
758
759 pyo3_async_runtimes::tokio::future_into_py(py, async move {
760 client
761 .unsubscribe_chart(instrument_id, &resolution)
762 .await
763 .map_err(to_pyvalue_err)
764 })
765 }
766
767 #[pyo3(name = "subscribe_bars")]
768 fn py_subscribe_bars<'py>(
769 &self,
770 py: Python<'py>,
771 bar_type: BarType,
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 .subscribe_bars(bar_type)
778 .await
779 .map_err(to_pyvalue_err)
780 })
781 }
782
783 #[pyo3(name = "unsubscribe_bars")]
784 fn py_unsubscribe_bars<'py>(
785 &self,
786 py: Python<'py>,
787 bar_type: BarType,
788 ) -> PyResult<Bound<'py, PyAny>> {
789 let client = self.clone();
790
791 pyo3_async_runtimes::tokio::future_into_py(py, async move {
792 client
793 .unsubscribe_bars(bar_type)
794 .await
795 .map_err(to_pyvalue_err)
796 })
797 }
798
799 #[pyo3(name = "submit_order")]
800 #[pyo3(signature = (
801 order_side,
802 quantity,
803 order_type,
804 client_order_id,
805 trader_id,
806 strategy_id,
807 instrument_id,
808 price=None,
809 time_in_force=None,
810 post_only=false,
811 reduce_only=false,
812 trigger_price=None,
813 trigger=None,
814 ))]
815 #[allow(clippy::too_many_arguments)]
816 fn py_submit_order<'py>(
817 &self,
818 py: Python<'py>,
819 order_side: OrderSide,
820 quantity: Quantity,
821 order_type: OrderType,
822 client_order_id: ClientOrderId,
823 trader_id: TraderId,
824 strategy_id: StrategyId,
825 instrument_id: InstrumentId,
826 price: Option<Price>,
827 time_in_force: Option<TimeInForce>,
828 post_only: bool,
829 reduce_only: bool,
830 trigger_price: Option<Price>,
831 trigger: Option<String>,
832 ) -> PyResult<Bound<'py, PyAny>> {
833 let client = self.clone();
834 let instrument_name = instrument_id.symbol.to_string();
835
836 let deribit_tif = time_in_force
838 .map(|tif| {
839 DeribitTimeInForce::try_from(tif)
840 .map(|deribit_tif| deribit_tif.as_str().to_string())
841 })
842 .transpose()
843 .map_err(to_pyvalue_err)?;
844
845 let params = DeribitOrderParams {
846 instrument_name,
847 amount: quantity.as_decimal(),
848 order_type: order_type.to_string().to_lowercase(),
849 label: Some(client_order_id.to_string()),
850 price: price.map(|p| p.as_decimal()),
851 time_in_force: deribit_tif,
852 post_only: if post_only { Some(true) } else { None },
853 reject_post_only: if post_only { Some(true) } else { None },
854 reduce_only: if reduce_only { Some(true) } else { None },
855 trigger_price: trigger_price.map(|p| p.as_decimal()),
856 trigger,
857 max_show: None,
858 valid_until: None,
859 };
860
861 pyo3_async_runtimes::tokio::future_into_py(py, async move {
862 client
863 .submit_order(
864 order_side,
865 params,
866 client_order_id,
867 trader_id,
868 strategy_id,
869 instrument_id,
870 )
871 .await
872 .map_err(to_pyruntime_err)?;
873 Ok(())
874 })
875 }
876
877 #[pyo3(name = "modify_order")]
878 #[allow(clippy::too_many_arguments)]
879 fn py_modify_order<'py>(
880 &self,
881 py: Python<'py>,
882 order_id: String,
883 quantity: Quantity,
884 price: Price,
885 client_order_id: ClientOrderId,
886 trader_id: TraderId,
887 strategy_id: StrategyId,
888 instrument_id: InstrumentId,
889 ) -> PyResult<Bound<'py, PyAny>> {
890 let client = self.clone();
891
892 pyo3_async_runtimes::tokio::future_into_py(py, async move {
893 client
894 .modify_order(
895 &order_id,
896 quantity,
897 price,
898 client_order_id,
899 trader_id,
900 strategy_id,
901 instrument_id,
902 )
903 .await
904 .map_err(to_pyruntime_err)?;
905 Ok(())
906 })
907 }
908
909 #[pyo3(name = "cancel_order")]
910 fn py_cancel_order<'py>(
911 &self,
912 py: Python<'py>,
913 order_id: String,
914 client_order_id: ClientOrderId,
915 trader_id: TraderId,
916 strategy_id: StrategyId,
917 instrument_id: InstrumentId,
918 ) -> PyResult<Bound<'py, PyAny>> {
919 let client = self.clone();
920
921 pyo3_async_runtimes::tokio::future_into_py(py, async move {
922 client
923 .cancel_order(
924 &order_id,
925 client_order_id,
926 trader_id,
927 strategy_id,
928 instrument_id,
929 )
930 .await
931 .map_err(to_pyruntime_err)?;
932 Ok(())
933 })
934 }
935
936 #[pyo3(name = "cancel_all_orders")]
937 #[pyo3(signature = (instrument_id, order_type=None))]
938 fn py_cancel_all_orders<'py>(
939 &self,
940 py: Python<'py>,
941 instrument_id: InstrumentId,
942 order_type: Option<String>,
943 ) -> PyResult<Bound<'py, PyAny>> {
944 let client = self.clone();
945
946 pyo3_async_runtimes::tokio::future_into_py(py, async move {
947 client
948 .cancel_all_orders(instrument_id, order_type)
949 .await
950 .map_err(to_pyruntime_err)?;
951 Ok(())
952 })
953 }
954
955 #[pyo3(name = "query_order")]
956 fn py_query_order<'py>(
957 &self,
958 py: Python<'py>,
959 order_id: String,
960 client_order_id: ClientOrderId,
961 trader_id: TraderId,
962 strategy_id: StrategyId,
963 instrument_id: InstrumentId,
964 ) -> PyResult<Bound<'py, PyAny>> {
965 let client = self.clone();
966
967 pyo3_async_runtimes::tokio::future_into_py(py, async move {
968 client
969 .query_order(
970 &order_id,
971 client_order_id,
972 trader_id,
973 strategy_id,
974 instrument_id,
975 )
976 .await
977 .map_err(to_pyruntime_err)?;
978 Ok(())
979 })
980 }
981}