1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use crate::error::{Error, Result};
use crate::http::HttpClient;
use crate::types::{CursorResponse, Timestamp, Trade};
/// Parameters for paginated trade history.
#[derive(Debug)]
pub struct GetTradesParams {
pub start: Timestamp,
pub end: Timestamp,
pub cursor: Option<String>,
pub limit: Option<i64>,
/// Filter by taker side: `"A"` (sell) or `"B"` (buy).
pub side: Option<String>,
}
/// Access to trade/fill endpoints for a specific exchange.
#[derive(Debug, Clone)]
pub struct TradesResource {
http: HttpClient,
prefix: String,
}
impl TradesResource {
pub(crate) fn new(http: HttpClient, prefix: &str) -> Self {
Self {
http,
prefix: prefix.to_string(),
}
}
/// Get paginated historical trades.
pub async fn list(
&self,
symbol: &str,
params: GetTradesParams,
) -> Result<CursorResponse<Vec<Trade>>> {
let mut qp = vec![
("start", params.start.to_millis().to_string()),
("end", params.end.to_millis().to_string()),
];
if let Some(c) = ¶ms.cursor {
qp.push(("cursor", c.clone()));
}
if let Some(l) = params.limit {
qp.push(("limit", l.to_string()));
}
if let Some(s) = ¶ms.side {
qp.push(("side", s.clone()));
}
let (data, next_cursor) = self
.http
.get_with_cursor(&format!("{}/trades/{}", self.prefix, symbol), &qp)
.await?;
Ok(CursorResponse { data, next_cursor })
}
/// Get recent trades.
///
/// Only available on Lighter.xyz (`/v1/lighter`) and HIP-3
/// (`/v1/hyperliquid/hip3`). The Hyperliquid base namespace
/// (`/v1/hyperliquid`) does **not** expose a `/recent` endpoint;
/// calling `client.hyperliquid.trades.recent(...)` returns
/// [`Error::InvalidParam`] without a network round-trip. Use
/// [`TradesResource::list`] with a time range instead.
pub async fn recent(&self, symbol: &str, limit: Option<i64>) -> Result<Vec<Trade>> {
// Reject the Hyperliquid base prefix: backend only exposes /recent
// for HIP-3 (`/v1/hyperliquid/hip3`) and Lighter (`/v1/lighter`).
// Match exactly on `/v1/hyperliquid` to avoid catching the hip3
// nested prefix.
if self.prefix == "/v1/hyperliquid" {
return Err(Error::InvalidParam(
"trades.recent() is not available on Hyperliquid; use trades.list() with a time range".to_string(),
));
}
let mut qp = vec![];
if let Some(l) = limit {
qp.push(("limit", l.to_string()));
}
self.http
.get(&format!("{}/trades/{}/recent", self.prefix, symbol), &qp)
.await
}
}