alpaca_data/options/
client.rs1use 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::merge_snapshot_page;
11use super::{
12 BarsRequest, BarsResponse, ChainRequest, ChainResponse, ConditionCodesRequest,
13 ConditionCodesResponse, ExchangeCodesResponse, LatestQuotesRequest, LatestQuotesResponse,
14 LatestTradesRequest, LatestTradesResponse, SnapshotsRequest, SnapshotsResponse, TradesRequest,
15 TradesResponse,
16};
17
18const MAX_SNAPSHOT_SYMBOLS_PER_REQUEST: usize = 100;
19
20#[derive(Clone)]
21pub struct OptionsClient {
22 inner: Arc<ClientInner>,
23}
24
25impl OptionsClient {
26 pub(crate) fn new(inner: Arc<ClientInner>) -> Self {
27 Self { inner }
28 }
29
30 pub async fn bars(&self, request: BarsRequest) -> Result<BarsResponse, Error> {
31 request.validate()?;
32 self.get_json(
33 "options.bars",
34 "/v1beta1/options/bars",
35 request.into_query(),
36 )
37 .await
38 }
39
40 pub async fn bars_all(&self, request: BarsRequest) -> Result<BarsResponse, Error> {
41 let client = self.clone();
42 pagination::collect_all(request, move |request| {
43 let client = client.clone();
44 async move { client.bars(request).await }
45 })
46 .await
47 }
48
49 pub async fn trades(&self, request: TradesRequest) -> Result<TradesResponse, Error> {
50 request.validate()?;
51 self.get_json(
52 "options.trades",
53 "/v1beta1/options/trades",
54 request.into_query(),
55 )
56 .await
57 }
58
59 pub async fn trades_all(&self, request: TradesRequest) -> Result<TradesResponse, Error> {
60 let client = self.clone();
61 pagination::collect_all(request, move |request| {
62 let client = client.clone();
63 async move { client.trades(request).await }
64 })
65 .await
66 }
67
68 pub async fn latest_quotes(
69 &self,
70 request: LatestQuotesRequest,
71 ) -> Result<LatestQuotesResponse, Error> {
72 request.validate()?;
73 self.get_json(
74 "options.latest_quotes",
75 "/v1beta1/options/quotes/latest",
76 request.into_query(),
77 )
78 .await
79 }
80
81 pub async fn latest_trades(
82 &self,
83 request: LatestTradesRequest,
84 ) -> Result<LatestTradesResponse, Error> {
85 request.validate()?;
86 self.get_json(
87 "options.latest_trades",
88 "/v1beta1/options/trades/latest",
89 request.into_query(),
90 )
91 .await
92 }
93
94 pub async fn snapshots(&self, request: SnapshotsRequest) -> Result<SnapshotsResponse, Error> {
95 request.validate()?;
96 self.get_json(
97 "options.snapshots",
98 "/v1beta1/options/snapshots",
99 request.into_query(),
100 )
101 .await
102 }
103
104 pub async fn snapshots_all(
105 &self,
106 request: SnapshotsRequest,
107 ) -> Result<SnapshotsResponse, Error> {
108 request.validate_all()?;
109
110 let mut combined = SnapshotsResponse::default();
111 for batch in request.batches(MAX_SNAPSHOT_SYMBOLS_PER_REQUEST) {
112 let client = self.clone();
113 let next = pagination::collect_all(batch, move |request| {
114 let client = client.clone();
115 async move { client.snapshots(request).await }
116 })
117 .await?;
118
119 merge_snapshot_page(
120 "options.snapshots_all",
121 &mut combined.snapshots,
122 next.snapshots,
123 )?;
124 }
125
126 Ok(combined)
127 }
128
129 pub async fn chain(&self, request: ChainRequest) -> Result<ChainResponse, Error> {
130 request.validate()?;
131 let path = format!("/v1beta1/options/snapshots/{}", request.path_symbol());
132 self.get_json("options.chain", path, request.into_query())
133 .await
134 }
135
136 pub async fn chain_all(&self, request: ChainRequest) -> Result<ChainResponse, Error> {
137 let client = self.clone();
138 pagination::collect_all(request, move |request| {
139 let client = client.clone();
140 async move { client.chain(request).await }
141 })
142 .await
143 }
144
145 pub async fn condition_codes(
146 &self,
147 request: ConditionCodesRequest,
148 ) -> Result<ConditionCodesResponse, Error> {
149 let path = format!(
150 "/v1beta1/options/meta/conditions/{}",
151 request.ticktype.as_str()
152 );
153 self.get_json("options.condition_codes", path, Vec::new())
154 .await
155 }
156
157 pub async fn exchange_codes(&self) -> Result<ExchangeCodesResponse, Error> {
158 self.get_json(
159 "options.exchange_codes",
160 "/v1beta1/options/meta/exchanges",
161 Vec::new(),
162 )
163 .await
164 }
165
166 #[allow(dead_code)]
167 #[must_use]
168 pub(crate) fn inner(&self) -> &Arc<ClientInner> {
169 &self.inner
170 }
171
172 async fn get_json<Response>(
173 &self,
174 operation: &'static str,
175 path: impl Into<String>,
176 query: Vec<(String, String)>,
177 ) -> Result<Response, Error>
178 where
179 Response: DeserializeOwned,
180 {
181 let request = RequestParts::new(Method::GET, path.into())
182 .with_operation(operation)
183 .with_query(query);
184
185 self.inner
186 .send_json::<Response>(request)
187 .await
188 .map(|response| response.into_body())
189 }
190}
191
192impl fmt::Debug for OptionsClient {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 f.debug_struct("OptionsClient").finish()
195 }
196}