Skip to main content

finance_query/domains/
market.rs

1//! Market-wide calendar and performance handles.
2//!
3//! Created via [`Providers::calendar`](crate::Providers::calendar) and
4//! [`Providers::market`](crate::Providers::market).
5
6use std::sync::Arc;
7
8use crate::error::Result;
9use crate::models::calendar::market::{CalendarKind, MarketCalendarEntry};
10use crate::models::chart::Candle;
11use crate::models::market::performance::{
12    IndustryPe, MoverDirection, MoverQuote, SectorPe, SectorPerformance,
13};
14use crate::providers::Capability;
15use crate::providers::ProviderSet;
16
17domain_handle! {
18    /// Market-wide event calendars backed by configured data providers.
19    ///
20    /// Routes through [`Capability::CALENDAR`]. Unlike
21    /// [`Ticker::calendar`](crate::Ticker::calendar), which builds a per-symbol
22    /// timeline, these span the whole market over a date range.
23    ///
24    /// Created via [`Providers::calendar`](crate::Providers::calendar).
25    pub struct MarketCalendar
26    caches: { cache: Vec<MarketCalendarEntry> }
27}
28
29impl MarketCalendar {
30    /// Fetch a calendar of `kind` over `[from, to]` (`YYYY-MM-DD` dates).
31    ///
32    /// Cached per `(kind, from, to)`.
33    pub async fn fetch(
34        &self,
35        kind: CalendarKind,
36        from: &str,
37        to: &str,
38    ) -> Result<Vec<MarketCalendarEntry>> {
39        let key = format!("{kind:?}\u{1f}{from}\u{1f}{to}");
40        let providers = Arc::clone(&self.providers);
41        let (from, to) = (from.to_string(), to.to_string());
42        self.cache
43            .get_or_try(key, move || async move {
44                providers
45                    .fetch(Capability::CALENDAR, move |p| {
46                        let (from, to) = (from.clone(), to.clone());
47                        let p = p.clone();
48                        async move {
49                            p.as_calendar()
50                                .ok_or_else(|| p.not_supported(kind.operation()))?
51                                .fetch_market_calendar(kind, &from, &to)
52                                .await
53                        }
54                    })
55                    .await
56            })
57            .await
58    }
59
60    /// Earnings releases scheduled or reported over `[from, to]`.
61    pub async fn earnings(&self, from: &str, to: &str) -> Result<Vec<MarketCalendarEntry>> {
62        self.fetch(CalendarKind::Earnings, from, to).await
63    }
64
65    /// Initial public offerings over `[from, to]`.
66    pub async fn ipos(&self, from: &str, to: &str) -> Result<Vec<MarketCalendarEntry>> {
67        self.fetch(CalendarKind::Ipo, from, to).await
68    }
69
70    /// Dividend payments over `[from, to]`.
71    pub async fn dividends(&self, from: &str, to: &str) -> Result<Vec<MarketCalendarEntry>> {
72        self.fetch(CalendarKind::Dividend, from, to).await
73    }
74
75    /// Stock splits over `[from, to]`.
76    pub async fn splits(&self, from: &str, to: &str) -> Result<Vec<MarketCalendarEntry>> {
77        self.fetch(CalendarKind::Split, from, to).await
78    }
79
80    /// Macro-economic releases over `[from, to]`.
81    pub async fn economic(&self, from: &str, to: &str) -> Result<Vec<MarketCalendarEntry>> {
82        self.fetch(CalendarKind::Economic, from, to).await
83    }
84
85    /// Upcoming market holidays and early closes. Providers return their
86    /// upcoming set, so no date range is taken.
87    pub async fn holidays(&self) -> Result<Vec<MarketCalendarEntry>> {
88        self.fetch(CalendarKind::MarketHoliday, "", "").await
89    }
90
91    /// Live open/closed status per exchange. A snapshot rather than a dated
92    /// event, so no date range is taken. Currently Alpha Vantage only.
93    pub async fn market_status(&self) -> Result<Vec<MarketCalendarEntry>> {
94        self.fetch(CalendarKind::MarketStatus, "", "").await
95    }
96}
97
98/// Market-wide performance statistics backed by configured data providers.
99///
100/// Routes through [`crate::Capability::MARKET`]. Unlike [`crate::finance::sector`] and
101/// [`crate::finance::market_summary`] — Yahoo-only convenience shortcuts — these
102/// honour the configured provider priority.
103///
104/// Created via [`Providers::market`](crate::Providers::market).
105pub struct Market {
106    providers: Arc<ProviderSet>,
107}
108
109impl Market {
110    pub(crate) fn with_providers(providers: Arc<ProviderSet>) -> Self {
111        Self { providers }
112    }
113
114    /// Aggregate performance for every sector.
115    pub async fn sector_performance(&self) -> Result<Vec<SectorPerformance>> {
116        dispatch_via!(
117            self,
118            MARKET,
119            as_market,
120            SectorPerformance,
121            fetch_sector_performance,
122            []
123        )
124    }
125
126    /// Historical aggregate sector performance, most recent first.
127    pub async fn sector_performance_history(
128        &self,
129        limit: u32,
130    ) -> Result<Vec<crate::models::market::performance::SectorPerformanceHistory>> {
131        dispatch_via!(
132            self,
133            MARKET,
134            as_market,
135            SectorPerformanceHistory,
136            fetch_sector_performance_history,
137            [],
138            limit
139        )
140    }
141
142    /// Price/earnings ratios by sector.
143    pub async fn sector_pe(&self) -> Result<Vec<SectorPe>> {
144        dispatch_via!(
145            self,
146            MARKET,
147            as_market,
148            SectorPerformance,
149            fetch_sector_pe,
150            []
151        )
152    }
153
154    /// Price/earnings ratios by industry.
155    pub async fn industry_pe(&self) -> Result<Vec<IndustryPe>> {
156        dispatch_via!(
157            self,
158            MARKET,
159            as_market,
160            SectorPerformance,
161            fetch_industry_pe,
162            []
163        )
164    }
165
166    /// Market movers for `direction`.
167    pub async fn movers(&self, direction: MoverDirection) -> Result<Vec<MoverQuote>> {
168        dispatch_via!(
169            self,
170            MARKET,
171            as_market,
172            MarketMovers,
173            fetch_market_movers,
174            [],
175            direction
176        )
177    }
178
179    /// Largest percentage gainers.
180    pub async fn gainers(&self) -> Result<Vec<MoverQuote>> {
181        self.movers(MoverDirection::Gainers).await
182    }
183
184    /// Largest percentage losers.
185    pub async fn losers(&self) -> Result<Vec<MoverQuote>> {
186        self.movers(MoverDirection::Losers).await
187    }
188
189    /// Highest traded volume.
190    pub async fn most_active(&self) -> Result<Vec<MoverQuote>> {
191        self.movers(MoverDirection::MostActive).await
192    }
193
194    /// Fetch grouped daily OHLCV bars for every stock ticker on `date`
195    /// (`YYYY-MM-DD`) in one call — "give me every ticker's OHLC for this
196    /// date" rather than one symbol at a time.
197    ///
198    /// Routes through [`Capability::CHART`](crate::providers::Capability::CHART)
199    /// (the same capability backing per-symbol chart methods) rather than
200    /// `MARKET`, since it's OHLCV data rather than a performance statistic.
201    /// Currently Polygon only. Not cached — one date is one request either way.
202    pub async fn grouped_daily(&self, date: &str) -> Result<Vec<(String, Candle)>> {
203        let date = date.to_string();
204        dispatch_via!(
205            self,
206            CHART,
207            as_chart,
208            GroupedDaily,
209            fetch_grouped_daily,
210            [date],
211            &date
212        )
213    }
214
215    /// Fetch grouped daily OHLCV bars for every crypto ticker on `date`
216    /// (`YYYY-MM-DD`) in one call. See [`grouped_daily`](Self::grouped_daily).
217    pub async fn crypto_grouped_daily(&self, date: &str) -> Result<Vec<(String, Candle)>> {
218        let date = date.to_string();
219        dispatch_via!(
220            self,
221            CHART,
222            as_chart,
223            CryptoGroupedDaily,
224            fetch_crypto_grouped_daily,
225            [date],
226            &date
227        )
228    }
229
230    /// Fetch grouped daily OHLCV bars for every forex ticker on `date`
231    /// (`YYYY-MM-DD`) in one call. See [`grouped_daily`](Self::grouped_daily).
232    pub async fn forex_grouped_daily(&self, date: &str) -> Result<Vec<(String, Candle)>> {
233        let date = date.to_string();
234        dispatch_via!(
235            self,
236            CHART,
237            as_chart,
238            ForexGroupedDaily,
239            fetch_forex_grouped_daily,
240            [date],
241            &date
242        )
243    }
244
245    /// Fetch coins/nfts/categories trending in the last 24h.
246    ///
247    /// Routes through [`Capability::CRYPTO`](crate::providers::Capability::CRYPTO).
248    /// Currently CoinGecko only.
249    #[cfg(feature = "crypto")]
250    pub async fn crypto_trending(&self) -> Result<Vec<crate::models::crypto::TrendingCoin>> {
251        dispatch_via!(
252            self,
253            CRYPTO,
254            as_crypto,
255            CryptoTrending,
256            fetch_crypto_trending,
257            []
258        )
259    }
260
261    /// Fetch aggregate global cryptocurrency market statistics.
262    ///
263    /// Routes through [`Capability::CRYPTO`](crate::providers::Capability::CRYPTO).
264    /// Currently CoinGecko only.
265    #[cfg(feature = "crypto")]
266    pub async fn crypto_global(&self) -> Result<crate::models::crypto::GlobalCryptoStats> {
267        dispatch_via!(
268            self,
269            CRYPTO,
270            as_crypto,
271            CryptoGlobal,
272            fetch_crypto_global,
273            []
274        )
275    }
276}