polyoxide_binance/spot/api/market.rs
1//! Market data: depth, trades, aggregate trades, klines, the average price and
2//! the three tickers.
3
4use polyoxide_core::HttpClient;
5
6use crate::spot::{
7 error::SpotError,
8 request::{route_builder, Routed},
9 types::{
10 AggTrade, AvgPrice, BookTicker, Depth, Interval, Kline, PriceTicker, Symbol, Ticker24h,
11 Trade,
12 },
13 weight::{Cost, Route},
14};
15
16/// Market namespace.
17#[derive(Debug, Clone)]
18pub struct MarketApi {
19 pub(crate) http: HttpClient,
20}
21
22impl MarketApi {
23 fn request<T>(&self, route: Route) -> Routed<T> {
24 Routed::new(&self.http, route)
25 }
26
27 /// `GET /api/v3/depth`: an order book snapshot. Without a limit it returns
28 /// 100 levels for weight 5; see [`GetDepth::limit`] for the rest.
29 pub fn depth(&self, symbol: &Symbol) -> GetDepth {
30 GetDepth {
31 request: self
32 .request(Route::Depth { limit: None })
33 .query("symbol", symbol),
34 }
35 }
36
37 /// `GET /api/v3/trades`: recent trades, oldest first (weight 25 at any
38 /// limit).
39 pub fn trades(&self, symbol: &Symbol) -> GetTrades {
40 GetTrades {
41 request: self.request(Route::Trades).query("symbol", symbol),
42 }
43 }
44
45 /// `GET /api/v3/aggTrades`: aggregate trades, oldest first (weight 4 at
46 /// any limit).
47 pub fn agg_trades(&self, symbol: &Symbol) -> GetAggTrades {
48 GetAggTrades {
49 request: self.request(Route::AggTrades).query("symbol", symbol),
50 }
51 }
52
53 /// `GET /api/v3/klines`: candles, oldest first (weight 2 at any limit).
54 pub fn klines(&self, symbol: &Symbol, interval: Interval) -> GetKlines {
55 GetKlines {
56 request: self
57 .request(Route::Klines)
58 .query("symbol", symbol)
59 .query("interval", interval),
60 }
61 }
62
63 /// `GET /api/v3/avgPrice`: the current average price (weight 2).
64 pub fn avg_price(&self, symbol: &Symbol) -> GetAvgPrice {
65 GetAvgPrice {
66 request: self.request(Route::AvgPrice).query("symbol", symbol),
67 }
68 }
69
70 /// `GET /api/v3/ticker/24hr` for one symbol (weight 2).
71 pub fn ticker_24h(&self, symbol: &Symbol) -> GetTicker24h {
72 GetTicker24h {
73 request: self
74 .request(Route::Ticker24h { all: false })
75 .query("symbol", symbol),
76 }
77 }
78
79 /// `GET /api/v3/ticker/24hr` for every symbol (weight 80).
80 pub fn tickers_24h(&self) -> GetTickers24h {
81 GetTickers24h {
82 request: self.request(Route::Ticker24h { all: true }),
83 }
84 }
85
86 /// `GET /api/v3/ticker/price` for one symbol (weight 2).
87 pub fn price(&self, symbol: &Symbol) -> GetPrice {
88 GetPrice {
89 request: self
90 .request(Route::Price { all: false })
91 .query("symbol", symbol),
92 }
93 }
94
95 /// `GET /api/v3/ticker/price` for every symbol (weight 4).
96 pub fn prices(&self) -> GetPrices {
97 GetPrices {
98 request: self.request(Route::Price { all: true }),
99 }
100 }
101
102 /// `GET /api/v3/ticker/bookTicker` for one symbol (weight 2).
103 pub fn book_ticker(&self, symbol: &Symbol) -> GetBookTicker {
104 GetBookTicker {
105 request: self
106 .request(Route::BookTicker { all: false })
107 .query("symbol", symbol),
108 }
109 }
110
111 /// `GET /api/v3/ticker/bookTicker` for every symbol (weight 4).
112 pub fn book_tickers(&self) -> GetBookTickers {
113 GetBookTickers {
114 request: self.request(Route::BookTicker { all: true }),
115 }
116 }
117}
118
119route_builder! {
120 /// Request builder for `GET /api/v3/avgPrice`.
121 GetAvgPrice => AvgPrice
122}
123
124route_builder! {
125 /// Request builder for `GET /api/v3/ticker/24hr` for one symbol.
126 GetTicker24h => Ticker24h
127}
128
129route_builder! {
130 /// Request builder for `GET /api/v3/ticker/24hr` for every symbol.
131 GetTickers24h => Vec<Ticker24h>
132}
133
134route_builder! {
135 /// Request builder for `GET /api/v3/ticker/price` for one symbol.
136 GetPrice => PriceTicker
137}
138
139route_builder! {
140 /// Request builder for `GET /api/v3/ticker/price` for every symbol.
141 GetPrices => Vec<PriceTicker>
142}
143
144route_builder! {
145 /// Request builder for `GET /api/v3/ticker/bookTicker` for one symbol.
146 GetBookTicker => BookTicker
147}
148
149route_builder! {
150 /// Request builder for `GET /api/v3/ticker/bookTicker` for every symbol.
151 GetBookTickers => Vec<BookTicker>
152}
153
154/// Request builder for `GET /api/v3/depth`.
155#[must_use = "a request does nothing until it is sent"]
156pub struct GetDepth {
157 request: Routed<Depth>,
158}
159
160impl GetDepth {
161 /// Levels per side, sent as given. Any value is accepted, and a limit past
162 /// 5000 returns 5000 levels. Sets the weight: up to 100 costs 5, up to 500
163 /// costs 25, up to 1000 costs 50, more costs 250.
164 pub fn limit(mut self, limit: u32) -> Self {
165 self.request = self
166 .request
167 .route(Route::Depth { limit: Some(limit) })
168 .query("limit", limit);
169 self
170 }
171
172 /// What sending this request will cost.
173 pub fn cost(&self) -> Cost {
174 self.request.cost()
175 }
176
177 /// Waits for the budget, sends, and decodes the response.
178 ///
179 /// A `429` holds every request on the budget, then this request is
180 /// retried on the client's schedule; with no retry left and no
181 /// `Retry-After`, the hold runs to the next UTC minute. A `418` holds
182 /// every request for its `Retry-After`, or 2 minutes, and is not retried.
183 ///
184 /// The wait has no deadline of its own: a request can wait out a minute's
185 /// spent budget or a ban, which Binance documents at up to 3 days. Wrap
186 /// the call in `tokio::time::timeout` where a caller needs a deadline.
187 pub async fn send(self) -> Result<Depth, SpotError> {
188 self.request.send().await
189 }
190}
191
192/// Request builder for `GET /api/v3/trades`.
193#[must_use = "a request does nothing until it is sent"]
194pub struct GetTrades {
195 request: Routed<Vec<Trade>>,
196}
197
198impl GetTrades {
199 polyoxide_core::query_setters! {
200 /// Rows to return, 500 by default and up to 1000; the venue truncates
201 /// past 1000. The weight is 25 whatever the limit.
202 limit: u32 => "limit",
203 }
204
205 /// What sending this request will cost.
206 pub fn cost(&self) -> Cost {
207 self.request.cost()
208 }
209
210 /// Waits for the budget, sends, and decodes the response.
211 ///
212 /// A `429` holds every request on the budget, then this request is
213 /// retried on the client's schedule; with no retry left and no
214 /// `Retry-After`, the hold runs to the next UTC minute. A `418` holds
215 /// every request for its `Retry-After`, or 2 minutes, and is not retried.
216 ///
217 /// The wait has no deadline of its own: a request can wait out a minute's
218 /// spent budget or a ban, which Binance documents at up to 3 days. Wrap
219 /// the call in `tokio::time::timeout` where a caller needs a deadline.
220 pub async fn send(self) -> Result<Vec<Trade>, SpotError> {
221 self.request.send().await
222 }
223}
224
225/// Request builder for `GET /api/v3/aggTrades`.
226#[must_use = "a request does nothing until it is sent"]
227pub struct GetAggTrades {
228 request: Routed<Vec<AggTrade>>,
229}
230
231impl GetAggTrades {
232 polyoxide_core::query_setters! {
233 /// Start from this aggregate trade id, inclusive.
234 from_id: u64 => "fromId",
235 /// Earliest trade time, Unix milliseconds.
236 start_time: u64 => "startTime",
237 /// Latest trade time, Unix milliseconds.
238 end_time: u64 => "endTime",
239 /// Rows to return, 500 by default and up to 1000; the venue truncates
240 /// past 1000. The weight is 4 whatever the limit.
241 limit: u32 => "limit",
242 }
243
244 /// What sending this request will cost.
245 pub fn cost(&self) -> Cost {
246 self.request.cost()
247 }
248
249 /// Waits for the budget, sends, and decodes the response.
250 ///
251 /// A `429` holds every request on the budget, then this request is
252 /// retried on the client's schedule; with no retry left and no
253 /// `Retry-After`, the hold runs to the next UTC minute. A `418` holds
254 /// every request for its `Retry-After`, or 2 minutes, and is not retried.
255 ///
256 /// The wait has no deadline of its own: a request can wait out a minute's
257 /// spent budget or a ban, which Binance documents at up to 3 days. Wrap
258 /// the call in `tokio::time::timeout` where a caller needs a deadline.
259 pub async fn send(self) -> Result<Vec<AggTrade>, SpotError> {
260 self.request.send().await
261 }
262}
263
264/// Request builder for `GET /api/v3/klines`.
265#[must_use = "a request does nothing until it is sent"]
266pub struct GetKlines {
267 request: Routed<Vec<Kline>>,
268}
269
270impl GetKlines {
271 polyoxide_core::query_setters! {
272 /// Earliest candle start, Unix milliseconds.
273 start_time: u64 => "startTime",
274 /// Latest candle start, Unix milliseconds.
275 end_time: u64 => "endTime",
276 /// Candles to return, 500 by default and up to 1000; the venue
277 /// truncates past 1000. The weight is 2 whatever the limit.
278 limit: u32 => "limit",
279 }
280
281 /// What sending this request will cost.
282 pub fn cost(&self) -> Cost {
283 self.request.cost()
284 }
285
286 /// Waits for the budget, sends, and decodes the response.
287 ///
288 /// A `429` holds every request on the budget, then this request is
289 /// retried on the client's schedule; with no retry left and no
290 /// `Retry-After`, the hold runs to the next UTC minute. A `418` holds
291 /// every request for its `Retry-After`, or 2 minutes, and is not retried.
292 ///
293 /// The wait has no deadline of its own: a request can wait out a minute's
294 /// spent budget or a ban, which Binance documents at up to 3 days. Wrap
295 /// the call in `tokio::time::timeout` where a caller needs a deadline.
296 pub async fn send(self) -> Result<Vec<Kline>, SpotError> {
297 self.request.send().await
298 }
299}