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