Skip to main content

alpaca_data/stocks/
response.rs

1use std::collections::HashMap;
2
3use alpaca_core::{Error, pagination::PaginatedResponse};
4use serde::{Deserialize, Serialize};
5
6use super::{Bar, Currency, DailyAuction, Quote, Snapshot, Trade};
7
8#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
9pub struct BarsResponse {
10    #[serde(default)]
11    pub bars: HashMap<String, Vec<Bar>>,
12    pub next_page_token: Option<String>,
13    pub currency: Option<Currency>,
14}
15
16#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
17pub struct AuctionsResponse {
18    #[serde(default)]
19    pub auctions: HashMap<String, Vec<DailyAuction>>,
20    pub next_page_token: Option<String>,
21    pub currency: Option<Currency>,
22}
23
24#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
25pub struct QuotesResponse {
26    #[serde(default)]
27    pub quotes: HashMap<String, Vec<Quote>>,
28    pub next_page_token: Option<String>,
29    pub currency: Option<Currency>,
30}
31
32#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
33pub struct TradesResponse {
34    #[serde(default)]
35    pub trades: HashMap<String, Vec<Trade>>,
36    pub next_page_token: Option<String>,
37    pub currency: Option<Currency>,
38}
39
40#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
41pub struct LatestBarsResponse {
42    #[serde(default)]
43    pub bars: HashMap<String, Bar>,
44    pub currency: Option<Currency>,
45}
46
47#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
48pub struct LatestQuotesResponse {
49    #[serde(default)]
50    pub quotes: HashMap<String, Quote>,
51    pub currency: Option<Currency>,
52}
53
54#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
55pub struct LatestTradesResponse {
56    #[serde(default)]
57    pub trades: HashMap<String, Trade>,
58    pub currency: Option<Currency>,
59}
60
61pub type SnapshotsResponse = HashMap<String, Snapshot>;
62
63pub type ConditionCodesResponse = HashMap<String, String>;
64pub type ExchangeCodesResponse = HashMap<String, String>;
65
66impl PaginatedResponse for BarsResponse {
67    fn next_page_token(&self) -> Option<&str> {
68        self.next_page_token.as_deref()
69    }
70
71    fn merge_page(&mut self, next: Self) -> Result<(), Error> {
72        merge_batch_currency("stocks.bars_all", &mut self.currency, next.currency)?;
73        merge_batch_page(&mut self.bars, next.bars);
74        self.next_page_token = next.next_page_token;
75        Ok(())
76    }
77
78    fn clear_next_page_token(&mut self) {
79        self.next_page_token = None;
80    }
81}
82
83impl PaginatedResponse for AuctionsResponse {
84    fn next_page_token(&self) -> Option<&str> {
85        self.next_page_token.as_deref()
86    }
87
88    fn merge_page(&mut self, next: Self) -> Result<(), Error> {
89        merge_batch_currency("stocks.auctions_all", &mut self.currency, next.currency)?;
90        merge_batch_page(&mut self.auctions, next.auctions);
91        self.next_page_token = next.next_page_token;
92        Ok(())
93    }
94
95    fn clear_next_page_token(&mut self) {
96        self.next_page_token = None;
97    }
98}
99
100impl PaginatedResponse for QuotesResponse {
101    fn next_page_token(&self) -> Option<&str> {
102        self.next_page_token.as_deref()
103    }
104
105    fn merge_page(&mut self, next: Self) -> Result<(), Error> {
106        merge_batch_currency("stocks.quotes_all", &mut self.currency, next.currency)?;
107        merge_batch_page(&mut self.quotes, next.quotes);
108        self.next_page_token = next.next_page_token;
109        Ok(())
110    }
111
112    fn clear_next_page_token(&mut self) {
113        self.next_page_token = None;
114    }
115}
116
117impl PaginatedResponse for TradesResponse {
118    fn next_page_token(&self) -> Option<&str> {
119        self.next_page_token.as_deref()
120    }
121
122    fn merge_page(&mut self, next: Self) -> Result<(), Error> {
123        merge_batch_currency("stocks.trades_all", &mut self.currency, next.currency)?;
124        merge_batch_page(&mut self.trades, next.trades);
125        self.next_page_token = next.next_page_token;
126        Ok(())
127    }
128
129    fn clear_next_page_token(&mut self) {
130        self.next_page_token = None;
131    }
132}
133
134fn merge_batch_currency(
135    operation: &str,
136    currency: &mut Option<Currency>,
137    next_currency: Option<Currency>,
138) -> Result<(), Error> {
139    match (currency.as_ref(), next_currency) {
140        (Some(current), Some(next)) if current != &next => Err(Error::InvalidRequest(format!(
141            "{operation} received mismatched currency across pages: expected {}, got {}",
142            current.as_str(),
143            next.as_str()
144        ))),
145        (None, Some(next)) => {
146            *currency = Some(next);
147            Ok(())
148        }
149        _ => Ok(()),
150    }
151}
152
153fn merge_batch_page<Item>(
154    current: &mut HashMap<String, Vec<Item>>,
155    next: HashMap<String, Vec<Item>>,
156) {
157    for (symbol, mut items) in next {
158        current.entry(symbol).or_default().append(&mut items);
159    }
160}