nautilus_kraken/http/mod.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! HTTP/REST client implementations for Kraken APIs.
17//!
18//! This module provides HTTP clients for interacting with Kraken's REST endpoints:
19//!
20//! - [`spot`]: Kraken Spot REST API
21//! - [`futures`]: Kraken Futures REST API
22
23use jiff::Timestamp;
24
25pub mod error;
26pub mod futures;
27pub mod models;
28pub mod spot;
29
30/// Applies a count `limit` to `items` returned oldest-first by Kraken.
31///
32/// Kraken's historical endpoints return their page in ascending (oldest-first)
33/// order. When the caller anchors the window with `start`, the oldest `limit`
34/// items from that anchor are the intended result, so the head of the page is
35/// kept. Otherwise a count-only request (`start` is `None`) means "the most
36/// recent `limit` items", so the tail is kept rather than the head.
37pub(crate) fn apply_count_limit<T>(
38 items: &mut Vec<T>,
39 start: Option<Timestamp>,
40 limit: Option<u64>,
41) {
42 if let Some(limit) = limit {
43 let limit = limit as usize;
44 if items.len() > limit {
45 if start.is_some() {
46 items.truncate(limit);
47 } else {
48 items.drain(..items.len() - limit);
49 }
50 }
51 }
52}
53
54// Re-exports
55pub use error::KrakenHttpError;
56pub use futures::{
57 client::{
58 KRAKEN_FUTURES_DEFAULT_RATE_LIMIT_PER_SECOND, KrakenFuturesHttpClient,
59 KrakenFuturesRawHttpClient,
60 },
61 query::*,
62};
63pub use spot::{
64 client::{
65 KRAKEN_SPOT_DEFAULT_RATE_LIMIT_PER_SECOND, KrakenSpotHttpClient, KrakenSpotRawHttpClient,
66 },
67 query::*,
68};
69
70#[cfg(test)]
71mod tests {
72 use nautilus_core::UnixNanos;
73 use nautilus_model::{
74 data::{Bar, BarType},
75 types::{Price, Quantity},
76 };
77 use rstest::rstest;
78
79 use super::*;
80
81 /// Builds `n` bars in ascending (oldest-first) order, as Kraken returns them,
82 /// with `ts_event` set to the 1-based index so ordering is easy to assert.
83 fn ascending_bars(n: u64) -> Vec<Bar> {
84 let bar_type = BarType::from("ETH/USD.KRAKEN-1-MINUTE-LAST-EXTERNAL");
85 let price = Price::from("100.0");
86 let volume = Quantity::from("1");
87 (1..=n)
88 .map(|i| {
89 Bar::new(
90 bar_type,
91 price,
92 price,
93 price,
94 price,
95 volume,
96 UnixNanos::from(i),
97 UnixNanos::default(),
98 )
99 })
100 .collect()
101 }
102
103 #[rstest]
104 fn test_apply_count_limit_no_limit_keeps_all() {
105 let mut bars = ascending_bars(5);
106 apply_count_limit(&mut bars, None, None);
107 assert_eq!(bars.len(), 5);
108 }
109
110 #[rstest]
111 fn test_apply_count_limit_count_only_keeps_most_recent() {
112 let mut bars = ascending_bars(10);
113 apply_count_limit(&mut bars, None, Some(3));
114 let ts: Vec<u64> = bars.iter().map(|b| b.ts_event.as_u64()).collect();
115 assert_eq!(ts, vec![8, 9, 10]);
116 }
117
118 #[rstest]
119 fn test_apply_count_limit_with_start_keeps_oldest() {
120 let mut bars = ascending_bars(10);
121 let start = Some(Timestamp::UNIX_EPOCH);
122 apply_count_limit(&mut bars, start, Some(3));
123 let ts: Vec<u64> = bars.iter().map(|b| b.ts_event.as_u64()).collect();
124 assert_eq!(ts, vec![1, 2, 3]);
125 }
126
127 #[rstest]
128 fn test_apply_count_limit_fewer_bars_than_limit() {
129 let mut bars = ascending_bars(2);
130 apply_count_limit(&mut bars, None, Some(5));
131 assert_eq!(bars.len(), 2);
132 }
133}