1use jiff::Timestamp;
19use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
20use nautilus_model::{
21 data::BarType,
22 identifiers::{AccountId, InstrumentId},
23 instruments::InstrumentAny,
24 python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
25};
26use pyo3::{
27 IntoPyObjectExt,
28 prelude::*,
29 types::{PyDict, PyList},
30};
31
32use crate::{
33 common::{consts::DYDX_VENUE, enums::DydxNetwork},
34 http::client::DydxHttpClient,
35};
36
37#[pymethods]
38#[pyo3_stub_gen::derive::gen_stub_pymethods]
39impl DydxHttpClient {
40 #[new]
55 #[pyo3(signature = (base_url=None, network=DydxNetwork::Mainnet, proxy_url=None))]
56 fn py_new(
57 base_url: Option<String>,
58 network: DydxNetwork,
59 proxy_url: Option<String>,
60 ) -> PyResult<Self> {
61 Self::new(
62 base_url, 60, proxy_url, network, None, )
65 .map_err(to_pyvalue_err)
66 }
67
68 #[pyo3(name = "is_testnet")]
70 fn py_is_testnet(&self) -> bool {
71 self.is_testnet()
72 }
73
74 #[pyo3(name = "base_url")]
76 fn py_base_url(&self) -> String {
77 self.base_url().to_string()
78 }
79
80 #[pyo3(name = "request_instruments")]
90 fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
91 let client = self.clone();
92
93 pyo3_async_runtimes::tokio::future_into_py(py, async move {
94 let instruments = client
95 .request_instruments(None)
96 .await
97 .map_err(to_pyvalue_err)?;
98
99 Python::attach(|py| {
100 let py_instruments: PyResult<Vec<Py<PyAny>>> = instruments
101 .into_iter()
102 .map(|inst| instrument_any_to_pyobject(py, inst))
103 .collect();
104 py_instruments
105 })
106 })
107 }
108
109 #[pyo3(name = "fetch_and_cache_instruments")]
121 fn py_fetch_and_cache_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
122 let client = self.clone();
123 pyo3_async_runtimes::tokio::future_into_py(py, async move {
124 client
125 .fetch_and_cache_instruments()
126 .await
127 .map_err(to_pyvalue_err)?;
128 Ok(())
129 })
130 }
131
132 #[pyo3(name = "fetch_instrument")]
139 fn py_fetch_instrument<'py>(
140 &self,
141 py: Python<'py>,
142 ticker: String,
143 ) -> PyResult<Bound<'py, PyAny>> {
144 let client = self.clone();
145 pyo3_async_runtimes::tokio::future_into_py(py, async move {
146 match client.fetch_and_cache_single_instrument(&ticker).await {
147 Ok(Some(instrument)) => {
148 Python::attach(|py| instrument_any_to_pyobject(py, instrument))
149 }
150 Ok(None) => Ok(Python::attach(|py| py.None())),
151 Err(e) => Err(to_pyvalue_err(e)),
152 }
153 })
154 }
155
156 #[pyo3(name = "get_instrument")]
158 fn py_get_instrument(&self, py: Python<'_>, symbol: &str) -> PyResult<Option<Py<PyAny>>> {
159 use nautilus_model::identifiers::Symbol;
160 let instrument_id = InstrumentId::new(Symbol::new(symbol), *DYDX_VENUE);
161 let instrument = self.get_instrument(&instrument_id);
162 match instrument {
163 Some(inst) => Ok(Some(instrument_any_to_pyobject(py, inst)?)),
164 None => Ok(None),
165 }
166 }
167
168 #[pyo3(name = "instrument_count")]
169 fn py_instrument_count(&self) -> usize {
170 self.cached_instruments_count()
171 }
172
173 #[pyo3(name = "instrument_symbols")]
174 fn py_instrument_symbols(&self) -> Vec<String> {
175 self.all_instrument_ids()
176 .into_iter()
177 .map(|id| id.symbol.to_string())
178 .collect()
179 }
180
181 #[pyo3(name = "cache_instruments")]
186 fn py_cache_instruments(
187 &self,
188 py: Python<'_>,
189 py_instruments: Vec<Bound<'_, PyAny>>,
190 ) -> PyResult<()> {
191 let instruments: Vec<InstrumentAny> = py_instruments
192 .into_iter()
193 .map(|py_inst| {
194 pyobject_to_instrument_any(py, py_inst.unbind())
196 })
197 .collect::<Result<Vec<_>, _>>()
198 .map_err(to_pyvalue_err)?;
199
200 self.cache_instruments(instruments);
201 Ok(())
202 }
203
204 #[pyo3(name = "get_orders")]
205 #[pyo3(signature = (address, subaccount_number, market=None, limit=None))]
206 fn py_get_orders<'py>(
207 &self,
208 py: Python<'py>,
209 address: String,
210 subaccount_number: u32,
211 market: Option<String>,
212 limit: Option<u32>,
213 ) -> PyResult<Bound<'py, PyAny>> {
214 let client = self.clone();
215 pyo3_async_runtimes::tokio::future_into_py(py, async move {
216 let response = client
217 .inner
218 .get_orders(&address, subaccount_number, market.as_deref(), limit)
219 .await
220 .map_err(to_pyvalue_err)?;
221 serde_json::to_string(&response).map_err(to_pyvalue_err)
222 })
223 }
224
225 #[pyo3(name = "get_fills")]
226 #[pyo3(signature = (address, subaccount_number, market=None, limit=None))]
227 fn py_get_fills<'py>(
228 &self,
229 py: Python<'py>,
230 address: String,
231 subaccount_number: u32,
232 market: Option<String>,
233 limit: Option<u32>,
234 ) -> PyResult<Bound<'py, PyAny>> {
235 let client = self.clone();
236 pyo3_async_runtimes::tokio::future_into_py(py, async move {
237 let response = client
238 .inner
239 .get_fills(&address, subaccount_number, market.as_deref(), limit)
240 .await
241 .map_err(to_pyvalue_err)?;
242 serde_json::to_string(&response).map_err(to_pyvalue_err)
243 })
244 }
245
246 #[pyo3(name = "get_subaccount")]
247 fn py_get_subaccount<'py>(
248 &self,
249 py: Python<'py>,
250 address: String,
251 subaccount_number: u32,
252 ) -> PyResult<Bound<'py, PyAny>> {
253 let client = self.clone();
254 pyo3_async_runtimes::tokio::future_into_py(py, async move {
255 let response = client
256 .inner
257 .get_subaccount(&address, subaccount_number)
258 .await
259 .map_err(to_pyvalue_err)?;
260 serde_json::to_string(&response).map_err(to_pyvalue_err)
261 })
262 }
263
264 #[pyo3(name = "request_order_status_reports")]
273 #[pyo3(signature = (address, subaccount_number, account_id, instrument_id=None))]
274 fn py_request_order_status_reports<'py>(
275 &self,
276 py: Python<'py>,
277 address: String,
278 subaccount_number: u32,
279 account_id: AccountId,
280 instrument_id: Option<InstrumentId>,
281 ) -> PyResult<Bound<'py, PyAny>> {
282 let client = self.clone();
283 pyo3_async_runtimes::tokio::future_into_py(py, async move {
284 let reports = client
285 .request_order_status_reports(
286 &address,
287 subaccount_number,
288 account_id,
289 instrument_id,
290 )
291 .await
292 .map_err(to_pyvalue_err)?;
293
294 Python::attach(|py| {
295 let py_reports = reports
296 .into_iter()
297 .map(|report| report.into_py_any(py))
298 .collect::<PyResult<Vec<_>>>()?;
299 let pylist = PyList::new(py, py_reports)?;
300 Ok(pylist.into_py_any_unwrap(py))
301 })
302 })
303 }
304
305 #[pyo3(name = "request_fill_reports")]
314 #[pyo3(signature = (address, subaccount_number, account_id, instrument_id=None))]
315 fn py_request_fill_reports<'py>(
316 &self,
317 py: Python<'py>,
318 address: String,
319 subaccount_number: u32,
320 account_id: AccountId,
321 instrument_id: Option<InstrumentId>,
322 ) -> PyResult<Bound<'py, PyAny>> {
323 let client = self.clone();
324 pyo3_async_runtimes::tokio::future_into_py(py, async move {
325 let reports = client
326 .request_fill_reports(&address, subaccount_number, account_id, instrument_id)
327 .await
328 .map_err(to_pyvalue_err)?;
329
330 Python::attach(|py| {
331 let py_reports = reports
332 .into_iter()
333 .map(|report| report.into_py_any(py))
334 .collect::<PyResult<Vec<_>>>()?;
335 let pylist = PyList::new(py, py_reports)?;
336 Ok(pylist.into_py_any_unwrap(py))
337 })
338 })
339 }
340
341 #[pyo3(name = "request_position_status_reports")]
350 #[pyo3(signature = (address, subaccount_number, account_id, instrument_id=None))]
351 fn py_request_position_status_reports<'py>(
352 &self,
353 py: Python<'py>,
354 address: String,
355 subaccount_number: u32,
356 account_id: AccountId,
357 instrument_id: Option<InstrumentId>,
358 ) -> PyResult<Bound<'py, PyAny>> {
359 let client = self.clone();
360 pyo3_async_runtimes::tokio::future_into_py(py, async move {
361 let reports = client
362 .request_position_status_reports(
363 &address,
364 subaccount_number,
365 account_id,
366 instrument_id,
367 )
368 .await
369 .map_err(to_pyvalue_err)?;
370
371 Python::attach(|py| {
372 let py_reports = reports
373 .into_iter()
374 .map(|report| report.into_py_any(py))
375 .collect::<PyResult<Vec<_>>>()?;
376 let pylist = PyList::new(py, py_reports)?;
377 Ok(pylist.into_py_any_unwrap(py))
378 })
379 })
380 }
381
382 #[pyo3(name = "request_account_state")]
391 fn py_request_account_state<'py>(
392 &self,
393 py: Python<'py>,
394 address: String,
395 subaccount_number: u32,
396 account_id: AccountId,
397 ) -> PyResult<Bound<'py, PyAny>> {
398 let client = self.clone();
399 pyo3_async_runtimes::tokio::future_into_py(py, async move {
400 let account_state = client
401 .request_account_state(&address, subaccount_number, account_id)
402 .await
403 .map_err(to_pyvalue_err)?;
404
405 Python::attach(|py| account_state.into_py_any(py))
406 })
407 }
408
409 #[pyo3(name = "request_bars")]
427 #[pyo3(signature = (bar_type, start=None, end=None, limit=None, timestamp_on_close=true))]
428 fn py_request_bars<'py>(
429 &self,
430 py: Python<'py>,
431 bar_type: BarType,
432 start: Option<Timestamp>,
433 end: Option<Timestamp>,
434 limit: Option<u32>,
435 timestamp_on_close: bool,
436 ) -> PyResult<Bound<'py, PyAny>> {
437 let client = self.clone();
438
439 pyo3_async_runtimes::tokio::future_into_py(py, async move {
440 let bars = client
441 .request_bars(bar_type, start, end, limit, timestamp_on_close)
442 .await
443 .map_err(to_pyvalue_err)?;
444
445 Python::attach(|py| {
446 let py_bars = bars
447 .into_iter()
448 .map(|bar| bar.into_py_any(py))
449 .collect::<PyResult<Vec<_>>>()?;
450 let pylist = PyList::new(py, py_bars)?;
451 Ok(pylist.into_py_any_unwrap(py))
452 })
453 })
454 }
455
456 #[pyo3(name = "request_trade_ticks")]
469 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
470 fn py_request_trade_ticks<'py>(
471 &self,
472 py: Python<'py>,
473 instrument_id: InstrumentId,
474 start: Option<Timestamp>,
475 end: Option<Timestamp>,
476 limit: Option<u32>,
477 ) -> PyResult<Bound<'py, PyAny>> {
478 let client = self.clone();
479
480 pyo3_async_runtimes::tokio::future_into_py(py, async move {
481 let trades = client
482 .request_trade_ticks(instrument_id, start, end, limit)
483 .await
484 .map_err(to_pyvalue_err)?;
485
486 Python::attach(|py| {
487 let py_trades = trades
488 .into_iter()
489 .map(|trade| trade.into_py_any(py))
490 .collect::<PyResult<Vec<_>>>()?;
491 let pylist = PyList::new(py, py_trades)?;
492 Ok(pylist.into_py_any_unwrap(py))
493 })
494 })
495 }
496
497 #[pyo3(name = "request_funding_rates")]
509 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
510 fn py_request_funding_rates<'py>(
511 &self,
512 py: Python<'py>,
513 instrument_id: InstrumentId,
514 start: Option<Timestamp>,
515 end: Option<Timestamp>,
516 limit: Option<u32>,
517 ) -> PyResult<Bound<'py, PyAny>> {
518 let client = self.clone();
519
520 pyo3_async_runtimes::tokio::future_into_py(py, async move {
521 let funding_rates = client
522 .request_funding_rates(instrument_id, start, end, limit)
523 .await
524 .map_err(to_pyvalue_err)?;
525
526 Python::attach(|py| {
527 let py_rates = funding_rates
528 .into_iter()
529 .map(|rate| rate.into_py_any(py))
530 .collect::<PyResult<Vec<_>>>()?;
531 let pylist = PyList::new(py, py_rates)?;
532 Ok(pylist.into_py_any_unwrap(py))
533 })
534 })
535 }
536
537 #[pyo3(name = "request_orderbook_snapshot")]
548 fn py_request_orderbook_snapshot<'py>(
549 &self,
550 py: Python<'py>,
551 instrument_id: InstrumentId,
552 ) -> PyResult<Bound<'py, PyAny>> {
553 let client = self.clone();
554
555 pyo3_async_runtimes::tokio::future_into_py(py, async move {
556 let deltas = client
557 .request_orderbook_snapshot(instrument_id)
558 .await
559 .map_err(to_pyvalue_err)?;
560
561 Python::attach(|py| deltas.into_py_any(py))
562 })
563 }
564
565 #[pyo3(name = "get_time")]
566 fn py_get_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
567 let client = self.clone();
568 pyo3_async_runtimes::tokio::future_into_py(py, async move {
569 let response = client.inner.get_time().await.map_err(to_pyvalue_err)?;
570 Python::attach(|py| {
571 let dict = PyDict::new(py);
572 dict.set_item("iso", response.iso.to_string())?;
573 dict.set_item("epoch", response.epoch_ms)?;
574 Ok(dict.into_py_any_unwrap(py))
575 })
576 })
577 }
578
579 #[pyo3(name = "get_height")]
580 fn py_get_height<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
581 let client = self.clone();
582 pyo3_async_runtimes::tokio::future_into_py(py, async move {
583 let response = client.inner.get_height().await.map_err(to_pyvalue_err)?;
584 Python::attach(|py| {
585 let dict = PyDict::new(py);
586 dict.set_item("height", response.height)?;
587 dict.set_item("time", response.time)?;
588 Ok(dict.into_py_any_unwrap(py))
589 })
590 })
591 }
592
593 #[pyo3(name = "get_transfers")]
594 #[pyo3(signature = (address, subaccount_number, limit=None))]
595 fn py_get_transfers<'py>(
596 &self,
597 py: Python<'py>,
598 address: String,
599 subaccount_number: u32,
600 limit: Option<u32>,
601 ) -> PyResult<Bound<'py, PyAny>> {
602 let client = self.clone();
603 pyo3_async_runtimes::tokio::future_into_py(py, async move {
604 let response = client
605 .inner
606 .get_transfers(&address, subaccount_number, limit)
607 .await
608 .map_err(to_pyvalue_err)?;
609 serde_json::to_string(&response).map_err(to_pyvalue_err)
610 })
611 }
612
613 fn __repr__(&self) -> String {
614 format!(
615 "DydxHttpClient(base_url='{}', is_testnet={}, cached_instruments={})",
616 self.base_url(),
617 self.is_testnet(),
618 self.cached_instruments_count()
619 )
620 }
621}