1use chrono::{DateTime, Utc};
19use nautilus_core::python::to_pyvalue_err;
20use nautilus_model::{
21 data::BarType,
22 enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TriggerType},
23 identifiers::{AccountId, ClientOrderId, InstrumentId, OrderListId, 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::http::client::BitmexHttpClient;
30
31#[pymethods]
32impl BitmexHttpClient {
33 #[new]
34 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, testnet=false, timeout_secs=None, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None, recv_window_ms=None, max_requests_per_second=None, max_requests_per_minute=None))]
35 #[allow(clippy::too_many_arguments)]
36 fn py_new(
37 api_key: Option<&str>,
38 api_secret: Option<&str>,
39 base_url: Option<&str>,
40 testnet: bool,
41 timeout_secs: Option<u64>,
42 max_retries: Option<u32>,
43 retry_delay_ms: Option<u64>,
44 retry_delay_max_ms: Option<u64>,
45 recv_window_ms: Option<u64>,
46 max_requests_per_second: Option<u32>,
47 max_requests_per_minute: Option<u32>,
48 ) -> PyResult<Self> {
49 let timeout = timeout_secs.or(Some(60));
50
51 let (final_api_key, final_api_secret) = if api_key.is_none() && api_secret.is_none() {
53 let (key_var, secret_var) = if testnet {
55 ("BITMEX_TESTNET_API_KEY", "BITMEX_TESTNET_API_SECRET")
56 } else {
57 ("BITMEX_API_KEY", "BITMEX_API_SECRET")
58 };
59
60 let env_key = std::env::var(key_var).ok();
61 let env_secret = std::env::var(secret_var).ok();
62 (env_key, env_secret)
63 } else {
64 (api_key.map(String::from), api_secret.map(String::from))
65 };
66
67 Self::new(
68 base_url.map(String::from),
69 final_api_key,
70 final_api_secret,
71 testnet,
72 timeout,
73 max_retries,
74 retry_delay_ms,
75 retry_delay_max_ms,
76 recv_window_ms,
77 max_requests_per_second,
78 max_requests_per_minute,
79 )
80 .map_err(to_pyvalue_err)
81 }
82
83 #[staticmethod]
84 #[pyo3(name = "from_env")]
85 fn py_from_env() -> PyResult<Self> {
86 Self::from_env().map_err(to_pyvalue_err)
87 }
88
89 #[getter]
90 #[pyo3(name = "base_url")]
91 #[must_use]
92 pub fn py_base_url(&self) -> &str {
93 self.base_url()
94 }
95
96 #[getter]
97 #[pyo3(name = "api_key")]
98 #[must_use]
99 pub fn py_api_key(&self) -> Option<&str> {
100 self.api_key()
101 }
102
103 #[pyo3(name = "update_position_leverage")]
104 fn py_update_position_leverage<'py>(
105 &self,
106 py: Python<'py>,
107 _symbol: String,
108 _leverage: f64,
109 ) -> PyResult<Bound<'py, PyAny>> {
110 let _client = self.clone();
111
112 pyo3_async_runtimes::tokio::future_into_py(py, async move {
113 Python::attach(|py| -> PyResult<Py<PyAny>> {
119 Ok(py.None())
121 })
122 })
123 }
124
125 #[pyo3(name = "request_instrument")]
126 fn py_request_instrument<'py>(
127 &self,
128 py: Python<'py>,
129 instrument_id: InstrumentId,
130 ) -> PyResult<Bound<'py, PyAny>> {
131 let client = self.clone();
132
133 pyo3_async_runtimes::tokio::future_into_py(py, async move {
134 let instrument = client
135 .request_instrument(instrument_id)
136 .await
137 .map_err(to_pyvalue_err)?;
138
139 Python::attach(|py| match instrument {
140 Some(inst) => instrument_any_to_pyobject(py, inst),
141 None => Ok(py.None()),
142 })
143 })
144 }
145
146 #[pyo3(name = "request_instruments")]
147 fn py_request_instruments<'py>(
148 &self,
149 py: Python<'py>,
150 active_only: bool,
151 ) -> PyResult<Bound<'py, PyAny>> {
152 let client = self.clone();
153
154 pyo3_async_runtimes::tokio::future_into_py(py, async move {
155 let instruments = client
156 .request_instruments(active_only)
157 .await
158 .map_err(to_pyvalue_err)?;
159
160 Python::attach(|py| {
161 let py_instruments: PyResult<Vec<_>> = instruments
162 .into_iter()
163 .map(|inst| instrument_any_to_pyobject(py, inst))
164 .collect();
165 let pylist = PyList::new(py, py_instruments?)
166 .unwrap()
167 .into_any()
168 .unbind();
169 Ok(pylist)
170 })
171 })
172 }
173
174 #[pyo3(name = "request_trades")]
175 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
176 fn py_request_trades<'py>(
177 &self,
178 py: Python<'py>,
179 instrument_id: InstrumentId,
180 start: Option<DateTime<Utc>>,
181 end: Option<DateTime<Utc>>,
182 limit: Option<u32>,
183 ) -> PyResult<Bound<'py, PyAny>> {
184 let client = self.clone();
185
186 pyo3_async_runtimes::tokio::future_into_py(py, async move {
187 let trades = client
188 .request_trades(instrument_id, start, end, limit)
189 .await
190 .map_err(to_pyvalue_err)?;
191
192 Python::attach(|py| {
193 let py_trades: PyResult<Vec<_>> = trades
194 .into_iter()
195 .map(|trade| trade.into_py_any(py))
196 .collect();
197 let pylist = PyList::new(py, py_trades?).unwrap().into_any().unbind();
198 Ok(pylist)
199 })
200 })
201 }
202
203 #[pyo3(name = "request_bars")]
204 #[pyo3(signature = (bar_type, start=None, end=None, limit=None, partial=false))]
205 fn py_request_bars<'py>(
206 &self,
207 py: Python<'py>,
208 bar_type: BarType,
209 start: Option<DateTime<Utc>>,
210 end: Option<DateTime<Utc>>,
211 limit: Option<u32>,
212 partial: bool,
213 ) -> PyResult<Bound<'py, PyAny>> {
214 let client = self.clone();
215
216 pyo3_async_runtimes::tokio::future_into_py(py, async move {
217 let bars = client
218 .request_bars(bar_type, start, end, limit, partial)
219 .await
220 .map_err(to_pyvalue_err)?;
221
222 Python::attach(|py| {
223 let py_bars: PyResult<Vec<_>> =
224 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
225 let pylist = PyList::new(py, py_bars?).unwrap().into_any().unbind();
226 Ok(pylist)
227 })
228 })
229 }
230
231 #[pyo3(name = "query_order")]
232 #[pyo3(signature = (instrument_id, client_order_id=None, venue_order_id=None))]
233 fn py_query_order<'py>(
234 &self,
235 py: Python<'py>,
236 instrument_id: InstrumentId,
237 client_order_id: Option<ClientOrderId>,
238 venue_order_id: Option<VenueOrderId>,
239 ) -> PyResult<Bound<'py, PyAny>> {
240 let client = self.clone();
241
242 pyo3_async_runtimes::tokio::future_into_py(py, async move {
243 match client
244 .query_order(instrument_id, client_order_id, venue_order_id)
245 .await
246 {
247 Ok(Some(report)) => Python::attach(|py| report.into_py_any(py)),
248 Ok(None) => Ok(Python::attach(|py| py.None())),
249 Err(e) => Err(to_pyvalue_err(e)),
250 }
251 })
252 }
253
254 #[pyo3(name = "request_order_status_reports")]
255 #[pyo3(signature = (instrument_id=None, open_only=false, limit=None))]
256 fn py_request_order_status_reports<'py>(
257 &self,
258 py: Python<'py>,
259 instrument_id: Option<InstrumentId>,
260 open_only: bool,
261 limit: Option<u32>,
262 ) -> PyResult<Bound<'py, PyAny>> {
263 let client = self.clone();
264
265 pyo3_async_runtimes::tokio::future_into_py(py, async move {
266 let reports = client
267 .request_order_status_reports(instrument_id, open_only, limit)
268 .await
269 .map_err(to_pyvalue_err)?;
270
271 Python::attach(|py| {
272 let py_reports: PyResult<Vec<_>> = reports
273 .into_iter()
274 .map(|report| report.into_py_any(py))
275 .collect();
276 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
277 Ok(pylist)
278 })
279 })
280 }
281
282 #[pyo3(name = "request_fill_reports")]
283 #[pyo3(signature = (instrument_id=None, limit=None))]
284 fn py_request_fill_reports<'py>(
285 &self,
286 py: Python<'py>,
287 instrument_id: Option<InstrumentId>,
288 limit: Option<u32>,
289 ) -> PyResult<Bound<'py, PyAny>> {
290 let client = self.clone();
291
292 pyo3_async_runtimes::tokio::future_into_py(py, async move {
293 let reports = client
294 .request_fill_reports(instrument_id, limit)
295 .await
296 .map_err(to_pyvalue_err)?;
297
298 Python::attach(|py| {
299 let py_reports: PyResult<Vec<_>> = reports
300 .into_iter()
301 .map(|report| report.into_py_any(py))
302 .collect();
303 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
304 Ok(pylist)
305 })
306 })
307 }
308
309 #[pyo3(name = "request_position_status_reports")]
310 fn py_request_position_status_reports<'py>(
311 &self,
312 py: Python<'py>,
313 ) -> PyResult<Bound<'py, PyAny>> {
314 let client = self.clone();
315
316 pyo3_async_runtimes::tokio::future_into_py(py, async move {
317 let reports = client
318 .request_position_status_reports()
319 .await
320 .map_err(to_pyvalue_err)?;
321
322 Python::attach(|py| {
323 let py_reports: PyResult<Vec<_>> = reports
324 .into_iter()
325 .map(|report| report.into_py_any(py))
326 .collect();
327 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
328 Ok(pylist)
329 })
330 })
331 }
332
333 #[pyo3(name = "submit_order")]
334 #[pyo3(signature = (
335 instrument_id,
336 client_order_id,
337 order_side,
338 order_type,
339 quantity,
340 time_in_force,
341 price = None,
342 trigger_price = None,
343 trigger_type = None,
344 display_qty = None,
345 post_only = false,
346 reduce_only = false,
347 order_list_id = None,
348 contingency_type = None
349 ))]
350 #[allow(clippy::too_many_arguments)]
351 fn py_submit_order<'py>(
352 &self,
353 py: Python<'py>,
354 instrument_id: InstrumentId,
355 client_order_id: ClientOrderId,
356 order_side: OrderSide,
357 order_type: OrderType,
358 quantity: Quantity,
359 time_in_force: TimeInForce,
360 price: Option<Price>,
361 trigger_price: Option<Price>,
362 trigger_type: Option<TriggerType>,
363 display_qty: Option<Quantity>,
364 post_only: bool,
365 reduce_only: bool,
366 order_list_id: Option<OrderListId>,
367 contingency_type: Option<ContingencyType>,
368 ) -> PyResult<Bound<'py, PyAny>> {
369 let client = self.clone();
370
371 pyo3_async_runtimes::tokio::future_into_py(py, async move {
372 let report = client
373 .submit_order(
374 instrument_id,
375 client_order_id,
376 order_side,
377 order_type,
378 quantity,
379 time_in_force,
380 price,
381 trigger_price,
382 trigger_type,
383 display_qty,
384 post_only,
385 reduce_only,
386 order_list_id,
387 contingency_type,
388 )
389 .await
390 .map_err(to_pyvalue_err)?;
391
392 Python::attach(|py| report.into_py_any(py))
393 })
394 }
395
396 #[pyo3(name = "cancel_order")]
397 #[pyo3(signature = (instrument_id, client_order_id=None, venue_order_id=None))]
398 fn py_cancel_order<'py>(
399 &self,
400 py: Python<'py>,
401 instrument_id: InstrumentId,
402 client_order_id: Option<ClientOrderId>,
403 venue_order_id: Option<VenueOrderId>,
404 ) -> PyResult<Bound<'py, PyAny>> {
405 let client = self.clone();
406
407 pyo3_async_runtimes::tokio::future_into_py(py, async move {
408 let report = client
409 .cancel_order(instrument_id, client_order_id, venue_order_id)
410 .await
411 .map_err(to_pyvalue_err)?;
412
413 Python::attach(|py| report.into_py_any(py))
414 })
415 }
416
417 #[pyo3(name = "cancel_orders")]
418 #[pyo3(signature = (instrument_id, client_order_ids=None, venue_order_ids=None))]
419 fn py_cancel_orders<'py>(
420 &self,
421 py: Python<'py>,
422 instrument_id: InstrumentId,
423 client_order_ids: Option<Vec<ClientOrderId>>,
424 venue_order_ids: Option<Vec<VenueOrderId>>,
425 ) -> PyResult<Bound<'py, PyAny>> {
426 let client = self.clone();
427
428 pyo3_async_runtimes::tokio::future_into_py(py, async move {
429 let reports = client
430 .cancel_orders(instrument_id, client_order_ids, venue_order_ids)
431 .await
432 .map_err(to_pyvalue_err)?;
433
434 Python::attach(|py| {
435 let py_reports: PyResult<Vec<_>> = reports
436 .into_iter()
437 .map(|report| report.into_py_any(py))
438 .collect();
439 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
440 Ok(pylist)
441 })
442 })
443 }
444
445 #[pyo3(name = "cancel_all_orders")]
446 #[pyo3(signature = (instrument_id, order_side))]
447 fn py_cancel_all_orders<'py>(
448 &self,
449 py: Python<'py>,
450 instrument_id: InstrumentId,
451 order_side: Option<OrderSide>,
452 ) -> PyResult<Bound<'py, PyAny>> {
453 let client = self.clone();
454
455 pyo3_async_runtimes::tokio::future_into_py(py, async move {
456 let reports = client
457 .cancel_all_orders(instrument_id, order_side)
458 .await
459 .map_err(to_pyvalue_err)?;
460
461 Python::attach(|py| {
462 let py_reports: PyResult<Vec<_>> = reports
463 .into_iter()
464 .map(|report| report.into_py_any(py))
465 .collect();
466 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
467 Ok(pylist)
468 })
469 })
470 }
471
472 #[pyo3(name = "modify_order")]
473 #[pyo3(signature = (
474 instrument_id,
475 client_order_id=None,
476 venue_order_id=None,
477 quantity=None,
478 price=None,
479 trigger_price=None
480 ))]
481 #[allow(clippy::too_many_arguments)]
482 fn py_modify_order<'py>(
483 &self,
484 py: Python<'py>,
485 instrument_id: InstrumentId,
486 client_order_id: Option<ClientOrderId>,
487 venue_order_id: Option<VenueOrderId>,
488 quantity: Option<Quantity>,
489 price: Option<Price>,
490 trigger_price: Option<Price>,
491 ) -> PyResult<Bound<'py, PyAny>> {
492 let client = self.clone();
493
494 pyo3_async_runtimes::tokio::future_into_py(py, async move {
495 let report = client
496 .modify_order(
497 instrument_id,
498 client_order_id,
499 venue_order_id,
500 quantity,
501 price,
502 trigger_price,
503 )
504 .await
505 .map_err(to_pyvalue_err)?;
506
507 Python::attach(|py| report.into_py_any(py))
508 })
509 }
510
511 #[pyo3(name = "add_instrument")]
512 fn py_add_instrument(&mut self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
513 let inst_any = pyobject_to_instrument_any(py, instrument)?;
514 self.add_instrument(inst_any);
515 Ok(())
516 }
517
518 #[pyo3(name = "cancel_all_requests")]
519 fn py_cancel_all_requests(&self) {
520 self.cancel_all_requests();
521 }
522
523 #[pyo3(name = "http_get_margin")]
524 fn py_http_get_margin<'py>(
525 &self,
526 py: Python<'py>,
527 currency: String,
528 ) -> PyResult<Bound<'py, PyAny>> {
529 let client = self.clone();
530
531 pyo3_async_runtimes::tokio::future_into_py(py, async move {
532 let margin = client
533 .http_get_margin(¤cy)
534 .await
535 .map_err(to_pyvalue_err)?;
536
537 Python::attach(|py| {
538 let account = margin.account;
541 account.into_py_any(py)
542 })
543 })
544 }
545
546 #[pyo3(name = "request_account_state")]
547 fn py_request_account_state<'py>(
548 &self,
549 py: Python<'py>,
550 account_id: AccountId,
551 ) -> PyResult<Bound<'py, PyAny>> {
552 let client = self.clone();
553
554 pyo3_async_runtimes::tokio::future_into_py(py, async move {
555 let account_state = client
556 .request_account_state(account_id)
557 .await
558 .map_err(to_pyvalue_err)?;
559
560 Python::attach(|py| account_state.into_py_any(py).map_err(to_pyvalue_err))
561 })
562 }
563
564 #[pyo3(name = "submit_orders_bulk")]
565 fn py_submit_orders_bulk<'py>(
566 &self,
567 py: Python<'py>,
568 orders: Vec<Py<PyAny>>,
569 ) -> PyResult<Bound<'py, PyAny>> {
570 let _client = self.clone();
571
572 let _params = Python::attach(|_py| {
574 orders
575 .into_iter()
576 .map(|obj| {
577 Ok(obj)
580 })
581 .collect::<PyResult<Vec<_>>>()
582 })?;
583
584 pyo3_async_runtimes::tokio::future_into_py(py, async move {
585 Python::attach(|py| -> PyResult<Py<PyAny>> {
589 let py_list = PyList::new(py, Vec::<Py<PyAny>>::new())?;
590 Ok(py_list.into())
594 })
595 })
596 }
597
598 #[pyo3(name = "modify_orders_bulk")]
599 fn py_modify_orders_bulk<'py>(
600 &self,
601 py: Python<'py>,
602 orders: Vec<Py<PyAny>>,
603 ) -> PyResult<Bound<'py, PyAny>> {
604 let _client = self.clone();
605
606 let _params = Python::attach(|_py| {
608 orders
609 .into_iter()
610 .map(|obj| {
611 Ok(obj)
614 })
615 .collect::<PyResult<Vec<_>>>()
616 })?;
617
618 pyo3_async_runtimes::tokio::future_into_py(py, async move {
619 Python::attach(|py| -> PyResult<Py<PyAny>> {
623 let py_list = PyList::new(py, Vec::<Py<PyAny>>::new())?;
624 Ok(py_list.into())
628 })
629 })
630 }
631
632 #[pyo3(name = "http_get_server_time")]
633 fn py_http_get_server_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
634 let client = self.clone();
635
636 pyo3_async_runtimes::tokio::future_into_py(py, async move {
637 let timestamp = client
638 .http_get_server_time()
639 .await
640 .map_err(to_pyvalue_err)?;
641
642 Python::attach(|py| timestamp.into_py_any(py))
643 })
644 }
645}