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::{prelude::*, types::PyList};
26
27use crate::http::{
28 client::DeribitHttpClient,
29 error::DeribitHttpError,
30 models::{DeribitCurrency, DeribitInstrumentKind},
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 is_testnet,
64 timeout_secs,
65 max_retries,
66 retry_delay_ms,
67 retry_delay_max_ms,
68 proxy_url,
69 )
70 .map_err(to_pyvalue_err)
71 }
72
73 #[getter]
74 #[pyo3(name = "is_testnet")]
75 #[must_use]
76 pub fn py_is_testnet(&self) -> bool {
77 self.is_testnet()
78 }
79
80 #[pyo3(name = "is_initialized")]
81 #[must_use]
82 pub fn py_is_initialized(&self) -> bool {
83 self.is_cache_initialized()
84 }
85
86 #[pyo3(name = "cache_instruments")]
90 pub fn py_cache_instruments(
91 &self,
92 py: Python<'_>,
93 instruments: Vec<Py<PyAny>>,
94 ) -> PyResult<()> {
95 let instruments: Result<Vec<_>, _> = instruments
96 .into_iter()
97 .map(|inst| pyobject_to_instrument_any(py, inst))
98 .collect();
99 self.cache_instruments(instruments?);
100 Ok(())
101 }
102
103 #[pyo3(name = "cache_instrument")]
107 pub fn py_cache_instrument(&self, py: Python<'_>, instrument: Py<PyAny>) -> PyResult<()> {
108 let inst = pyobject_to_instrument_any(py, instrument)?;
109 self.cache_instruments(vec![inst]);
110 Ok(())
111 }
112
113 #[pyo3(name = "request_instruments")]
114 #[pyo3(signature = (currency, kind=None))]
115 fn py_request_instruments<'py>(
116 &self,
117 py: Python<'py>,
118 currency: DeribitCurrency,
119 kind: Option<DeribitInstrumentKind>,
120 ) -> PyResult<Bound<'py, PyAny>> {
121 let client = self.clone();
122
123 pyo3_async_runtimes::tokio::future_into_py(py, async move {
124 let instruments = client
125 .request_instruments(currency, kind)
126 .await
127 .map_err(to_pyvalue_err)?;
128
129 Python::attach(|py| {
130 let py_instruments: PyResult<Vec<_>> = instruments
131 .into_iter()
132 .map(|inst| instrument_any_to_pyobject(py, inst))
133 .collect();
134 let pylist = PyList::new(py, py_instruments?)
135 .unwrap()
136 .into_any()
137 .unbind();
138 Ok(pylist)
139 })
140 })
141 }
142
143 #[pyo3(name = "request_instrument")]
144 fn py_request_instrument<'py>(
145 &self,
146 py: Python<'py>,
147 instrument_id: InstrumentId,
148 ) -> PyResult<Bound<'py, PyAny>> {
149 let client = self.clone();
150
151 pyo3_async_runtimes::tokio::future_into_py(py, async move {
152 let instrument = client
153 .request_instrument(instrument_id)
154 .await
155 .map_err(to_pyvalue_err)?;
156
157 Python::attach(|py| instrument_any_to_pyobject(py, instrument))
158 })
159 }
160
161 #[pyo3(name = "request_account_state")]
162 fn py_request_account_state<'py>(
163 &self,
164 py: Python<'py>,
165 account_id: AccountId,
166 ) -> PyResult<Bound<'py, PyAny>> {
167 let client = self.clone();
168
169 pyo3_async_runtimes::tokio::future_into_py(py, async move {
170 let account_state = client
171 .request_account_state(account_id)
172 .await
173 .map_err(to_pyvalue_err)?;
174
175 Python::attach(|py| Ok(account_state.into_py_any_unwrap(py)))
176 })
177 }
178
179 #[pyo3(name = "request_trades")]
180 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
181 fn py_request_trades<'py>(
182 &self,
183 py: Python<'py>,
184 instrument_id: InstrumentId,
185 start: Option<DateTime<Utc>>,
186 end: Option<DateTime<Utc>>,
187 limit: Option<u32>,
188 ) -> PyResult<Bound<'py, PyAny>> {
189 let client = self.clone();
190
191 pyo3_async_runtimes::tokio::future_into_py(py, async move {
192 let trades = client
193 .request_trades(instrument_id, start, end, limit)
194 .await
195 .map_err(to_pyvalue_err)?;
196
197 Python::attach(|py| {
198 let pylist = PyList::new(
199 py,
200 trades.into_iter().map(|trade| trade.into_py_any_unwrap(py)),
201 )?;
202 Ok(pylist.into_py_any_unwrap(py))
203 })
204 })
205 }
206
207 #[pyo3(name = "request_bars")]
208 #[pyo3(signature = (bar_type, start=None, end=None, limit=None))]
209 fn py_request_bars<'py>(
210 &self,
211 py: Python<'py>,
212 bar_type: BarType,
213 start: Option<DateTime<Utc>>,
214 end: Option<DateTime<Utc>>,
215 limit: Option<u32>,
216 ) -> PyResult<Bound<'py, PyAny>> {
217 let client = self.clone();
218
219 pyo3_async_runtimes::tokio::future_into_py(py, async move {
220 let bars = client
221 .request_bars(bar_type, start, end, limit)
222 .await
223 .map_err(to_pyvalue_err)?;
224
225 Python::attach(|py| {
226 let pylist =
227 PyList::new(py, bars.into_iter().map(|bar| bar.into_py_any_unwrap(py)))?;
228 Ok(pylist.into_py_any_unwrap(py))
229 })
230 })
231 }
232
233 #[pyo3(name = "request_book_snapshot")]
234 #[pyo3(signature = (instrument_id, depth=None))]
235 fn py_request_book_snapshot<'py>(
236 &self,
237 py: Python<'py>,
238 instrument_id: InstrumentId,
239 depth: Option<u32>,
240 ) -> PyResult<Bound<'py, PyAny>> {
241 let client = self.clone();
242
243 pyo3_async_runtimes::tokio::future_into_py(py, async move {
244 let book = client
245 .request_book_snapshot(instrument_id, depth)
246 .await
247 .map_err(to_pyvalue_err)?;
248
249 Python::attach(|py| Ok(book.into_py_any_unwrap(py)))
250 })
251 }
252}
253
254impl From<DeribitHttpError> for PyErr {
255 fn from(error: DeribitHttpError) -> Self {
256 match error {
257 DeribitHttpError::Canceled(msg) => to_pyruntime_err(format!("Request canceled: {msg}")),
259 DeribitHttpError::NetworkError(msg) => {
260 to_pyruntime_err(format!("Network error: {msg}"))
261 }
262 DeribitHttpError::UnexpectedStatus { status, body } => {
263 to_pyruntime_err(format!("Unexpected HTTP status code {status}: {body}"))
264 }
265 DeribitHttpError::Timeout(msg) => to_pyruntime_err(format!("Request timeout: {msg}")),
266 DeribitHttpError::MissingCredentials => {
268 to_pyvalue_err("Missing credentials for authenticated request")
269 }
270 DeribitHttpError::ValidationError(msg) => {
271 to_pyvalue_err(format!("Parameter validation error: {msg}"))
272 }
273 DeribitHttpError::JsonError(msg) => to_pyvalue_err(format!("JSON error: {msg}")),
274 DeribitHttpError::DeribitError {
275 error_code,
276 message,
277 } => to_pyvalue_err(format!("Deribit error {error_code}: {message}")),
278 }
279 }
280}