Skip to main content

alpaca_data/options/
response.rs

1use std::collections::HashMap;
2
3use alpaca_core::{Error as CoreError, pagination::PaginatedResponse};
4use serde::{Deserialize, Serialize};
5
6use super::{Bar, 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}
14
15#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
16pub struct TradesResponse {
17    #[serde(default)]
18    pub trades: HashMap<String, Vec<Trade>>,
19    pub next_page_token: Option<String>,
20}
21
22#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
23pub struct LatestQuotesResponse {
24    #[serde(default)]
25    pub quotes: HashMap<String, Quote>,
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
29pub struct LatestTradesResponse {
30    #[serde(default)]
31    pub trades: HashMap<String, Trade>,
32}
33
34#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
35pub struct SnapshotsResponse {
36    #[serde(default)]
37    pub snapshots: HashMap<String, Snapshot>,
38    pub next_page_token: Option<String>,
39}
40
41#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
42pub struct ChainResponse {
43    #[serde(default)]
44    pub snapshots: HashMap<String, Snapshot>,
45    pub next_page_token: Option<String>,
46}
47
48pub type ConditionCodesResponse = HashMap<String, String>;
49pub type ExchangeCodesResponse = HashMap<String, String>;
50
51impl PaginatedResponse for BarsResponse {
52    fn next_page_token(&self) -> Option<&str> {
53        self.next_page_token.as_deref()
54    }
55
56    fn merge_page(&mut self, next: Self) -> Result<(), CoreError> {
57        merge_batch_page(&mut self.bars, next.bars);
58        self.next_page_token = next.next_page_token;
59        Ok(())
60    }
61
62    fn clear_next_page_token(&mut self) {
63        self.next_page_token = None;
64    }
65}
66
67impl PaginatedResponse for TradesResponse {
68    fn next_page_token(&self) -> Option<&str> {
69        self.next_page_token.as_deref()
70    }
71
72    fn merge_page(&mut self, next: Self) -> Result<(), CoreError> {
73        merge_batch_page(&mut self.trades, next.trades);
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 SnapshotsResponse {
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<(), CoreError> {
89        merge_snapshot_page("options.snapshots_all", &mut self.snapshots, next.snapshots)?;
90        self.next_page_token = next.next_page_token;
91        Ok(())
92    }
93
94    fn clear_next_page_token(&mut self) {
95        self.next_page_token = None;
96    }
97}
98
99impl PaginatedResponse for ChainResponse {
100    fn next_page_token(&self) -> Option<&str> {
101        self.next_page_token.as_deref()
102    }
103
104    fn merge_page(&mut self, next: Self) -> Result<(), CoreError> {
105        merge_snapshot_page("options.chain_all", &mut self.snapshots, next.snapshots)?;
106        self.next_page_token = next.next_page_token;
107        Ok(())
108    }
109
110    fn clear_next_page_token(&mut self) {
111        self.next_page_token = None;
112    }
113}
114
115fn merge_batch_page<Item>(
116    current: &mut HashMap<String, Vec<Item>>,
117    next: HashMap<String, Vec<Item>>,
118) {
119    for (symbol, mut items) in next {
120        current.entry(symbol).or_default().append(&mut items);
121    }
122}
123
124fn merge_snapshot_page(
125    operation: &str,
126    current: &mut HashMap<String, Snapshot>,
127    next: HashMap<String, Snapshot>,
128) -> Result<(), CoreError> {
129    for (symbol, snapshot) in next {
130        if current.insert(symbol.clone(), snapshot).is_some() {
131            return Err(CoreError::InvalidRequest(format!(
132                "{operation} received duplicate symbol across pages: {symbol}"
133            )));
134        }
135    }
136
137    Ok(())
138}