alpaca_data/cache/
state.rs1use std::collections::{HashMap, HashSet};
2use std::time::SystemTime;
3
4use crate::options;
5use crate::stocks::{self, Adjustment, BarPoint, Currency, DataFeed, TimeFrame};
6use crate::symbols::option_contract_symbol;
7
8pub(crate) type BarsMap = HashMap<String, Vec<BarPoint>>;
9
10#[derive(Debug, Clone)]
11pub struct StockBarsRequest {
12 pub key: String,
13 pub symbols: Vec<String>,
14 pub timeframe: TimeFrame,
15 pub start: Option<String>,
16 pub end: Option<String>,
17 pub limit: u32,
18 pub adjustment: Option<Adjustment>,
19 pub feed: Option<DataFeed>,
20 pub currency: Option<Currency>,
21 pub chunk_size: usize,
22}
23
24impl StockBarsRequest {
25 pub(crate) fn normalized(mut self) -> Self {
26 self.symbols = normalize_stock_list(&self.symbols);
27 self
28 }
29
30 pub(crate) fn merge_from(&mut self, next: &Self) {
31 self.symbols = merge_values(&self.symbols, &next.symbols);
32 self.timeframe = next.timeframe.clone();
33 self.start = next.start.clone();
34 self.end = next.end.clone();
35 self.limit = next.limit;
36 self.adjustment = next.adjustment.clone();
37 self.feed = next.feed;
38 self.currency = next.currency.clone();
39 self.chunk_size = next.chunk_size;
40 }
41}
42
43#[derive(Debug, Default)]
44pub(crate) struct SnapshotCache<T> {
45 pub subscribed: HashSet<String>,
46 pub values: HashMap<String, T>,
47 pub empty: HashSet<String>,
48 pub updated_at: Option<SystemTime>,
49}
50
51#[derive(Debug, Default)]
52pub(crate) struct StockBarsCache {
53 pub requests: HashMap<String, StockBarsRequest>,
54 pub values: HashMap<String, BarsMap>,
55 pub empty: HashMap<String, HashSet<String>>,
56 pub updated_at: HashMap<String, SystemTime>,
57}
58
59#[derive(Debug, Default)]
60pub(crate) struct CacheState {
61 pub stocks: SnapshotCache<stocks::Snapshot>,
62 pub options: SnapshotCache<options::Snapshot>,
63 pub bars: StockBarsCache,
64}
65
66pub(crate) fn normalize_values<S: AsRef<str>>(values: &[S]) -> Vec<String> {
67 let mut normalized = Vec::new();
68 let mut seen = HashSet::new();
69 for value in values {
70 let value = value.as_ref().trim();
71 if !value.is_empty() && seen.insert(value.to_string()) {
72 normalized.push(value.to_string());
73 }
74 }
75 normalized
76}
77
78pub(crate) fn normalize_stock_symbols<S: AsRef<str>>(symbols: &[S]) -> Vec<(String, String)> {
79 normalize_values(symbols)
80 .into_iter()
81 .map(|symbol| {
82 let resolved = stocks::display_stock_symbol(&symbol);
83 (symbol, resolved)
84 })
85 .collect()
86}
87
88pub(crate) fn normalize_option_symbols<S: AsRef<str>>(symbols: &[S]) -> Vec<String> {
89 let normalized = normalize_values(symbols);
90 let mut values = Vec::new();
91 let mut seen = HashSet::new();
92 for symbol in normalized {
93 let symbol = option_contract_symbol(&symbol);
94 if !symbol.is_empty() && seen.insert(symbol.clone()) {
95 values.push(symbol);
96 }
97 }
98 values
99}
100
101pub(crate) fn collect_cached_hits<T: Clone>(
102 requested: &[String],
103 cached: &HashMap<String, T>,
104 empty: &HashSet<String>,
105) -> (HashMap<String, T>, Vec<String>) {
106 let mut hits = HashMap::new();
107 let mut missing = Vec::new();
108 for key in requested {
109 if let Some(value) = cached.get(key) {
110 hits.insert(key.clone(), value.clone());
111 } else if !empty.contains(key) {
112 missing.push(key.clone());
113 }
114 }
115 (hits, missing)
116}
117
118fn normalize_stock_list<S: AsRef<str>>(symbols: &[S]) -> Vec<String> {
119 normalize_stock_symbols(symbols)
120 .into_iter()
121 .map(|(_, resolved)| resolved)
122 .collect()
123}
124
125fn merge_values(current: &[String], next: &[String]) -> Vec<String> {
126 let mut merged = Vec::new();
127 let mut seen = HashSet::new();
128 for value in current.iter().chain(next.iter()) {
129 if seen.insert(value.clone()) {
130 merged.push(value.clone());
131 }
132 }
133 merged
134}