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")]
140 fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
141 let client = self.clone();
142
143 pyo3_async_runtimes::tokio::future_into_py(py, async move {
144 let instruments = client
145 .request_instruments()
146 .await
147 .map_err(to_pyruntime_err)?;
148
149 Python::attach(|py| {
150 let py_instruments: PyResult<Vec<_>> = instruments
151 .into_iter()
152 .map(|inst| instrument_any_to_pyobject(py, inst))
153 .collect();
154 let pylist = PyList::new(py, py_instruments?)?;
155 Ok(pylist.unbind())
156 })
157 })
158 }
159
160 #[pyo3(name = "request_instrument_statuses")]
162 fn py_request_instrument_statuses<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
163 let client = self.clone();
164
165 pyo3_async_runtimes::tokio::future_into_py(py, async move {
166 let statuses = client
167 .request_instrument_statuses()
168 .await
169 .map_err(to_pyruntime_err)?;
170
171 Python::attach(|py| {
172 let dict = PyDict::new(py);
173 for (instrument_id, action) in statuses {
174 dict.set_item(
175 instrument_id.into_bound_py_any(py)?,
176 action.into_bound_py_any(py)?,
177 )?;
178 }
179 Ok(dict.into_any().unbind())
180 })
181 })
182 }
183
184 #[pyo3(name = "request_trades")]
185 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
186 fn py_request_trades<'py>(
187 &self,
188 py: Python<'py>,
189 instrument_id: InstrumentId,
190 start: Option<Timestamp>,
191 end: Option<Timestamp>,
192 limit: Option<u64>,
193 ) -> PyResult<Bound<'py, PyAny>> {
194 let client = self.clone();
195
196 pyo3_async_runtimes::tokio::future_into_py(py, async move {
197 let trades = client
198 .request_trades(instrument_id, start, end, limit)
199 .await
200 .map_err(to_pyruntime_err)?;
201
202 Python::attach(|py| {
203 let py_trades: PyResult<Vec<_>> = trades
204 .into_iter()
205 .map(|trade| trade.into_py_any(py))
206 .collect();
207 let pylist = PyList::new(py, py_trades?)?.into_any().unbind();
208 Ok(pylist)
209 })
210 })
211 }
212
213 #[pyo3(name = "request_mark_price")]
215 fn py_request_mark_price<'py>(
216 &self,
217 py: Python<'py>,
218 instrument_id: InstrumentId,
219 ) -> PyResult<Bound<'py, PyAny>> {
220 let client = self.clone();
221
222 pyo3_async_runtimes::tokio::future_into_py(py, async move {
223 let mark_price = client
224 .request_mark_price(instrument_id)
225 .await
226 .map_err(to_pyruntime_err)?;
227
228 Ok(mark_price)
229 })
230 }
231
232 #[pyo3(name = "request_index_price")]
233 fn py_request_index_price<'py>(
234 &self,
235 py: Python<'py>,
236 instrument_id: InstrumentId,
237 ) -> PyResult<Bound<'py, PyAny>> {
238 let client = self.clone();
239
240 pyo3_async_runtimes::tokio::future_into_py(py, async move {
241 let index_price = client
242 .request_index_price(instrument_id)
243 .await
244 .map_err(to_pyruntime_err)?;
245
246 Ok(index_price)
247 })
248 }
249
250 #[pyo3(name = "request_book_snapshot")]
252 #[pyo3(signature = (instrument_id, depth=None))]
253 fn py_request_book_snapshot<'py>(
254 &self,
255 py: Python<'py>,
256 instrument_id: InstrumentId,
257 depth: Option<u32>,
258 ) -> PyResult<Bound<'py, PyAny>> {
259 let client = self.clone();
260
261 pyo3_async_runtimes::tokio::future_into_py(py, async move {
262 let book = client
263 .request_book_snapshot(instrument_id, depth)
264 .await
265 .map_err(to_pyruntime_err)?;
266
267 Python::attach(|py| book.into_py_any(py))
268 })
269 }
270
271 #[pyo3(name = "request_bars")]
272 #[pyo3(signature = (bar_type, start=None, end=None, limit=None))]
273 fn py_request_bars<'py>(
274 &self,
275 py: Python<'py>,
276 bar_type: BarType,
277 start: Option<Timestamp>,
278 end: Option<Timestamp>,
279 limit: Option<u64>,
280 ) -> PyResult<Bound<'py, PyAny>> {
281 let client = self.clone();
282
283 pyo3_async_runtimes::tokio::future_into_py(py, async move {
284 let bars = client
285 .request_bars(bar_type, start, end, limit)
286 .await
287 .map_err(to_pyruntime_err)?;
288
289 Python::attach(|py| {
290 let py_bars: PyResult<Vec<_>> =
291 bars.into_iter().map(|bar| bar.into_py_any(py)).collect();
292 let pylist = PyList::new(py, py_bars?)?.into_any().unbind();
293 Ok(pylist)
294 })
295 })
296 }
297
298 #[pyo3(name = "request_account_state")]
310 fn py_request_account_state<'py>(
311 &self,
312 py: Python<'py>,
313 account_id: AccountId,
314 ) -> PyResult<Bound<'py, PyAny>> {
315 let client = self.clone();
316
317 pyo3_async_runtimes::tokio::future_into_py(py, async move {
318 let account_state = client
319 .request_account_state(account_id)
320 .await
321 .map_err(to_pyruntime_err)?;
322
323 Python::attach(|py| account_state.into_pyobject(py).map(|o| o.unbind()))
324 })
325 }
326
327 #[pyo3(name = "request_order_status_reports")]
328 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None, open_only=false))]
329 fn py_request_order_status_reports<'py>(
330 &self,
331 py: Python<'py>,
332 account_id: AccountId,
333 instrument_id: Option<InstrumentId>,
334 start: Option<Timestamp>,
335 end: Option<Timestamp>,
336 open_only: bool,
337 ) -> PyResult<Bound<'py, PyAny>> {
338 let client = self.clone();
339
340 pyo3_async_runtimes::tokio::future_into_py(py, async move {
341 let reports = client
342 .request_order_status_reports(account_id, instrument_id, start, end, open_only)
343 .await
344 .map_err(to_pyruntime_err)?;
345
346 Python::attach(|py| {
347 let py_reports: PyResult<Vec<_>> = reports
348 .into_iter()
349 .map(|report| report.into_py_any(py))
350 .collect();
351 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
352 Ok(pylist)
353 })
354 })
355 }
356
357 #[pyo3(name = "request_fill_reports")]
358 #[pyo3(signature = (account_id, instrument_id=None, start=None, end=None))]
359 fn py_request_fill_reports<'py>(
360 &self,
361 py: Python<'py>,
362 account_id: AccountId,
363 instrument_id: Option<InstrumentId>,
364 start: Option<Timestamp>,
365 end: Option<Timestamp>,
366 ) -> PyResult<Bound<'py, PyAny>> {
367 let client = self.clone();
368
369 pyo3_async_runtimes::tokio::future_into_py(py, async move {
370 let reports = client
371 .request_fill_reports(account_id, instrument_id, start, end)
372 .await
373 .map_err(to_pyruntime_err)?;
374
375 Python::attach(|py| {
376 let py_reports: PyResult<Vec<_>> = reports
377 .into_iter()
378 .map(|report| report.into_py_any(py))
379 .collect();
380 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
381 Ok(pylist)
382 })
383 })
384 }
385
386 #[pyo3(name = "request_position_status_reports")]
387 #[pyo3(signature = (account_id, instrument_id=None))]
388 fn py_request_position_status_reports<'py>(
389 &self,
390 py: Python<'py>,
391 account_id: AccountId,
392 instrument_id: Option<InstrumentId>,
393 ) -> PyResult<Bound<'py, PyAny>> {
394 let client = self.clone();
395
396 pyo3_async_runtimes::tokio::future_into_py(py, async move {
397 let reports = client
398 .request_position_status_reports(account_id, instrument_id)
399 .await
400 .map_err(to_pyruntime_err)?;
401
402 Python::attach(|py| {
403 let py_reports: PyResult<Vec<_>> = reports
404 .into_iter()
405 .map(|report| report.into_py_any(py))
406 .collect();
407 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
408 Ok(pylist)
409 })
410 })
411 }
412
413 #[pyo3(name = "submit_order")]
424 #[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))]
425 #[expect(clippy::too_many_arguments)]
426 fn py_submit_order<'py>(
427 &self,
428 py: Python<'py>,
429 account_id: AccountId,
430 instrument_id: InstrumentId,
431 client_order_id: ClientOrderId,
432 order_side: OrderSide,
433 order_type: OrderType,
434 quantity: Quantity,
435 time_in_force: TimeInForce,
436 price: Option<Price>,
437 trigger_price: Option<Price>,
438 trigger_type: Option<TriggerType>,
439 reduce_only: bool,
440 post_only: bool,
441 ) -> PyResult<Bound<'py, PyAny>> {
442 let client = self.clone();
443
444 pyo3_async_runtimes::tokio::future_into_py(py, async move {
445 let report = client
446 .submit_order(
447 account_id,
448 instrument_id,
449 client_order_id,
450 order_side,
451 order_type,
452 quantity,
453 time_in_force,
454 price,
455 trigger_price,
456 trigger_type,
457 reduce_only,
458 post_only,
459 )
460 .await
461 .map_err(to_pyruntime_err)?;
462
463 Python::attach(|py| report.into_pyobject(py).map(|o| o.unbind()))
464 })
465 }
466
467 #[pyo3(name = "modify_order")]
479 #[pyo3(signature = (instrument_id, client_order_id=None, venue_order_id=None, quantity=None, price=None, trigger_price=None))]
480 #[expect(clippy::too_many_arguments)]
481 fn py_modify_order<'py>(
482 &self,
483 py: Python<'py>,
484 instrument_id: InstrumentId,
485 client_order_id: Option<ClientOrderId>,
486 venue_order_id: Option<VenueOrderId>,
487 quantity: Option<Quantity>,
488 price: Option<Price>,
489 trigger_price: Option<Price>,
490 ) -> PyResult<Bound<'py, PyAny>> {
491 let client = self.clone();
492
493 pyo3_async_runtimes::tokio::future_into_py(py, async move {
494 let new_venue_order_id = client
495 .modify_order(
496 instrument_id,
497 client_order_id,
498 venue_order_id,
499 quantity,
500 price,
501 trigger_price,
502 )
503 .await
504 .map_err(to_pyruntime_err)?;
505
506 Python::attach(|py| new_venue_order_id.into_pyobject(py).map(|o| o.unbind()))
507 })
508 }
509
510 #[pyo3(name = "cancel_order")]
520 #[pyo3(signature = (account_id, instrument_id, client_order_id=None, venue_order_id=None))]
521 fn py_cancel_order<'py>(
522 &self,
523 py: Python<'py>,
524 account_id: AccountId,
525 instrument_id: InstrumentId,
526 client_order_id: Option<ClientOrderId>,
527 venue_order_id: Option<VenueOrderId>,
528 ) -> PyResult<Bound<'py, PyAny>> {
529 let client = self.clone();
530
531 pyo3_async_runtimes::tokio::future_into_py(py, async move {
532 client
533 .cancel_order(account_id, instrument_id, client_order_id, venue_order_id)
534 .await
535 .map_err(to_pyruntime_err)
536 })
537 }
538
539 #[pyo3(name = "cancel_all_orders")]
540 #[pyo3(signature = (instrument_id=None))]
541 fn py_cancel_all_orders<'py>(
542 &self,
543 py: Python<'py>,
544 instrument_id: Option<InstrumentId>,
545 ) -> PyResult<Bound<'py, PyAny>> {
546 let client = self.clone();
547
548 pyo3_async_runtimes::tokio::future_into_py(py, async move {
549 let symbol = instrument_id.map(|id| id.symbol.to_string());
550 let response = client
551 .inner
552 .cancel_all_orders(symbol)
553 .await
554 .map_err(to_pyruntime_err)?;
555
556 Ok(response.cancel_status.cancelled_orders.len())
557 })
558 }
559
560 #[pyo3(name = "cancel_orders_batch")]
570 fn py_cancel_orders_batch<'py>(
571 &self,
572 py: Python<'py>,
573 venue_order_ids: Vec<VenueOrderId>,
574 ) -> PyResult<Bound<'py, PyAny>> {
575 let client = self.clone();
576
577 pyo3_async_runtimes::tokio::future_into_py(py, async move {
578 client
579 .cancel_orders_batch(venue_order_ids)
580 .await
581 .map_err(to_pyruntime_err)
582 })
583 }
584}
585
586#[pymethods]
590impl KrakenFuturesHttpClient {
591 #[pyo3(name = "submit_orders_batch")]
600 #[expect(clippy::type_complexity)]
601 fn py_submit_orders_batch<'py>(
602 &self,
603 py: Python<'py>,
604 orders: Vec<(
605 InstrumentId,
606 ClientOrderId,
607 OrderSide,
608 OrderType,
609 Quantity,
610 TimeInForce,
611 Option<Price>,
612 Option<Price>,
613 Option<TriggerType>,
614 bool,
615 bool,
616 )>,
617 ) -> PyResult<Bound<'py, PyAny>> {
618 let client = self.clone();
619
620 pyo3_async_runtimes::tokio::future_into_py(py, async move {
621 let statuses = client
622 .submit_orders_batch(orders)
623 .await
624 .map_err(to_pyruntime_err)?;
625
626 let result: Vec<String> = statuses.into_iter().map(|s| s.status).collect();
627 Ok(result)
628 })
629 }
630
631 #[expect(clippy::type_complexity)]
633 #[pyo3(name = "edit_orders_batch")]
634 fn py_edit_orders_batch<'py>(
635 &self,
636 py: Python<'py>,
637 orders: Vec<(
638 InstrumentId,
639 Option<ClientOrderId>,
640 Option<VenueOrderId>,
641 Option<Quantity>,
642 Option<Price>,
643 Option<Price>,
644 )>,
645 ) -> PyResult<Bound<'py, PyAny>> {
646 let client = self.clone();
647
648 pyo3_async_runtimes::tokio::future_into_py(py, async move {
649 client
650 .edit_orders_batch(orders)
651 .await
652 .map_err(to_pyruntime_err)
653 })
654 }
655}