Skip to main content

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
30pub(crate) mod coverage;
31
32/// Applies a count `limit` to `items` returned oldest-first by Kraken.
33///
34/// Kraken's historical endpoints return their page in ascending (oldest-first)
35/// order. When the caller anchors the window with `start`, the oldest `limit`
36/// items from that anchor are the intended result, so the head of the page is
37/// kept. Otherwise a count-only request (`start` is `None`) means "the most
38/// recent `limit` items", so the tail is kept rather than the head.
39pub(crate) fn apply_count_limit<T>(
40    items: &mut Vec<T>,
41    start: Option<Timestamp>,
42    limit: Option<u64>,
43) {
44    if let Some(limit) = limit {
45        let limit = limit as usize;
46        if items.len() > limit {
47            if start.is_some() {
48                items.truncate(limit);
49            } else {
50                items.drain(..items.len() - limit);
51            }
52        }
53    }
54}
55
56// Re-exports
57pub use error::KrakenHttpError;
58pub use futures::{
59    client::{
60        KRAKEN_FUTURES_DEFAULT_RATE_LIMIT_PER_SECOND, KrakenFuturesHttpClient,
61        KrakenFuturesRawHttpClient,
62    },
63    query::*,
64};
65pub use spot::{
66    client::{
67        KRAKEN_SPOT_DEFAULT_RATE_LIMIT_PER_SECOND, KrakenSpotHttpClient, KrakenSpotRawHttpClient,
68    },
69    query::*,
70};
71
72#[cfg(test)]
73mod tests {
74    use nautilus_core::UnixNanos;
75    use nautilus_model::{
76        data::{Bar, BarType},
77        types::{Price, Quantity},
78    };
79    use rstest::rstest;
80
81    use super::*;
82
83    /// Builds `n` bars in ascending (oldest-first) order, as Kraken returns them,
84    /// with `ts_event` set to the 1-based index so ordering is easy to assert.
85    fn ascending_bars(n: u64) -> Vec<Bar> {
86        let bar_type = BarType::from("ETH/USD.KRAKEN-1-MINUTE-LAST-EXTERNAL");
87        let price = Price::from("100.0");
88        let volume = Quantity::from("1");
89        (1..=n)
90            .map(|i| {
91                Bar::new(
92                    bar_type,
93                    price,
94                    price,
95                    price,
96                    price,
97                    volume,
98                    UnixNanos::from(i),
99                    UnixNanos::default(),
100                )
101            })
102            .collect()
103    }
104
105    #[rstest]
106    fn test_apply_count_limit_no_limit_keeps_all() {
107        let mut bars = ascending_bars(5);
108        apply_count_limit(&mut bars, None, None);
109        assert_eq!(bars.len(), 5);
110    }
111
112    #[rstest]
113    fn test_apply_count_limit_count_only_keeps_most_recent() {
114        let mut bars = ascending_bars(10);
115        apply_count_limit(&mut bars, None, Some(3));
116        let ts: Vec<u64> = bars.iter().map(|b| b.ts_event.as_u64()).collect();
117        assert_eq!(ts, vec![8, 9, 10]);
118    }
119
120    #[rstest]
121    fn test_apply_count_limit_with_start_keeps_oldest() {
122        let mut bars = ascending_bars(10);
123        let start = Some(Timestamp::UNIX_EPOCH);
124        apply_count_limit(&mut bars, start, Some(3));
125        let ts: Vec<u64> = bars.iter().map(|b| b.ts_event.as_u64()).collect();
126        assert_eq!(ts, vec![1, 2, 3]);
127    }
128
129    #[rstest]
130    fn test_apply_count_limit_fewer_bars_than_limit() {
131        let mut bars = ascending_bars(2);
132        apply_count_limit(&mut bars, None, Some(5));
133        assert_eq!(bars.len(), 2);
134    }
135}