Skip to main content

alpaca_data/stocks/
client.rs

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}