1use chrono::{DateTime, Utc};
19use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyruntime_err, to_pyvalue_err};
20use nautilus_model::{
21 data::BarType,
22 identifiers::{AccountId, InstrumentId},
23 python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
24};
25use pyo3::{conversion::IntoPyObjectExt, prelude::*, types::PyList};
26
27use crate::http::{
28 client::DeribitHttpClient,
29 error::DeribitHttpError,
30 models::{DeribitCurrency, DeribitProductType},
31};
32
33#[pymethods]
34impl DeribitHttpClient {
35 #[new]
36 #[pyo3(signature = (
37 api_key=None,
38 api_secret=None,
39 base_url=None,
40 is_testnet=false,
41 timeout_secs=None,
42 max_retries=None,
43 retry_delay_ms=None,
44 retry_delay_max_ms=None,
45 proxy_url=None,
46 ))]
47 #[allow(clippy::too_many_arguments)]
48 #[allow(unused_variables)]
49 fn py_new(
50 api_key: Option<String>,
51 api_secret: Option<String>,
52 base_url: Option<String>,
53 is_testnet: bool,
54 timeout_secs: Option<u64>,
55 max_retries: Option<u32>,
56 retry_delay_ms: Option<u64>,
57 retry_delay_max_ms: Option<u64>,
58 proxy_url: Option<String>,
59 ) -> PyResult<Self> {
60 Self::new_with_env(
61 api_key,
62 api_secret,
63 base_url,
64 is_testnet,
65 timeout_secs,
66 max_retries,
67 retry_delay_ms,
68 retry_delay_max_ms,
69 proxy_url,
70 )
71 .map_err(to_pyvalue_err)
72 }
73
74 #[getter]
75 #[pyo3(name = "is_testnet")]
76 #[must_use]
77 pub fn py_is_testnet(&self) -> bool {
78 self.is_testnet()
79 }
80
81 #[pyo3(name = "is_initialized")]
82 #[must_use]
83 pub fn py_is_initialized(&self) -> bool {
84 self.is_cache_initialized()
85 }
86
87 #[pyo3(name = "cache_instruments")]
91 pub fn py_cache_instruments(
92 &self,
93 py: Python<'_>,
94 instruments: Vec<Py<PyAny>>,
95 ) -> PyResult<()> {
96 let instruments: Result<Vec<_>, _> = instruments
97 .into_iter()
98 .map(|inst| pyobject_to_instrument_any(py, inst))
99 .collect();
100 self.cache_instruments(instruments?);
101 Ok(())
102 }
103
104 #[pyo3(name = "cache_instrument")]
108 pub fn py_cache_instrument(&self, py: Python<'_>, instrument: Py<PyAny>) -> PyResult<()> {
109 let inst = pyobject_to_instrument_any(py, instrument)?;
110 self.cache_instruments(vec![inst]);
111 Ok(())
112 }
113
114 #[pyo3(name = "request_instruments")]
115 #[pyo3(signature = (currency, product_type=None))]
116 fn py_request_instruments<'py>(
117 &self,
118 py: Python<'py>,
119 currency: DeribitCurrency,
120 product_type: Option<DeribitProductType>,
121 ) -> PyResult<Bound<'py, PyAny>> {
122 let client = self.clone();
123
124 pyo3_async_runtimes::tokio::future_into_py(py, async move {
125 let instruments = client
126 .request_instruments(currency, product_type)
127 .await
128 .map_err(to_pyvalue_err)?;
129
130 Python::attach(|py| {
131 let py_instruments: PyResult<Vec<_>> = instruments
132 .into_iter()
133 .map(|inst| instrument_any_to_pyobject(py, inst))
134 .collect();
135 let pylist = PyList::new(py, py_instruments?)
136 .unwrap()
137 .into_any()
138 .unbind();
139 Ok(pylist)
140 })
141 })
142 }
143
144 #[pyo3(name = "request_instrument")]
145 fn py_request_instrument<'py>(
146 &self,
147 py: Python<'py>,
148 instrument_id: InstrumentId,
149 ) -> PyResult<Bound<'py, PyAny>> {
150 let client = self.clone();
151
152 pyo3_async_runtimes::tokio::future_into_py(py, async move {
153 let instrument = client
154 .request_instrument(instrument_id)
155 .await
156 .map_err(to_pyvalue_err)?;
157
158 Python::attach(|py| instrument_any_to_pyobject(py, instrument))
159 })
160 }
161
162 #[pyo3(name = "request_account_state")]
163 fn py_request_account_state<'py>(
164 &self,
165 py: Python<'py>,
166 account_id: AccountId,
167 ) -> PyResult<Bound<'py, PyAny>> {
168 let client = self.clone();
169
170 pyo3_async_runtimes::tokio::future_into_py(py, async move {
171 let account_state = client
172 .request_account_state(account_id)
173 .await
174 .map_err(to_pyvalue_err)?;
175
176 Python::attach(|py| Ok(account_state.into_py_any_unwrap(py)))
177 })
178 }
179
180 #[pyo3(name = "request_trades")]
181 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
182 fn py_request_trades<'py>(
183 &self,
184 py: Python<'py>,
185 instrument_id: InstrumentId,
186 start: Option<DateTime<Utc>>,
187 end: Option<DateTime<Utc>>,
188 limit: Option<u32>,
189 ) -> PyResult<Bound<'py, PyAny>> {
190 let client = self.clone();
191
192 pyo3_async_runtimes::tokio::future_into_py(py, async move {
193 let trades = client
194 .request_trades(instrument_id, start, end, limit)
195 .await
196 .map_err(to_pyvalue_err)?;
197
198 Python::attach(|py| {
199 let pylist = PyList::new(
200 py,
201 trades.into_iter().map(|trade| trade.into_py_any_unwrap(py)),
202 )?;
203 Ok(pylist.into_py_any_unwrap(py))
204 })
205 })
206 }
207
208 #[pyo3(name = "request_bars")]
209 #[pyo3(signature = (bar_type, start=None, end=None, limit=None))]
210 fn py_request_bars<'py>(
211 &self,
212 py: Python<'py>,
213 bar_type: BarType,
214 start: Option<DateTime<Utc>>,
215 end: Option<DateTime<Utc>>,
216 limit: Option<u32>,
217 ) -> PyResult<Bound<'py, PyAny>> {
218 let client = self.clone();
219
220 pyo3_async_runtimes::tokio::future_into_py(py, async move {
221 let bars = client
222 .request_bars(bar_type, start, end, limit)
223 .await
224 .map_err(to_pyvalue_err)?;
225
226 Python::attach(|py| {
227 let pylist =
228 PyList::new(py, bars.into_iter().map(|bar| bar.into_py_any_unwrap(py)))?;
229 Ok(pylist.into_py_any_unwrap(py))
230 })
231 })
232 }
233
234 #[pyo3(name = "request_book_snapshot")]
235 #[pyo3(signature = (instrument_id, depth=None))]
236 fn py_request_book_snapshot<'py>(
237 &self,
238 py: Python<'py>,
239 instrument_id: InstrumentId,
240 depth: Option<u32>,
241 ) -> PyResult<Bound<'py, PyAny>> {
242 let client = self.clone();
243
244 pyo3_async_runtimes::tokio::future_into_py(py, async move {
245 let book = client
246 .request_book_snapshot(instrument_id, depth)
247 .await
248 .map_err(to_pyvalue_err)?;
249
250 Python::attach(|py| Ok(book.into_py_any_unwrap(py)))
251 })
252 }
253
254 #[pyo3(name = "request_order_status_reports")]
255 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None, open_only=true))]
256 fn py_request_order_status_reports<'py>(
257 &self,
258 py: Python<'py>,
259 account_id: AccountId,
260 instrument_id: Option<InstrumentId>,
261 start: Option<u64>,
262 end: Option<u64>,
263 open_only: bool,
264 ) -> PyResult<Bound<'py, PyAny>> {
265 let client = self.clone();
266
267 pyo3_async_runtimes::tokio::future_into_py(py, async move {
268 let reports = client
269 .request_order_status_reports(
270 account_id,
271 instrument_id,
272 start.map(nautilus_core::UnixNanos::from),
273 end.map(nautilus_core::UnixNanos::from),
274 open_only,
275 )
276 .await
277 .map_err(to_pyvalue_err)?;
278
279 Python::attach(|py| {
280 let py_reports: PyResult<Vec<_>> = reports
281 .into_iter()
282 .map(|report| report.into_py_any(py))
283 .collect();
284 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
285 Ok(pylist)
286 })
287 })
288 }
289
290 #[pyo3(name = "request_fill_reports")]
291 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None))]
292 fn py_request_fill_reports<'py>(
293 &self,
294 py: Python<'py>,
295 account_id: AccountId,
296 instrument_id: Option<InstrumentId>,
297 start: Option<u64>,
298 end: Option<u64>,
299 ) -> PyResult<Bound<'py, PyAny>> {
300 let client = self.clone();
301
302 pyo3_async_runtimes::tokio::future_into_py(py, async move {
303 let reports = client
304 .request_fill_reports(
305 account_id,
306 instrument_id,
307 start.map(nautilus_core::UnixNanos::from),
308 end.map(nautilus_core::UnixNanos::from),
309 )
310 .await
311 .map_err(to_pyvalue_err)?;
312
313 Python::attach(|py| {
314 let py_reports: PyResult<Vec<_>> = reports
315 .into_iter()
316 .map(|report| report.into_py_any(py))
317 .collect();
318 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
319 Ok(pylist)
320 })
321 })
322 }
323
324 #[pyo3(name = "request_position_status_reports")]
325 #[pyo3(signature = (account_id, instrument_id=None))]
326 fn py_request_position_status_reports<'py>(
327 &self,
328 py: Python<'py>,
329 account_id: AccountId,
330 instrument_id: Option<InstrumentId>,
331 ) -> PyResult<Bound<'py, PyAny>> {
332 let client = self.clone();
333
334 pyo3_async_runtimes::tokio::future_into_py(py, async move {
335 let reports = client
336 .request_position_status_reports(account_id, instrument_id)
337 .await
338 .map_err(to_pyvalue_err)?;
339
340 Python::attach(|py| {
341 let py_reports: PyResult<Vec<_>> = reports
342 .into_iter()
343 .map(|report| report.into_py_any(py))
344 .collect();
345 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
346 Ok(pylist)
347 })
348 })
349 }
350}
351
352impl From<DeribitHttpError> for PyErr {
353 fn from(error: DeribitHttpError) -> Self {
354 match error {
355 DeribitHttpError::Canceled(msg) => to_pyruntime_err(format!("Request canceled: {msg}")),
357 DeribitHttpError::NetworkError(msg) => {
358 to_pyruntime_err(format!("Network error: {msg}"))
359 }
360 DeribitHttpError::UnexpectedStatus { status, body } => {
361 to_pyruntime_err(format!("Unexpected HTTP status code {status}: {body}"))
362 }
363 DeribitHttpError::Timeout(msg) => to_pyruntime_err(format!("Request timeout: {msg}")),
364 DeribitHttpError::MissingCredentials => {
366 to_pyvalue_err("Missing credentials for authenticated request")
367 }
368 DeribitHttpError::ValidationError(msg) => {
369 to_pyvalue_err(format!("Parameter validation error: {msg}"))
370 }
371 DeribitHttpError::JsonError(msg) => to_pyvalue_err(format!("JSON error: {msg}")),
372 DeribitHttpError::DeribitError {
373 error_code,
374 message,
375 } => to_pyvalue_err(format!("Deribit error {error_code}: {message}")),
376 }
377 }
378}