Skip to main content

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