1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
//! Capability traits over a single equity symbol.
use super::super::Operation;
use crate::error::Result;
use crate::models::quote::QuoteSummaryResponse;
use super::ProviderCore;
/// [`crate::Capability::QUOTE`] — single and batch equity quotes.
#[async_trait::async_trait]
pub trait QuoteProvider: ProviderCore {
/// Fetch every quote module this provider serves for one symbol.
async fn fetch_quote(&self, symbol: &str) -> Result<QuoteSummaryResponse>;
/// Fetch quotes for multiple symbols in a single request.
/// Returns `(symbol, QuoteSummaryResponse)` pairs — only partially populated
/// (price module only) since batch endpoints don't return full quoteSummary data.
async fn fetch_quotes_batch(&self, _: &[&str]) -> Result<Vec<(String, QuoteSummaryResponse)>> {
Err(self.not_supported(Operation::QuotesBatch))
}
/// Fetch a snapshot for symbols spanning several asset classes in one
/// request. Rows the provider could not resolve are returned with
/// `error` set rather than dropped.
///
/// NOTE: Polygon is the only cross-asset snapshot among the providers
/// integrated here. The keyless sources are each single-asset-class, so
/// reproducing this means fanning out per class and inventing a merge.
async fn fetch_unified_snapshot(
&self,
_symbols: &[&str],
) -> Result<Vec<crate::models::quote::snapshot::MarketSnapshot>> {
Err(self.not_supported(Operation::UnifiedSnapshot))
}
}
/// [`crate::Capability::CHART`] — historical OHLCV candles and sparklines.
#[async_trait::async_trait]
pub trait ChartProvider: ProviderCore {
/// Fetch OHLCV candles at one interval over one range.
async fn fetch_chart(
&self,
symbol: &str,
interval: crate::Interval,
range: crate::TimeRange,
) -> Result<crate::models::chart::Chart>;
/// Fetch OHLCV candles between two Unix timestamps.
async fn fetch_chart_range(
&self,
_symbol: &str,
_interval: crate::Interval,
_start: i64,
_end: i64,
) -> Result<crate::models::chart::Chart> {
Err(self.not_supported(Operation::ChartRange))
}
/// Fetch lightweight sparkline data for multiple symbols in a single request.
/// Returns successfully-parsed `(symbol, Spark)` pairs; callers fill in
/// missing-symbol errors for any symbol absent from the result.
async fn fetch_spark(
&self,
_symbols: &[&str],
_interval: crate::Interval,
_range: crate::TimeRange,
) -> Result<Vec<(String, crate::models::chart::spark::Spark)>> {
Err(self.not_supported(Operation::Spark))
}
/// Fetch grouped daily OHLCV bars for every stock ticker on `date`
/// (`YYYY-MM-DD`) in a single request — market-wide rather than
/// symbol-scoped. Returns `(symbol, candle)` pairs.
///
/// NOTE: no keyless provider in this crate publishes a market-wide bulk
/// EOD file for stocks; this stays a Polygon-only capability.
async fn fetch_grouped_daily(
&self,
_date: &str,
) -> Result<Vec<(String, crate::models::chart::Candle)>> {
Err(self.not_supported(Operation::GroupedDaily))
}
/// Fetch grouped daily OHLCV bars for every crypto ticker on `date`
/// (`YYYY-MM-DD`) in a single request. Returns `(symbol, candle)` pairs.
///
/// NOTE: Binance's 24hr ticker endpoint covers every symbol in one call
/// but only as a rolling now-minus-24h window, not an arbitrary
/// historical `date` — the wrong shape for this operation, so it stays
/// Polygon-only.
async fn fetch_crypto_grouped_daily(
&self,
_date: &str,
) -> Result<Vec<(String, crate::models::chart::Candle)>> {
Err(self.not_supported(Operation::CryptoGroupedDaily))
}
/// Fetch grouped daily OHLCV bars for every forex ticker on `date`
/// (`YYYY-MM-DD`) in a single request. Returns `(symbol, candle)` pairs.
///
/// NOTE: Frankfurter serves every ECB reference rate for a historical
/// `date` in one call, but only a single daily fixing rate, not OHLCV
/// candles — the wrong shape for this operation, so it stays
/// Polygon-only.
async fn fetch_forex_grouped_daily(
&self,
_date: &str,
) -> Result<Vec<(String, crate::models::chart::Candle)>> {
Err(self.not_supported(Operation::ForexGroupedDaily))
}
}
/// [`crate::Capability::FUNDAMENTALS`] — financial statements and share-supply data.
#[async_trait::async_trait]
pub trait FundamentalsProvider: ProviderCore {
/// Fetch one financial statement at one reporting frequency.
async fn fetch_financials(
&self,
symbol: &str,
stmt_type: crate::StatementType,
frequency: crate::Frequency,
) -> Result<crate::models::fundamentals::FinancialStatement>;
/// Fetch bi-monthly short-interest settlement reports.
async fn fetch_short_interest(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::fundamentals::ShortInterest>> {
Err(self.not_supported(Operation::ShortInterest))
}
/// Fetch daily short-volume data.
async fn fetch_short_volume(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::fundamentals::ShortVolume>> {
Err(self.not_supported(Operation::ShortVolume))
}
/// Fetch share float and shares outstanding.
async fn fetch_share_float(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::ShareFloat> {
Err(self.not_supported(Operation::ShareFloat))
}
/// Fetch the company's identity/classification profile.
async fn fetch_company_profile(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::CompanyProfile> {
Err(self.not_supported(Operation::CompanyProfile))
}
/// Fetch the aggregated analyst price-target consensus.
async fn fetch_price_target_consensus(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::PriceTargetConsensus> {
Err(self.not_supported(Operation::PriceTargetConsensus))
}
/// Fetch price-target publication activity over trailing windows.
///
/// NOTE: analyst price targets aren't SEC-filed data — no keyless
/// provider in this crate publishes trailing-window target-publication
/// counts, so this stays FMP-only.
async fn fetch_price_target_summary(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::PriceTargetSummary> {
Err(self.not_supported(Operation::PriceTargetSummary))
}
/// Fetch the aggregated analyst rating consensus.
async fn fetch_rating_consensus(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::RatingConsensus> {
Err(self.not_supported(Operation::RatingConsensus))
}
/// Fetch the trailing-twelve-month key-metrics snapshot.
async fn fetch_key_metrics_ttm(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::KeyMetricsTtm> {
Err(self.not_supported(Operation::KeyMetricsTtm))
}
/// Fetch the trailing-twelve-month ratios snapshot.
async fn fetch_ratios_ttm(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::FinancialRatiosTtm> {
Err(self.not_supported(Operation::RatiosTtm))
}
/// Fetch an ETF's profile and portfolio holdings.
async fn fetch_etf_profile(
&self,
_symbol: &str,
) -> Result<crate::models::fundamentals::EtfProfile> {
Err(self.not_supported(Operation::EtfProfile))
}
/// Fetch earnings-surprise history, newest first.
async fn fetch_earnings_surprises(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::fundamentals::EarningsSurprise>> {
Err(self.not_supported(Operation::EarningsSurprises))
}
/// Fetch the raw per-analyst grade-action history behind
/// [`fetch_rating_consensus`](Self::fetch_rating_consensus)'s rollup.
async fn fetch_grading_history(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::fundamentals::GradingAction>> {
Err(self.not_supported(Operation::GradingHistory))
}
}
/// [`crate::Capability::CORPORATE`] — news, corporate events, similar-symbol
/// recommendations.
#[async_trait::async_trait]
pub trait CorporateProvider: ProviderCore {
/// Fetch recent news articles mentioning one symbol.
async fn fetch_news(&self, symbol: &str) -> Result<Vec<crate::models::corporate::news::News>>;
/// Fetch dividends, splits, and capital gains for one symbol.
async fn fetch_events(&self, symbol: &str)
-> Result<crate::models::chart::events::ChartEvents>;
/// Symbols this provider considers comparable to `symbol`.
async fn fetch_similar_symbols(
&self,
_symbol: &str,
_limit: u32,
) -> Result<Vec<crate::models::corporate::recommendation::SimilarSymbol>> {
Err(self.not_supported(Operation::Recommendations))
}
/// Fetch the company's own press releases.
async fn fetch_press_releases(
&self,
_symbol: &str,
_limit: u32,
) -> Result<Vec<crate::models::corporate::press_release::PressRelease>> {
Err(self.not_supported(Operation::PressReleases))
}
/// Fetch an earnings call transcript, provider-neutral shape. `quarter`
/// and `year` narrow to a specific call; a provider with no "latest"
/// shortcut requires both.
async fn fetch_earnings_transcript(
&self,
_symbol: &str,
_quarter: Option<&str>,
_year: Option<i32>,
) -> Result<crate::models::corporate::earnings_transcript::EarningsTranscript> {
Err(self.not_supported(Operation::EarningsTranscript))
}
/// Fetch reported executive compensation, most recent fiscal year first.
async fn fetch_executive_compensation(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::corporate::governance::ExecutiveCompensation>> {
Err(self.not_supported(Operation::ExecutiveCompensation))
}
/// Fetch reported employee headcount history, most recent period first.
async fn fetch_employee_count(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::corporate::governance::EmployeeCount>> {
Err(self.not_supported(Operation::EmployeeCount))
}
}
/// [`crate::Capability::OPTIONS`] — options chains.
#[async_trait::async_trait]
pub trait OptionsProvider: ProviderCore {
/// Fetch the options chain for one symbol.
async fn fetch_options(
&self,
symbol: &str,
date: Option<i64>,
) -> Result<crate::models::options::Options>;
}
/// [`crate::Capability::FILINGS`] — SEC filing data.
#[async_trait::async_trait]
pub trait FilingsProvider: ProviderCore {
/// Fetch regulatory filings for one symbol.
async fn fetch_filings(&self, symbol: &str) -> Result<crate::models::filings::ProviderFilings>;
/// Fetch the sectioned text of one filing by accession number.
async fn fetch_filing_sections(
&self,
_accession_number: &str,
_form: crate::models::filings::FilingSectionForm,
) -> Result<Vec<crate::models::filings::FilingSection>> {
Err(self.not_supported(Operation::FilingSections))
}
/// Fetch risk factors extracted from a symbol's SEC filings.
async fn fetch_risk_factors(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::filings::RiskFactor>> {
Err(self.not_supported(Operation::RiskFactors))
}
/// Search filing *text* rather than looking filings up by filer.
///
/// `symbol` scopes the search to one filer; the provider resolves it to
/// whatever identifier its own index is keyed by (a CIK, for EDGAR).
/// `None` searches every filer.
async fn fetch_filing_search(
&self,
_symbol: Option<&str>,
_query: &str,
_filters: &crate::models::filings::FilingSearchFilters,
) -> Result<Vec<crate::models::filings::FilingSearchHit>> {
Err(self.not_supported(Operation::FilingSearch))
}
/// Fetch insider transactions reported on Forms 3/4/5, newest filing first.
async fn fetch_insider_trades(
&self,
_symbol: &str,
_limit: u32,
) -> Result<Vec<crate::models::filings::InsiderTrade>> {
Err(self.not_supported(Operation::InsiderTrades))
}
/// Fetch the latest reported 13F institutional holdings for a filer.
async fn fetch_institutional_holdings(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::filings::InstitutionalHolding>> {
Err(self.not_supported(Operation::InstitutionalHoldings))
}
/// Fetch congressional (senate) stock-trade disclosures for a symbol.
async fn fetch_congressional_trades(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::filings::CongressionalTrade>> {
Err(self.not_supported(Operation::CongressionalTrades))
}
/// Fetch SEC fails-to-deliver data for a symbol.
async fn fetch_fails_to_deliver(
&self,
_symbol: &str,
) -> Result<Vec<crate::models::filings::FailToDeliver>> {
Err(self.not_supported(Operation::FailsToDeliver))
}
}