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