Skip to main content

finance_query/domains/
snapshot.rs

1//! Cross-market snapshot handle.
2//!
3//! Created via [`Providers::snapshot`](crate::Providers::snapshot).
4
5use std::sync::Arc;
6
7use crate::error::Result;
8use crate::models::quote::snapshot::MarketSnapshot;
9use crate::providers::{Capability, Operation};
10
11domain_handle! {
12    /// Snapshots for a watchlist spanning several asset classes, from one request.
13    ///
14    /// Routes through [`Capability::QUOTE`]. Unlike
15    /// [`Tickers`](crate::Tickers) — which is equity-shaped and returns full quote
16    /// summaries — this takes provider-spelled symbols from any market (`"AAPL"`,
17    /// `"X:BTCUSD"`, `"I:SPX"`, `"C:EURUSD"`, `"O:NCLH221014C00005000"`) and
18    /// returns one flattened row each. Polygon is currently the only provider whose
19    /// snapshot endpoint spans markets.
20    ///
21    /// Created via [`Providers::snapshot`](crate::Providers::snapshot).
22    pub struct Snapshot
23    caches: { cache: Vec<MarketSnapshot> }
24}
25
26impl Snapshot {
27    /// Fetch snapshots for `symbols`, which may span asset classes.
28    ///
29    /// Cached per symbol list. Symbols the provider could not resolve come back
30    /// as rows with [`MarketSnapshot::error`] set rather than being dropped, so
31    /// the result can be aligned with the request.
32    pub async fn get<S: AsRef<str>>(&self, symbols: &[S]) -> Result<Vec<MarketSnapshot>> {
33        let owned: Vec<String> = symbols.iter().map(|s| s.as_ref().to_string()).collect();
34        let key = owned.join("\u{1f}");
35        let providers = Arc::clone(&self.providers);
36
37        self.cache
38            .get_or_try(key, move || async move {
39                providers
40                    .fetch(Capability::QUOTE, move |p| {
41                        let owned = owned.clone();
42                        let p = p.clone();
43                        async move {
44                            let refs: Vec<&str> = owned.iter().map(String::as_str).collect();
45                            p.as_quote()
46                                .ok_or_else(|| p.not_supported(Operation::UnifiedSnapshot))?
47                                .fetch_unified_snapshot(&refs)
48                                .await
49                        }
50                    })
51                    .await
52            })
53            .await
54    }
55}