1use chrono::{DateTime, Utc};
19use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
20use nautilus_model::{
21 data::BarType,
22 enums::{OrderSide, OrderType, TimeInForce},
23 identifiers::{AccountId, ClientOrderId, InstrumentId, 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::{
30 common::{credential::KrakenCredential, enums::KrakenEnvironment},
31 http::KrakenFuturesHttpClient,
32};
33
34#[pymethods]
35impl KrakenFuturesHttpClient {
36 #[new]
37 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, demo=false, timeout_secs=None, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None, proxy_url=None, max_requests_per_second=None))]
38 #[allow(clippy::too_many_arguments)]
39 fn py_new(
40 api_key: Option<String>,
41 api_secret: Option<String>,
42 base_url: Option<String>,
43 demo: bool,
44 timeout_secs: Option<u64>,
45 max_retries: Option<u32>,
46 retry_delay_ms: Option<u64>,
47 retry_delay_max_ms: Option<u64>,
48 proxy_url: Option<String>,
49 max_requests_per_second: Option<u32>,
50 ) -> PyResult<Self> {
51 let timeout = timeout_secs.or(Some(60));
52
53 let environment = if demo {
54 KrakenEnvironment::Demo
55 } else {
56 KrakenEnvironment::Mainnet
57 };
58
59 if let Some(cred) = KrakenCredential::resolve_futures(api_key, api_secret, demo) {
60 let (k, s) = cred.into_parts();
61 Self::with_credentials(
62 k,
63 s,
64 environment,
65 base_url,
66 timeout,
67 max_retries,
68 retry_delay_ms,
69 retry_delay_max_ms,
70 proxy_url,
71 max_requests_per_second,
72 )
73 .map_err(to_pyvalue_err)
74 } else {
75 Self::new(
76 environment,
77 base_url,
78 timeout,
79 max_retries,
80 retry_delay_ms,
81 retry_delay_max_ms,
82 proxy_url,
83 max_requests_per_second,
84 )
85 .map_err(to_pyvalue_err)
86 }
87 }
88
89 #[getter]
90 #[pyo3(name = "base_url")]
91 #[must_use]
92 pub fn py_base_url(&self) -> String {
93 self.inner.base_url().to_string()
94 }
95
96 #[getter]
97 #[pyo3(name = "api_key")]
98 #[must_use]
99 pub fn py_api_key(&self) -> Option<&str> {
100 self.inner.credential().map(|c| c.api_key())
101 }
102
103 #[getter]
104 #[pyo3(name = "api_key_masked")]
105 #[must_use]
106 pub fn py_api_key_masked(&self) -> Option<String> {
107 self.inner.credential().map(|c| c.api_key_masked())
108 }
109
110 #[pyo3(name = "cache_instrument")]
111 fn py_cache_instrument(&self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
112 let inst_any = pyobject_to_instrument_any(py, instrument)?;
113 self.cache_instrument(inst_any);
114 Ok(())
115 }
116
117 #[pyo3(name = "cancel_all_requests")]
118 fn py_cancel_all_requests(&self) {
119 self.cancel_all_requests();
120 }
121
122 #[pyo3(name = "request_instruments")]
123 fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
124 let client = self.clone();
125
126 pyo3_async_runtimes::tokio::future_into_py(py, async move {
127 let instruments = client
128 .request_instruments()
129 .await
130 .map_err(to_pyruntime_err)?;
131
132 Python::attach(|py| {
133 let py_instruments: PyResult<Vec<_>> = instruments
134 .into_iter()
135 .map(|inst| instrument_any_to_pyobject(py, inst))
136 .collect();
137 let pylist = PyList::new(py, py_instruments?).unwrap();
138 Ok(pylist.unbind())
139 })
140 })
141 }
142
143 #[pyo3(name = "request_trades")]
144 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
145 fn py_request_trades<'py>(
146 &self,
147 py: Python<'py>,
148 instrument_id: InstrumentId,
149 start: Option<DateTime<Utc>>,
150 end: Option<DateTime<Utc>>,
151 limit: Option<u64>,
152 ) -> PyResult<Bound<'py, PyAny>> {
153 let client = self.clone();
154
155 pyo3_async_runtimes::tokio::future_into_py(py, async move {
156 let trades = client
157 .request_trades(instrument_id, start, end, limit)
158 .await
159 .map_err(to_pyruntime_err)?;
160
161 Python::attach(|py| {
162 let py_trades: PyResult<Vec<_>> = trades
163 .into_iter()
164 .map(|trade| trade.into_py_any(py))
165 .collect();
166 let pylist = PyList::new(py, py_trades?).unwrap().into_any().unbind();
167 Ok(pylist)
168 })
169 })
170 }
171
172 #[pyo3(name = "request_mark_price")]
173 fn py_request_mark_price<'py>(
174 &self,
175 py: Python<'py>,
176 instrument_id: InstrumentId,
177 ) -> PyResult<Bound<'py, PyAny>> {
178 let client = self.clone();
179
180 pyo3_async_runtimes::tokio::future_into_py(py, async move {
181 let mark_price = client
182 .request_mark_price(instrument_id)
183 .await
184 .map_err(to_pyruntime_err)?;
185
186 Ok(mark_price)
187 })
188 }
189
190 #[pyo3(name = "request_index_price")]
191 fn py_request_index_price<'py>(
192 &self,
193 py: Python<'py>,
194 instrument_id: InstrumentId,
195 ) -> PyResult<Bound<'py, PyAny>> {
196 let client = self.clone();
197
198 pyo3_async_runtimes::tokio::future_into_py(py, async move {
199 let index_price = client
200 .request_index_price(instrument_id)
201 .await
202 .map_err(to_pyruntime_err)?;
203
204 Ok(index_price)
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<u64>,
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_pyruntime_err)?;
225
226 Python::attach(|py| {
227 let py_bars: PyResult<Vec<_>> =
228 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
229 let pylist = PyList::new(py, py_bars?).unwrap().into_any().unbind();
230 Ok(pylist)
231 })
232 })
233 }
234
235 #[pyo3(name = "request_account_state")]
236 fn py_request_account_state<'py>(
237 &self,
238 py: Python<'py>,
239 account_id: AccountId,
240 ) -> PyResult<Bound<'py, PyAny>> {
241 let client = self.clone();
242
243 pyo3_async_runtimes::tokio::future_into_py(py, async move {
244 let account_state = client
245 .request_account_state(account_id)
246 .await
247 .map_err(to_pyruntime_err)?;
248
249 Python::attach(|py| account_state.into_pyobject(py).map(|o| o.unbind()))
250 })
251 }
252
253 #[pyo3(name = "request_order_status_reports")]
254 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None, open_only=false))]
255 fn py_request_order_status_reports<'py>(
256 &self,
257 py: Python<'py>,
258 account_id: AccountId,
259 instrument_id: Option<InstrumentId>,
260 start: Option<DateTime<Utc>>,
261 end: Option<DateTime<Utc>>,
262 open_only: bool,
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_order_status_reports(account_id, instrument_id, start, end, open_only)
269 .await
270 .map_err(to_pyruntime_err)?;
271
272 Python::attach(|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_fill_reports")]
284 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None))]
285 fn py_request_fill_reports<'py>(
286 &self,
287 py: Python<'py>,
288 account_id: AccountId,
289 instrument_id: Option<InstrumentId>,
290 start: Option<DateTime<Utc>>,
291 end: Option<DateTime<Utc>>,
292 ) -> PyResult<Bound<'py, PyAny>> {
293 let client = self.clone();
294
295 pyo3_async_runtimes::tokio::future_into_py(py, async move {
296 let reports = client
297 .request_fill_reports(account_id, instrument_id, start, end)
298 .await
299 .map_err(to_pyruntime_err)?;
300
301 Python::attach(|py| {
302 let py_reports: PyResult<Vec<_>> = reports
303 .into_iter()
304 .map(|report| report.into_py_any(py))
305 .collect();
306 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
307 Ok(pylist)
308 })
309 })
310 }
311
312 #[pyo3(name = "request_position_status_reports")]
313 #[pyo3(signature = (account_id, instrument_id=None))]
314 fn py_request_position_status_reports<'py>(
315 &self,
316 py: Python<'py>,
317 account_id: AccountId,
318 instrument_id: Option<InstrumentId>,
319 ) -> PyResult<Bound<'py, PyAny>> {
320 let client = self.clone();
321
322 pyo3_async_runtimes::tokio::future_into_py(py, async move {
323 let reports = client
324 .request_position_status_reports(account_id, instrument_id)
325 .await
326 .map_err(to_pyruntime_err)?;
327
328 Python::attach(|py| {
329 let py_reports: PyResult<Vec<_>> = reports
330 .into_iter()
331 .map(|report| report.into_py_any(py))
332 .collect();
333 let pylist = PyList::new(py, py_reports?).unwrap().into_any().unbind();
334 Ok(pylist)
335 })
336 })
337 }
338
339 #[pyo3(name = "submit_order")]
340 #[pyo3(signature = (account_id, instrument_id, client_order_id, order_side, order_type, quantity, time_in_force, price=None, trigger_price=None, reduce_only=false, post_only=false))]
341 #[allow(clippy::too_many_arguments)]
342 fn py_submit_order<'py>(
343 &self,
344 py: Python<'py>,
345 account_id: AccountId,
346 instrument_id: InstrumentId,
347 client_order_id: ClientOrderId,
348 order_side: OrderSide,
349 order_type: OrderType,
350 quantity: Quantity,
351 time_in_force: TimeInForce,
352 price: Option<Price>,
353 trigger_price: Option<Price>,
354 reduce_only: bool,
355 post_only: bool,
356 ) -> PyResult<Bound<'py, PyAny>> {
357 let client = self.clone();
358
359 pyo3_async_runtimes::tokio::future_into_py(py, async move {
360 let report = client
361 .submit_order(
362 account_id,
363 instrument_id,
364 client_order_id,
365 order_side,
366 order_type,
367 quantity,
368 time_in_force,
369 price,
370 trigger_price,
371 reduce_only,
372 post_only,
373 )
374 .await
375 .map_err(to_pyruntime_err)?;
376
377 Python::attach(|py| report.into_pyobject(py).map(|o| o.unbind()))
378 })
379 }
380
381 #[pyo3(name = "modify_order")]
382 #[pyo3(signature = (instrument_id, client_order_id=None, venue_order_id=None, quantity=None, price=None, trigger_price=None))]
383 #[allow(clippy::too_many_arguments)]
384 fn py_modify_order<'py>(
385 &self,
386 py: Python<'py>,
387 instrument_id: InstrumentId,
388 client_order_id: Option<ClientOrderId>,
389 venue_order_id: Option<VenueOrderId>,
390 quantity: Option<Quantity>,
391 price: Option<Price>,
392 trigger_price: Option<Price>,
393 ) -> PyResult<Bound<'py, PyAny>> {
394 let client = self.clone();
395
396 pyo3_async_runtimes::tokio::future_into_py(py, async move {
397 let new_venue_order_id = client
398 .modify_order(
399 instrument_id,
400 client_order_id,
401 venue_order_id,
402 quantity,
403 price,
404 trigger_price,
405 )
406 .await
407 .map_err(to_pyruntime_err)?;
408
409 Python::attach(|py| new_venue_order_id.into_pyobject(py).map(|o| o.unbind()))
410 })
411 }
412
413 #[pyo3(name = "cancel_order")]
414 #[pyo3(signature = (account_id, instrument_id, client_order_id=None, venue_order_id=None))]
415 fn py_cancel_order<'py>(
416 &self,
417 py: Python<'py>,
418 account_id: AccountId,
419 instrument_id: InstrumentId,
420 client_order_id: Option<ClientOrderId>,
421 venue_order_id: Option<VenueOrderId>,
422 ) -> PyResult<Bound<'py, PyAny>> {
423 let client = self.clone();
424
425 pyo3_async_runtimes::tokio::future_into_py(py, async move {
426 client
427 .cancel_order(account_id, instrument_id, client_order_id, venue_order_id)
428 .await
429 .map_err(to_pyruntime_err)
430 })
431 }
432
433 #[pyo3(name = "cancel_all_orders")]
434 #[pyo3(signature = (instrument_id=None))]
435 fn py_cancel_all_orders<'py>(
436 &self,
437 py: Python<'py>,
438 instrument_id: Option<InstrumentId>,
439 ) -> PyResult<Bound<'py, PyAny>> {
440 let client = self.clone();
441
442 pyo3_async_runtimes::tokio::future_into_py(py, async move {
443 let symbol = instrument_id.map(|id| id.symbol.to_string());
444 let response = client
445 .inner
446 .cancel_all_orders(symbol)
447 .await
448 .map_err(to_pyruntime_err)?;
449
450 Ok(response.cancel_status.cancelled_orders.len())
451 })
452 }
453
454 #[pyo3(name = "cancel_orders_batch")]
455 fn py_cancel_orders_batch<'py>(
456 &self,
457 py: Python<'py>,
458 venue_order_ids: Vec<VenueOrderId>,
459 ) -> PyResult<Bound<'py, PyAny>> {
460 let client = self.clone();
461
462 pyo3_async_runtimes::tokio::future_into_py(py, async move {
463 client
464 .cancel_orders_batch(venue_order_ids)
465 .await
466 .map_err(to_pyruntime_err)
467 })
468 }
469}