Skip to main content

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}