1use jiff::Timestamp;
19use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
20use nautilus_model::{
21 data::BarType,
22 enums::{OrderSide, OrderType, TimeInForce, TriggerType},
23 identifiers::{AccountId, ClientOrderId, InstrumentId, VenueOrderId},
24 python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
25 types::{Price, Quantity},
26};
27use pyo3::{
28 conversion::IntoPyObjectExt,
29 prelude::*,
30 types::{PyDict, PyList},
31};
32
33use crate::{
34 common::{credential::KrakenCredential, enums::KrakenEnvironment},
35 http::KrakenFuturesHttpClient,
36};
37
38#[pymethods]
39#[pyo3_stub_gen::derive::gen_stub_pymethods]
40impl KrakenFuturesHttpClient {
41 #[new]
47 #[pyo3(signature = (api_key=None, api_secret=None, base_url=None, demo=false, timeout_secs=60, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None, proxy_url=None, max_requests_per_second=5))]
48 #[expect(clippy::too_many_arguments)]
49 fn py_new(
50 api_key: Option<String>,
51 api_secret: Option<String>,
52 base_url: Option<String>,
53 demo: bool,
54 timeout_secs: 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 max_requests_per_second: u32,
60 ) -> PyResult<Self> {
61 let environment = if demo {
62 KrakenEnvironment::Demo
63 } else {
64 KrakenEnvironment::Live
65 };
66
67 if let Some(cred) = KrakenCredential::resolve_futures(api_key, api_secret, demo) {
68 let (k, s) = cred.into_parts();
69 Self::with_credentials(
70 k,
71 s,
72 environment,
73 base_url,
74 timeout_secs,
75 max_retries,
76 retry_delay_ms,
77 retry_delay_max_ms,
78 proxy_url,
79 max_requests_per_second,
80 )
81 .map_err(to_pyvalue_err)
82 } else {
83 Self::new(
84 environment,
85 base_url,
86 timeout_secs,
87 max_retries,
88 retry_delay_ms,
89 retry_delay_max_ms,
90 proxy_url,
91 max_requests_per_second,
92 )
93 .map_err(to_pyvalue_err)
94 }
95 }
96
97 #[getter]
98 #[pyo3(name = "base_url")]
99 #[must_use]
100 pub fn py_base_url(&self) -> String {
101 self.inner.base_url().to_string()
102 }
103
104 #[getter]
105 #[pyo3(name = "api_key")]
106 #[must_use]
107 pub fn py_api_key(&self) -> Option<&str> {
108 self.inner.credential().map(|c| c.api_key())
109 }
110
111 #[getter]
112 #[pyo3(name = "api_key_masked")]
113 #[must_use]
114 pub fn py_api_key_masked(&self) -> Option<String> {
115 self.inner.credential().map(|c| c.api_key_masked())
116 }
117
118 #[pyo3(name = "cache_instrument")]
120 fn py_cache_instrument(&self, py: Python, instrument: Py<PyAny>) -> PyResult<()> {
121 let inst_any = pyobject_to_instrument_any(py, instrument)?;
122 self.cache_instrument(inst_any);
123 Ok(())
124 }
125
126 #[pyo3(name = "cancel_all_requests")]
128 fn py_cancel_all_requests(&self) {
129 self.cancel_all_requests();
130 }
131
132 #[pyo3(name = "request_instruments")]
134 fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
135 let client = self.clone();
136
137 pyo3_async_runtimes::tokio::future_into_py(py, async move {
138 let instruments = client
139 .request_instruments()
140 .await
141 .map_err(to_pyruntime_err)?;
142
143 Python::attach(|py| {
144 let py_instruments: PyResult<Vec<_>> = instruments
145 .into_iter()
146 .map(|inst| instrument_any_to_pyobject(py, inst))
147 .collect();
148 let pylist = PyList::new(py, py_instruments?)?;
149 Ok(pylist.unbind())
150 })
151 })
152 }
153
154 #[pyo3(name = "request_instrument_statuses")]
156 fn py_request_instrument_statuses<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
157 let client = self.clone();
158
159 pyo3_async_runtimes::tokio::future_into_py(py, async move {
160 let statuses = client
161 .request_instrument_statuses()
162 .await
163 .map_err(to_pyruntime_err)?;
164
165 Python::attach(|py| {
166 let dict = PyDict::new(py);
167 for (instrument_id, action) in statuses {
168 dict.set_item(
169 instrument_id.into_bound_py_any(py)?,
170 action.into_bound_py_any(py)?,
171 )?;
172 }
173 Ok(dict.into_any().unbind())
174 })
175 })
176 }
177
178 #[pyo3(name = "request_trades")]
179 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
180 fn py_request_trades<'py>(
181 &self,
182 py: Python<'py>,
183 instrument_id: InstrumentId,
184 start: Option<Timestamp>,
185 end: Option<Timestamp>,
186 limit: Option<u64>,
187 ) -> PyResult<Bound<'py, PyAny>> {
188 let client = self.clone();
189
190 pyo3_async_runtimes::tokio::future_into_py(py, async move {
191 let trades = client
192 .request_trades(instrument_id, start, end, limit)
193 .await
194 .map_err(to_pyruntime_err)?;
195
196 Python::attach(|py| {
197 let py_trades: PyResult<Vec<_>> = trades
198 .into_iter()
199 .map(|trade| trade.into_py_any(py))
200 .collect();
201 let pylist = PyList::new(py, py_trades?)?.into_any().unbind();
202 Ok(pylist)
203 })
204 })
205 }
206
207 #[pyo3(name = "request_mark_price")]
209 fn py_request_mark_price<'py>(
210 &self,
211 py: Python<'py>,
212 instrument_id: InstrumentId,
213 ) -> PyResult<Bound<'py, PyAny>> {
214 let client = self.clone();
215
216 pyo3_async_runtimes::tokio::future_into_py(py, async move {
217 let mark_price = client
218 .request_mark_price(instrument_id)
219 .await
220 .map_err(to_pyruntime_err)?;
221
222 Ok(mark_price)
223 })
224 }
225
226 #[pyo3(name = "request_index_price")]
227 fn py_request_index_price<'py>(
228 &self,
229 py: Python<'py>,
230 instrument_id: InstrumentId,
231 ) -> PyResult<Bound<'py, PyAny>> {
232 let client = self.clone();
233
234 pyo3_async_runtimes::tokio::future_into_py(py, async move {
235 let index_price = client
236 .request_index_price(instrument_id)
237 .await
238 .map_err(to_pyruntime_err)?;
239
240 Ok(index_price)
241 })
242 }
243
244 #[pyo3(name = "request_book_snapshot")]
246 #[pyo3(signature = (instrument_id, depth=None))]
247 fn py_request_book_snapshot<'py>(
248 &self,
249 py: Python<'py>,
250 instrument_id: InstrumentId,
251 depth: Option<u32>,
252 ) -> PyResult<Bound<'py, PyAny>> {
253 let client = self.clone();
254
255 pyo3_async_runtimes::tokio::future_into_py(py, async move {
256 let book = client
257 .request_book_snapshot(instrument_id, depth)
258 .await
259 .map_err(to_pyruntime_err)?;
260
261 Python::attach(|py| book.into_py_any(py))
262 })
263 }
264
265 #[pyo3(name = "request_bars")]
266 #[pyo3(signature = (bar_type, start=None, end=None, limit=None))]
267 fn py_request_bars<'py>(
268 &self,
269 py: Python<'py>,
270 bar_type: BarType,
271 start: Option<Timestamp>,
272 end: Option<Timestamp>,
273 limit: Option<u64>,
274 ) -> PyResult<Bound<'py, PyAny>> {
275 let client = self.clone();
276
277 pyo3_async_runtimes::tokio::future_into_py(py, async move {
278 let bars = client
279 .request_bars(bar_type, start, end, limit)
280 .await
281 .map_err(to_pyruntime_err)?;
282
283 Python::attach(|py| {
284 let py_bars: PyResult<Vec<_>> =
285 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
286 let pylist = PyList::new(py, py_bars?)?.into_any().unbind();
287 Ok(pylist)
288 })
289 })
290 }
291
292 #[pyo3(name = "request_account_state")]
304 fn py_request_account_state<'py>(
305 &self,
306 py: Python<'py>,
307 account_id: AccountId,
308 ) -> PyResult<Bound<'py, PyAny>> {
309 let client = self.clone();
310
311 pyo3_async_runtimes::tokio::future_into_py(py, async move {
312 let account_state = client
313 .request_account_state(account_id)
314 .await
315 .map_err(to_pyruntime_err)?;
316
317 Python::attach(|py| account_state.into_pyobject(py).map(|o| o.unbind()))
318 })
319 }
320
321 #[pyo3(name = "request_order_status_reports")]
322 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None, open_only=false))]
323 fn py_request_order_status_reports<'py>(
324 &self,
325 py: Python<'py>,
326 account_id: AccountId,
327 instrument_id: Option<InstrumentId>,
328 start: Option<Timestamp>,
329 end: Option<Timestamp>,
330 open_only: bool,
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_order_status_reports(account_id, instrument_id, start, end, open_only)
337 .await
338 .map_err(to_pyruntime_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 #[pyo3(name = "request_fill_reports")]
352 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None))]
353 fn py_request_fill_reports<'py>(
354 &self,
355 py: Python<'py>,
356 account_id: AccountId,
357 instrument_id: Option<InstrumentId>,
358 start: Option<Timestamp>,
359 end: Option<Timestamp>,
360 ) -> PyResult<Bound<'py, PyAny>> {
361 let client = self.clone();
362
363 pyo3_async_runtimes::tokio::future_into_py(py, async move {
364 let reports = client
365 .request_fill_reports(account_id, instrument_id, start, end)
366 .await
367 .map_err(to_pyruntime_err)?;
368
369 Python::attach(|py| {
370 let py_reports: PyResult<Vec<_>> = reports
371 .into_iter()
372 .map(|report| report.into_py_any(py))
373 .collect();
374 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
375 Ok(pylist)
376 })
377 })
378 }
379
380 #[pyo3(name = "request_position_status_reports")]
381 #[pyo3(signature = (account_id, instrument_id=None))]
382 fn py_request_position_status_reports<'py>(
383 &self,
384 py: Python<'py>,
385 account_id: AccountId,
386 instrument_id: Option<InstrumentId>,
387 ) -> PyResult<Bound<'py, PyAny>> {
388 let client = self.clone();
389
390 pyo3_async_runtimes::tokio::future_into_py(py, async move {
391 let reports = client
392 .request_position_status_reports(account_id, instrument_id)
393 .await
394 .map_err(to_pyruntime_err)?;
395
396 Python::attach(|py| {
397 let py_reports: PyResult<Vec<_>> = reports
398 .into_iter()
399 .map(|report| report.into_py_any(py))
400 .collect();
401 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
402 Ok(pylist)
403 })
404 })
405 }
406
407 #[pyo3(name = "submit_order")]
418 #[pyo3(signature = (account_id, instrument_id, client_order_id, order_side, order_type, quantity, time_in_force, price=None, trigger_price=None, trigger_type=None, reduce_only=false, post_only=false))]
419 #[expect(clippy::too_many_arguments)]
420 fn py_submit_order<'py>(
421 &self,
422 py: Python<'py>,
423 account_id: AccountId,
424 instrument_id: InstrumentId,
425 client_order_id: ClientOrderId,
426 order_side: OrderSide,
427 order_type: OrderType,
428 quantity: Quantity,
429 time_in_force: TimeInForce,
430 price: Option<Price>,
431 trigger_price: Option<Price>,
432 trigger_type: Option<TriggerType>,
433 reduce_only: bool,
434 post_only: bool,
435 ) -> PyResult<Bound<'py, PyAny>> {
436 let client = self.clone();
437
438 pyo3_async_runtimes::tokio::future_into_py(py, async move {
439 let report = client
440 .submit_order(
441 account_id,
442 instrument_id,
443 client_order_id,
444 order_side,
445 order_type,
446 quantity,
447 time_in_force,
448 price,
449 trigger_price,
450 trigger_type,
451 reduce_only,
452 post_only,
453 )
454 .await
455 .map_err(to_pyruntime_err)?;
456
457 Python::attach(|py| report.into_pyobject(py).map(|o| o.unbind()))
458 })
459 }
460
461 #[pyo3(name = "modify_order")]
473 #[pyo3(signature = (instrument_id, client_order_id=None, venue_order_id=None, quantity=None, price=None, trigger_price=None))]
474 #[expect(clippy::too_many_arguments)]
475 fn py_modify_order<'py>(
476 &self,
477 py: Python<'py>,
478 instrument_id: InstrumentId,
479 client_order_id: Option<ClientOrderId>,
480 venue_order_id: Option<VenueOrderId>,
481 quantity: Option<Quantity>,
482 price: Option<Price>,
483 trigger_price: Option<Price>,
484 ) -> PyResult<Bound<'py, PyAny>> {
485 let client = self.clone();
486
487 pyo3_async_runtimes::tokio::future_into_py(py, async move {
488 let new_venue_order_id = client
489 .modify_order(
490 instrument_id,
491 client_order_id,
492 venue_order_id,
493 quantity,
494 price,
495 trigger_price,
496 )
497 .await
498 .map_err(to_pyruntime_err)?;
499
500 Python::attach(|py| new_venue_order_id.into_pyobject(py).map(|o| o.unbind()))
501 })
502 }
503
504 #[pyo3(name = "cancel_order")]
514 #[pyo3(signature = (account_id, instrument_id, client_order_id=None, venue_order_id=None))]
515 fn py_cancel_order<'py>(
516 &self,
517 py: Python<'py>,
518 account_id: AccountId,
519 instrument_id: InstrumentId,
520 client_order_id: Option<ClientOrderId>,
521 venue_order_id: Option<VenueOrderId>,
522 ) -> PyResult<Bound<'py, PyAny>> {
523 let client = self.clone();
524
525 pyo3_async_runtimes::tokio::future_into_py(py, async move {
526 client
527 .cancel_order(account_id, instrument_id, client_order_id, venue_order_id)
528 .await
529 .map_err(to_pyruntime_err)
530 })
531 }
532
533 #[pyo3(name = "cancel_all_orders")]
534 #[pyo3(signature = (instrument_id=None))]
535 fn py_cancel_all_orders<'py>(
536 &self,
537 py: Python<'py>,
538 instrument_id: Option<InstrumentId>,
539 ) -> PyResult<Bound<'py, PyAny>> {
540 let client = self.clone();
541
542 pyo3_async_runtimes::tokio::future_into_py(py, async move {
543 let symbol = instrument_id.map(|id| id.symbol.to_string());
544 let response = client
545 .inner
546 .cancel_all_orders(symbol)
547 .await
548 .map_err(to_pyruntime_err)?;
549
550 Ok(response.cancel_status.cancelled_orders.len())
551 })
552 }
553
554 #[pyo3(name = "cancel_orders_batch")]
564 fn py_cancel_orders_batch<'py>(
565 &self,
566 py: Python<'py>,
567 venue_order_ids: Vec<VenueOrderId>,
568 ) -> PyResult<Bound<'py, PyAny>> {
569 let client = self.clone();
570
571 pyo3_async_runtimes::tokio::future_into_py(py, async move {
572 client
573 .cancel_orders_batch(venue_order_ids)
574 .await
575 .map_err(to_pyruntime_err)
576 })
577 }
578}
579
580#[pymethods]
584impl KrakenFuturesHttpClient {
585 #[pyo3(name = "submit_orders_batch")]
594 #[expect(clippy::type_complexity)]
595 fn py_submit_orders_batch<'py>(
596 &self,
597 py: Python<'py>,
598 orders: Vec<(
599 InstrumentId,
600 ClientOrderId,
601 OrderSide,
602 OrderType,
603 Quantity,
604 TimeInForce,
605 Option<Price>,
606 Option<Price>,
607 Option<TriggerType>,
608 bool,
609 bool,
610 )>,
611 ) -> PyResult<Bound<'py, PyAny>> {
612 let client = self.clone();
613
614 pyo3_async_runtimes::tokio::future_into_py(py, async move {
615 let statuses = client
616 .submit_orders_batch(orders)
617 .await
618 .map_err(to_pyruntime_err)?;
619
620 let result: Vec<String> = statuses.into_iter().map(|s| s.status).collect();
621 Ok(result)
622 })
623 }
624
625 #[expect(clippy::type_complexity)]
627 #[pyo3(name = "edit_orders_batch")]
628 fn py_edit_orders_batch<'py>(
629 &self,
630 py: Python<'py>,
631 orders: Vec<(
632 InstrumentId,
633 Option<ClientOrderId>,
634 Option<VenueOrderId>,
635 Option<Quantity>,
636 Option<Price>,
637 Option<Price>,
638 )>,
639 ) -> PyResult<Bound<'py, PyAny>> {
640 let client = self.clone();
641
642 pyo3_async_runtimes::tokio::future_into_py(py, async move {
643 client
644 .edit_orders_batch(orders)
645 .await
646 .map_err(to_pyruntime_err)
647 })
648 }
649}