1use std::fmt;
2use std::sync::Arc;
3
4use alpaca_http::RequestParts;
5use reqwest::Method;
6use serde::de::DeserializeOwned;
7
8use crate::{Error, client::ClientInner, pagination};
9
10use super::{
11 AuctionsRequest, AuctionsResponse, AuctionsSingleRequest, AuctionsSingleResponse, BarsRequest,
12 BarsResponse, BarsSingleRequest, BarsSingleResponse, ConditionCodesRequest,
13 ConditionCodesResponse, ExchangeCodesResponse, LatestBarRequest, LatestBarResponse,
14 LatestBarsRequest, LatestBarsResponse, LatestQuoteRequest, LatestQuoteResponse,
15 LatestQuotesRequest, LatestQuotesResponse, LatestTradeRequest, LatestTradeResponse,
16 LatestTradesRequest, LatestTradesResponse, QuotesRequest, QuotesResponse, QuotesSingleRequest,
17 QuotesSingleResponse, SnapshotRequest, SnapshotResponse, SnapshotsRequest, SnapshotsResponse,
18 TradesRequest, TradesResponse, TradesSingleRequest, TradesSingleResponse,
19};
20
21#[derive(Clone)]
22pub struct StocksClient {
23 inner: Arc<ClientInner>,
24}
25
26impl StocksClient {
27 pub(crate) fn new(inner: Arc<ClientInner>) -> Self {
28 Self { inner }
29 }
30
31 pub async fn bars(&self, request: BarsRequest) -> Result<BarsResponse, Error> {
32 request.validate()?;
33 self.get_json("stocks.bars", "/v2/stocks/bars", request.into_query())
34 .await
35 }
36
37 pub async fn bars_all(&self, request: BarsRequest) -> Result<BarsResponse, Error> {
38 let client = self.clone();
39 pagination::collect_all(request, move |request| {
40 let client = client.clone();
41 async move { client.bars(request).await }
42 })
43 .await
44 }
45
46 pub async fn bars_single(
47 &self,
48 request: BarsSingleRequest,
49 ) -> Result<BarsSingleResponse, Error> {
50 request.validate()?;
51 let path = format!("/v2/stocks/{}/bars", request.symbol);
52 self.get_json("stocks.bars_single", path, request.into_query())
53 .await
54 }
55
56 pub async fn bars_single_all(
57 &self,
58 request: BarsSingleRequest,
59 ) -> Result<BarsSingleResponse, Error> {
60 let client = self.clone();
61 pagination::collect_all(request, move |request| {
62 let client = client.clone();
63 async move { client.bars_single(request).await }
64 })
65 .await
66 }
67
68 pub async fn auctions(&self, request: AuctionsRequest) -> Result<AuctionsResponse, Error> {
69 request.validate()?;
70 self.get_json(
71 "stocks.auctions",
72 "/v2/stocks/auctions",
73 request.into_query(),
74 )
75 .await
76 }
77
78 pub async fn auctions_all(&self, request: AuctionsRequest) -> Result<AuctionsResponse, Error> {
79 let client = self.clone();
80 pagination::collect_all(request, move |request| {
81 let client = client.clone();
82 async move { client.auctions(request).await }
83 })
84 .await
85 }
86
87 pub async fn auctions_single(
88 &self,
89 request: AuctionsSingleRequest,
90 ) -> Result<AuctionsSingleResponse, Error> {
91 request.validate()?;
92 let path = format!("/v2/stocks/{}/auctions", request.symbol);
93 self.get_json("stocks.auctions_single", path, request.into_query())
94 .await
95 }
96
97 pub async fn auctions_single_all(
98 &self,
99 request: AuctionsSingleRequest,
100 ) -> Result<AuctionsSingleResponse, Error> {
101 let client = self.clone();
102 pagination::collect_all(request, move |request| {
103 let client = client.clone();
104 async move { client.auctions_single(request).await }
105 })
106 .await
107 }
108
109 pub async fn quotes(&self, request: QuotesRequest) -> Result<QuotesResponse, Error> {
110 request.validate()?;
111 self.get_json("stocks.quotes", "/v2/stocks/quotes", request.into_query())
112 .await
113 }
114
115 pub async fn quotes_all(&self, request: QuotesRequest) -> Result<QuotesResponse, Error> {
116 let client = self.clone();
117 pagination::collect_all(request, move |request| {
118 let client = client.clone();
119 async move { client.quotes(request).await }
120 })
121 .await
122 }
123
124 pub async fn quotes_single(
125 &self,
126 request: QuotesSingleRequest,
127 ) -> Result<QuotesSingleResponse, Error> {
128 request.validate()?;
129 let path = format!("/v2/stocks/{}/quotes", request.symbol);
130 self.get_json("stocks.quotes_single", path, request.into_query())
131 .await
132 }
133
134 pub async fn quotes_single_all(
135 &self,
136 request: QuotesSingleRequest,
137 ) -> Result<QuotesSingleResponse, Error> {
138 let client = self.clone();
139 pagination::collect_all(request, move |request| {
140 let client = client.clone();
141 async move { client.quotes_single(request).await }
142 })
143 .await
144 }
145
146 pub async fn trades(&self, request: TradesRequest) -> Result<TradesResponse, Error> {
147 request.validate()?;
148 self.get_json("stocks.trades", "/v2/stocks/trades", request.into_query())
149 .await
150 }
151
152 pub async fn trades_all(&self, request: TradesRequest) -> Result<TradesResponse, Error> {
153 let client = self.clone();
154 pagination::collect_all(request, move |request| {
155 let client = client.clone();
156 async move { client.trades(request).await }
157 })
158 .await
159 }
160
161 pub async fn trades_single(
162 &self,
163 request: TradesSingleRequest,
164 ) -> Result<TradesSingleResponse, Error> {
165 request.validate()?;
166 let path = format!("/v2/stocks/{}/trades", request.symbol);
167 self.get_json("stocks.trades_single", path, request.into_query())
168 .await
169 }
170
171 pub async fn trades_single_all(
172 &self,
173 request: TradesSingleRequest,
174 ) -> Result<TradesSingleResponse, Error> {
175 let client = self.clone();
176 pagination::collect_all(request, move |request| {
177 let client = client.clone();
178 async move { client.trades_single(request).await }
179 })
180 .await
181 }
182
183 pub async fn latest_bars(
184 &self,
185 request: LatestBarsRequest,
186 ) -> Result<LatestBarsResponse, Error> {
187 request.validate()?;
188 self.get_json(
189 "stocks.latest_bars",
190 "/v2/stocks/bars/latest",
191 request.into_query(),
192 )
193 .await
194 }
195
196 pub async fn latest_bar(&self, request: LatestBarRequest) -> Result<LatestBarResponse, Error> {
197 request.validate()?;
198 let path = format!("/v2/stocks/{}/bars/latest", request.symbol);
199 self.get_json("stocks.latest_bar", path, request.into_query())
200 .await
201 }
202
203 pub async fn latest_quotes(
204 &self,
205 request: LatestQuotesRequest,
206 ) -> Result<LatestQuotesResponse, Error> {
207 request.validate()?;
208 self.get_json(
209 "stocks.latest_quotes",
210 "/v2/stocks/quotes/latest",
211 request.into_query(),
212 )
213 .await
214 }
215
216 pub async fn latest_quote(
217 &self,
218 request: LatestQuoteRequest,
219 ) -> Result<LatestQuoteResponse, Error> {
220 request.validate()?;
221 let path = format!("/v2/stocks/{}/quotes/latest", request.symbol);
222 self.get_json("stocks.latest_quote", path, request.into_query())
223 .await
224 }
225
226 pub async fn latest_trades(
227 &self,
228 request: LatestTradesRequest,
229 ) -> Result<LatestTradesResponse, Error> {
230 request.validate()?;
231 self.get_json(
232 "stocks.latest_trades",
233 "/v2/stocks/trades/latest",
234 request.into_query(),
235 )
236 .await
237 }
238
239 pub async fn latest_trade(
240 &self,
241 request: LatestTradeRequest,
242 ) -> Result<LatestTradeResponse, Error> {
243 request.validate()?;
244 let path = format!("/v2/stocks/{}/trades/latest", request.symbol);
245 self.get_json("stocks.latest_trade", path, request.into_query())
246 .await
247 }
248
249 pub async fn snapshots(&self, request: SnapshotsRequest) -> Result<SnapshotsResponse, Error> {
250 request.validate()?;
251 self.get_json(
252 "stocks.snapshots",
253 "/v2/stocks/snapshots",
254 request.into_query(),
255 )
256 .await
257 }
258
259 pub async fn snapshot(&self, request: SnapshotRequest) -> Result<SnapshotResponse, Error> {
260 request.validate()?;
261 let path = format!("/v2/stocks/{}/snapshot", request.symbol);
262 self.get_json("stocks.snapshot", path, request.into_query())
263 .await
264 }
265
266 pub async fn condition_codes(
267 &self,
268 request: ConditionCodesRequest,
269 ) -> Result<ConditionCodesResponse, Error> {
270 let path = format!("/v2/stocks/meta/conditions/{}", request.ticktype.as_str());
271 self.get_json("stocks.condition_codes", path, request.into_query())
272 .await
273 }
274
275 pub async fn exchange_codes(&self) -> Result<ExchangeCodesResponse, Error> {
276 self.get_json(
277 "stocks.exchange_codes",
278 "/v2/stocks/meta/exchanges",
279 Vec::new(),
280 )
281 .await
282 }
283
284 #[allow(dead_code)]
285 #[must_use]
286 pub(crate) fn inner(&self) -> &Arc<ClientInner> {
287 &self.inner
288 }
289
290 async fn get_json<Response>(
291 &self,
292 operation: &'static str,
293 path: impl Into<String>,
294 query: Vec<(String, String)>,
295 ) -> Result<Response, Error>
296 where
297 Response: DeserializeOwned,
298 {
299 let request = RequestParts::new(Method::GET, path.into())
300 .with_operation(operation)
301 .with_query(query);
302
303 self.inner
304 .send_json::<Response>(request)
305 .await
306 .map(|response| response.into_body())
307 }
308}
309
310impl fmt::Debug for StocksClient {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.debug_struct("StocksClient")
313 .field("base_url", self.inner.base_url())
314 .finish()
315 }
316}