SBFoundation Domain & Dataset Reference
Last Updated: 2026-08-30 (TASK-3649 / B-450.2 — grades gains lifecycle_scope: all, the first use of the new per-dataset symbol-lifecycle keymap key; entry count unchanged at 59)
This document is the comprehensive reference for all data domains and datasets in SBFoundation. For API usage and pipeline operations, see docs/api-usage.md. For dataset configuration details, see config/dataset_keymap.yaml.
Domain Overview
SBFoundation ingests data across 5 domains containing 59 datasets from 3 sources (FMP, FRED, FINRA). (Counts corrected against config/dataset_keymap.yaml on 2026-08-18 — the previous “4 domains / 54 datasets” wording predated the etf domain and the revenue-segmentation feeds.)
eod |
3 |
Mixed |
End-of-day prices and company profiles |
api.run_eod() |
quarter |
4 |
Global |
Quarterly financial statements and key metrics |
api.run_quarter() |
annual |
5 |
Global |
Annual financial statements, key metrics, and ratios |
api.run_annual() |
eow |
45 |
Mixed |
End-of-week: index constituents, macro indicators, FX rates, stock splits, analyst estimates, analyst rating actions (grades, F-450), earnings surprises, revenue segmentation, short interest |
api.run_eow() |
etf |
2 |
Per-ticker |
ETF reference data (info + holdings, F-343 / EU-2); folded into the EOW cadence bucket |
ingest-etf CLI |
Execution order: eod → quarter → annual → eow
Data sources: - FMP (Financial Modeling Prep) — 27 datasets; requires FMP_API_KEY - FRED (Federal Reserve Economic Data) — 31 datasets; requires FRED_API_KEY - FINRA (Financial Industry Regulatory Authority) — 1 dataset; no API key required
EOD Domain (3 datasets)
End-of-day market data and company profiles. This is the primary daily-refresh domain.
Market Data
eod-bulk-price |
global |
fmp_eod_bulk_price |
symbol, date |
Daily (1) |
eod-bulk |
eod-price-history |
per_ticker |
fmp_eod_bulk_price |
symbol, date |
Daily (0) |
historical-price-eod/dividend-adjusted |
- eod-bulk-price: Daily bulk CSV of OHLCV + adjusted close for all instruments. Primary source for
gold.fact_eod. At Gold promotion, OHLC columns are adjusted by the adj_close / close ratio so all prices are split- and dividend-adjusted.
- eod-price-history: Per-ticker historical price backfill (deep-history
backfill-eod-chunked + delisted survivorship backfill). Writes to the same Silver table as eod-bulk-price. B-147.6 (F-147): endpoint corrected from historical-price-eod/full (which returns no adjClose → NULL adj_close for ~28% of gold.fact_eod) to historical-price-eod/dividend-adjusted (returns adjOpen/adjHigh/adjLow/adjClose), mapped via the dedicated EodDividendAdjustedPriceDTO — adjClose populates both close and adj_close, so the rows are already-adjusted and identical to the bulk path through Gold’s back-adjustment math. B-147.9 (F-147): the keymap dto_schema api: keys are the projection authority for this adjusted endpoint. open/high/low/close declare api: adjOpen/adjHigh/adjLow/adjClose and adj_close declares api: adjClose (TASK-3881). The Silver path (DTOProjection._project_from_schema) does not read the DTO field metadata. Before that fix, about 8.95M adjusted rows (4,737 symbols) projected NULL open/high/low/close. The stored-row repair is the runbook’s B-147.9 section (TASK-3947). Not in the nightly EOD path (excluded by EodBronzeIngester); driven only by the backfill/delisted commands.
- Fields:
symbol, date, open, high, low, close, adj_close, volume, data_quality_flag (F-086 — DTO-time severity-wins flag: non_positive_close (error) / non_positive_volume (warn) / ohlcv_corrected (info))
- Symbol filter:
symbol_filter_col: symbol — only allowlisted instruments promoted to Silver
- Calendar guard (B-448.6 / TASK-3874): both
eod-bulk-price and eod-price-history declare calendar_guard: weekday_only — an optional keymap field (vocabulary: weekday_only; absent = no guard; an unknown value is rejected by DatasetKeymapEntry.from_payload). At Silver promotion SilverService._apply_calendar_guard drops every row whose row_date_col falls on a Saturday or Sunday, and quarantines the whole artifact (zero rows promoted, one WARNING) when its weekend-row fraction is ≥ sbsilver.settings.WEEKEND_ROW_QUARANTINE_FRACTION (0.05). Why: FMP’s historical-price-eod/dividend-adjusted endpoint returned WLL’s entire history shifted one calendar day early (878 Sunday bars), and the (symbol, date) MERGE turned that into overwrite + insert + stranded NULL-adj_close rows. Of 4,561 tickers compared across both endpoints, 4,545 are clean and 16 carry weekend bars. A shifted series mislabels its weekday rows too, which is why a payload over the threshold is refused rather than trimmed. US-holiday weekdays are deliberately not guarded (non-US listings trade on them). The stored-data repair is six deferred, approval-gated migrations under db/migrations/pending/20260911_101..106_*.sql.pending (operator TASK-3880). Detail → docs/sbfoundation-steps.md Step 3.
- Quality thresholds (F-086 / TASK-313):
eod-bulk-price carries an optional quality_thresholds block consumed by PriceDataQualityService during the factor phase. Defaults: flag_high_price_z=5.0, flag_high_volume_z=5.0, max_gap_business_days=5, and — TASK-3197 — adjacency_ratio_upper=4.0, adjacency_ratio_lower=0.25, adjacency_sub_cent_floor=0.01 for the fourth detector flag_adjacency_discontinuity (single-day adj_close discontinuity = vendor back-adjustment artifact; fires when the adjacent ratio falls outside [lower, upper], or when the prior close is sub-cent — below the $0.01 US minimum tick, hence an artifact denominator). TASK-3863 adds frozen_price_min_run_bars=21 for the fifth detector flag_frozen_price (an adj_close unchanged across >= N consecutive bars, one finding per streak; keyed on price rather than volume, since a frozen vendor series often carries real volume). Detect-only: neither detector mutates stored prices or gates a run. Override per-key in config/dataset_keymap.yaml; missing keys fall back to the service defaults. A key absent from sbsilver.settings.DEFAULT_QUALITY_THRESHOLDS is ignored by the resolver, so a new threshold must be added there first.
- Documentation: FMP EOD Bulk | FMP Historical Price
Quarter Domain (4 datasets)
Quarterly financial statements and key metrics ingested as global bulk CSVs. One file per quarter covers all allowlisted instruments.
income-statement-bulk-quarter |
fmp_income_statement_bulk_quarter |
symbol, period, calendar_year |
Daily (1) |
income-statement-bulk?period=quarter |
balance-sheet-bulk-quarter |
fmp_balance_sheet_bulk_quarter |
symbol, period, calendar_year |
Daily (1) |
balance-sheet-statement-bulk?period=quarter |
cashflow-bulk-quarter |
fmp_cashflow_bulk_quarter |
symbol, period, calendar_year |
Daily (1) |
cash-flow-statement-bulk?period=quarter |
key-metrics-bulk-quarter |
fmp_key_metrics_bulk_quarter |
symbol, period, calendar_year |
Quarterly (90) |
key-metrics-bulk?period=quarter |
- Gold table:
gold.fact_quarter — one row per (instrument_sk, period_date_sk, period). Built from income + balance sheet + cashflow via optional LEFT JOINs.
- Key fields — income:
revenue, gross_profit, operating_income, net_income, ebitda, eps, eps_diluted, interest_expense, depreciation_and_amortization, weighted_average_shs_out, weighted_average_shs_out_diluted
- Key fields — balance sheet:
total_assets, total_liabilities, total_stockholders_equity, cash_and_cash_equivalents, long_term_debt, net_debt, goodwill_and_intangible_assets, retained_earnings
- Key fields — cashflow:
operating_cash_flow, capital_expenditure, free_cash_flow, dividends_paid, common_stock_repurchased
- Key fields — metrics:
roic, invested_capital, capex_to_ocf, ev_to_ebitda, days_sales_outstanding, days_payables_outstanding, days_inventory
- Symbol filter: All use
symbol_filter_col: symbol
- Row-level quality flag (F-086 / TASK-307+TASK-310):
fmp_balance_sheet_bulk_quarter and fmp_income_statement_bulk_quarter each gain a nullable data_quality_flag VARCHAR column. DTOs stamp balance_sheet_identity_violation (severity warn) when |total_assets - (total_liabilities + total_stockholders_equity)| / |total_assets| > 0.05, and net_income_exceeds_revenue (severity warn) when revenue > 0 AND net_income > revenue. Each fired flag also lands a row in ops.silver_anomaly.
- Season gate:
QuarterService skips runs outside earnings windows unless year+period are provided explicitly.
- Documentation: FMP Financial Statements Bulk | FMP Key Metrics Bulk
Annual Domain (5 datasets)
Annual (FY) financial statements, key metrics, and ratios. Same bulk CSV pattern as quarterly.
income-statement-bulk-annual |
fmp_income_statement_bulk_annual |
symbol, calendar_year |
Monday (1) |
income-statement-bulk?period=FY |
balance-sheet-bulk-annual |
fmp_balance_sheet_bulk_annual |
symbol, calendar_year |
Tuesday (1) |
balance-sheet-statement-bulk?period=FY |
cashflow-bulk-annual |
fmp_cashflow_bulk_annual |
symbol, calendar_year |
Wednesday (1) |
cash-flow-statement-bulk?period=FY |
key-metrics-bulk-annual |
fmp_key_metrics_bulk_annual |
symbol, calendar_year |
Thursday (365) |
key-metrics-bulk?period=FY |
ratios-bulk-annual |
fmp_ratios_bulk_annual |
symbol, calendar_year |
Friday (365) |
ratios-bulk?period=FY |
- Gold table:
gold.fact_annual — one row per (instrument_sk, period_date_sk). Merged from income + balance sheet + cashflow + key metrics + ratios via optional LEFT JOINs.
- Key fields — income:
revenue, gross_profit, operating_income, net_income, ebitda, eps, eps_diluted, interest_expense, depreciation_and_amortization, weighted_average_shs_out, weighted_average_shs_out_diluted
- Key fields — balance sheet:
total_assets, total_liabilities, total_stockholders_equity, cash_and_cash_equivalents, long_term_debt, net_debt, goodwill_and_intangible_assets, retained_earnings
- Key fields — cashflow:
operating_cash_flow, capital_expenditure, free_cash_flow, dividends_paid, common_stock_repurchased
- Key fields — ratios:
gross_profit_margin, operating_profit_margin, net_profit_margin, effective_tax_rate, debt_ratio, interest_coverage
- Symbol filter: All use
symbol_filter_col: symbol
- Row-level quality flag (F-086 / TASK-307+TASK-310):
fmp_balance_sheet_bulk_annual and fmp_income_statement_bulk_annual each gain a nullable data_quality_flag VARCHAR column. Same detectors as the quarter variants — balance_sheet_identity_violation (warn) and net_income_exceeds_revenue (warn) — with each fired flag also landing a row in ops.silver_anomaly.
- Season gate:
AnnualService skips runs outside Jan–Mar unless year is provided explicitly.
- Documentation: FMP Financial Statements Bulk | FMP Key Metrics Bulk | FMP Ratios Bulk
EOW Domain (45 datasets)
End-of-week data: index constituent history, macro indicators, FX rates, company delisted list, mergers & acquisitions, stock splits, analyst estimates, analyst rating actions (grades, F-450), earnings surprises, revenue segmentation, and FINRA equity short interest. Keymap default cadence is run_days: [sat]; since F-356 (EU-6) the weekly deep run (Friday NIGHTLY_FULL, or an ad-hoc --research-mode nightly-full) force-refreshes the whole bucket by bypassing that weekday gate (force_weekly=True → bypass_run_days=True). ⚠ That flag bypasses the min_age_days floor as well — the earlier claim here, “with min_age_days still gating”, was false and is corrected by B-102.1: BronzeService._process_run_request nulls the ingestion watermark under the flag, so decide_cadence can never return too_soon. Plain Mon–Thu NIGHTLY stays cadence-gated, and since B-102.1 that is true per plan variant: the weekday EOW branch hands the bypass only to date_list / snapshot recipes (the FRED series the EOW date-walk exists to gap-fill), while the seven symbol_list per-ticker datasets (analyst-estimates ×2, earnings-surprises, grades, revenue-product-segmentation, revenue-geographic-segmentation, stock-split) resolve their own declared run_days / min_age_days. Before that fix a permanently-unsatisfiable FRED anchor put the whole domain on the bypass branch every Monday and Tuesday, re-fetching all seven nightly. Bronze-only task: ingest_bronze_eow_task; Silver promotion: promote_silver_eow_task.
Index Constituent History
sp500-constituent-history |
fmp_sp500_constituent_history |
symbol, date_of_change |
Weekly (7, Sat) |
historical-sp500-constituent |
nasdaq100-constituent-history |
fmp_nasdaq100_constituent_history |
symbol, date_of_change |
Weekly (7, Sat) |
historical-nasdaq-constituent |
djia-constituent-history |
fmp_djia_constituent_history |
symbol, date_of_change |
Weekly (7, Sat) |
historical-dowjones-constituent |
- Gold table:
gold.fact_index_constituent — one row per (index_sk, instrument_sk, effective_from). effective_to = NULL means still a member. Built by GoldIndexService by replaying Silver change-log tables.
- Fields:
symbol, date_of_change, added_security, removed_ticker, removed_security, reason
- No symbol filter — index constituent data includes removed tickers by design.
- Reliable from: SP500 Oct 2008; NASDAQ100 Jan 1995; DJIA Jan 1994.
- Documentation: FMP SP500 | FMP NASDAQ | FMP DJIA
Stock Splits
stock-split |
per_ticker |
fmp_stock_split |
symbol, date |
Monthly (30, Sat) |
splits |
- Purpose: Historical stock split events (numerator, denominator, split type). Feeds
gold.fact_stock_split.
- Fields:
symbol, date, numerator, denominator, split_type
- Documentation: FMP Stock Splits
Macro / Economics Indicators
fred-dgs10 |
fred |
fred_dgs10 |
date |
Saturday (1) |
series/observations |
fred-usrecm |
fred |
fred_usrecm |
date |
Saturday (28) |
series/observations |
fred-dgs1mo |
fred |
fred_dgs1mo |
date |
Saturday (1) |
series/observations |
fred-dgs3mo |
fred |
fred_dgs3mo |
date |
Saturday (1) |
series/observations |
fred-dgs1 |
fred |
fred_dgs1 |
date |
Saturday (1) |
series/observations |
fred-dgs2 |
fred |
fred_dgs2 |
date |
Saturday (1) |
series/observations |
fred-dgs5 |
fred |
fred_dgs5 |
date |
Saturday (1) |
series/observations |
fred-dgs30 |
fred |
fred_dgs30 |
date |
Saturday (1) |
series/observations |
fred-t10y2y |
fred |
fred_t10y2y |
date |
Saturday (1) |
series/observations |
fred-baa10y |
fred |
fred_baa10y |
date |
Saturday (1) |
series/observations |
fred-vixcls |
fred |
fred_vixcls |
date |
Saturday (1) |
series/observations |
market-risk-premium |
fmp |
fmp_market_risk_premium |
country |
Saturday (30) |
market-risk-premium |
- fred-dgs10: 10-Year Treasury Constant Maturity Rate (risk-free rate proxy for CAPM/DCF). Series starts 1962-01-01. Used by
EowHistoryService as the representative EOW freshness cursor.
- fred-usrecm: NBER U.S. Recession Indicator (1 = recession, 0 = expansion). Series starts 1967-01-01.
- F-317 FRED macro-series expansion (9 datasets): Silver-only macro time-series (each
series/observations, Saturday cadence, min_age_days: 1), sharing one generic FredObservationDTO over {date, value}. Data-only — no macro-style factor is derived from them yet (deferred to Wave 2).
- fred-dgs1mo / fred-dgs3mo: 1-Month / 3-Month Treasury Constant Maturity Rate — the short-end of the curve, the correct excess-return risk-free tenor (the 10Y
fred-dgs10 stays the cost-of-equity / WACC proxy).
- fred-dgs1 / fred-dgs2 / fred-dgs5 / fred-dgs30: 1/2/5/30-Year Treasury Constant Maturity Rate — the Treasury constant-maturity yield curve.
- fred-t10y2y: 10-Year minus 2-Year Treasury term spread (yield-curve / regime signal).
- fred-baa10y: Moody’s Baa corporate yield minus 10-Year Treasury credit spread (credit-regime signal).
- fred-vixcls: CBOE Volatility Index (VIX) close (volatility-regime signal).
- market-risk-premium: Equity risk premium by country (total equity risk premium, country risk premium).
- Gold: All FRED + market-risk-premium datasets are Silver-only — no
instrument_sk FK. Consumed directly by the feature engine.
- Requires:
FRED_API_KEY for FRED datasets.
- Documentation: FRED DGS10 | FRED USRECM | FRED DGS1MO | FRED DGS3MO | FRED DGS1 | FRED DGS2 | FRED DGS5 | FRED DGS30 | FRED T10Y2Y | FRED BAA10Y | FRED VIXCLS | FMP Market Risk Premium
Foreign Exchange Rates (FRED FX)
fred-dexuseu |
fred |
fred_dexuseu |
DEXUSEU |
EUR |
usd_per_foreign |
Saturday (1) |
fred-dexusuk |
fred |
fred_dexusuk |
DEXUSUK |
GBP |
usd_per_foreign |
Saturday (1) |
fred-dexusal |
fred |
fred_dexusal |
DEXUSAL |
AUD |
usd_per_foreign |
Saturday (1) |
fred-dexusnz |
fred |
fred_dexusnz |
DEXUSNZ |
NZD |
usd_per_foreign |
Saturday (1) |
fred-dexcaus |
fred |
fred_dexcaus |
DEXCAUS |
CAD |
foreign_per_usd |
Saturday (1) |
fred-dexjpus |
fred |
fred_dexjpus |
DEXJPUS |
JPY |
foreign_per_usd |
Saturday (1) |
fred-dexnous |
fred |
fred_dexnous |
DEXNOUS |
NOK |
foreign_per_usd |
Saturday (1) |
fred-dexszus |
fred |
fred_dexszus |
DEXSZUS |
CHF |
foreign_per_usd |
Saturday (1) |
fred-dexbzus |
fred |
fred_dexbzus |
DEXBZUS |
BRL |
foreign_per_usd |
Saturday (1) |
fred-dexmxus |
fred |
fred_dexmxus |
DEXMXUS |
MXN |
foreign_per_usd |
Saturday (1) |
fred-dexchus |
fred |
fred_dexchus |
DEXCHUS |
CNY |
foreign_per_usd |
Saturday (1) |
fred-dexinus |
fred |
fred_dexinus |
DEXINUS |
INR |
foreign_per_usd |
Saturday (1) |
fred-dexkous |
fred |
fred_dexkous |
DEXKOUS |
KRW |
foreign_per_usd |
Saturday (1) |
fred-dexsius |
fred |
fred_dexsius |
DEXSIUS |
SGD |
foreign_per_usd |
Saturday (1) |
fred-dextaus |
fred |
fred_dextaus |
DEXTAUS |
TWD |
foreign_per_usd |
Saturday (1) |
fred-dexsdus |
fred |
fred_dexsdus |
DEXSDUS |
SEK |
foreign_per_usd |
Saturday (1) |
fred-dexdnus |
fred |
fred_dexdnus |
DEXDNUS |
DKK |
foreign_per_usd |
Saturday (1) |
fred-dexsfus |
fred |
fred_dexsfus |
DEXSFUS |
ZAR |
foreign_per_usd |
Saturday (1) |
fred-dexhkus |
fred |
fred_dexhkus |
DEXHKUS |
HKD |
foreign_per_usd |
Saturday (1) |
fred-ccusma02ilm618n |
fred |
fred_ccusma02ilm618n |
CCUSMA02ILM618N |
ILS |
foreign_per_usd |
Saturday (1) — OECD monthly avg (no daily H.10 shekel series) |
- TASK-3143 / F-444 FRED FX expansion (20 datasets): Silver-only daily exchange-rate series (each
series/observations, Saturday cadence, min_age_days: 1, max_date_quantity: 8), all sharing the generic FredObservationDTO over {date, value} (key col date). Reporting currency → USD conversion source for a later task that converts non-USD Gold fundamentals to USD; the currency → series + quoting-convention mapping is governed in config/fx_pairs.yaml (loaded by sbcontracts.fx_pairs.FxPairConfigService).
- Convention:
usd_per_foreign — rate is USD per 1 unit foreign → to-USD factor = rate (multiply). foreign_per_usd — rate is foreign units per 1 USD → to-USD factor = 1/rate (divide).
- Gold: Silver-only — no
instrument_sk FK (FX series have no instrument). Requires FRED_API_KEY.
- Caveat: the
DEX* series ids and quoting directions in fx_pairs.yaml are best-effort per the design brief; the operator verifies them against live FRED metadata at ingest (F-444 §14.1 / TASK-3147).
Analyst Estimates & Earnings Surprises
analyst-estimates |
annual |
per_ticker |
fmp_analyst_estimates |
ticker, fiscal_year, fiscal_quarter, as_of_date |
Weekly (7, Sat) |
analyst-estimates?symbol=__ticker__&period=annual&limit=10 |
analyst-estimates |
quarter |
per_ticker |
fmp_analyst_estimates |
ticker, fiscal_year, fiscal_quarter, as_of_date |
Weekly (7, Sat) |
analyst-estimates?symbol=__ticker__&period=quarter&limit=20 |
earnings-surprises |
— |
per_ticker |
fmp_earnings_surprises |
ticker, date |
Quarterly (90, Sat) |
earnings?symbol=__ticker__ |
- analyst-estimates: Forward-looking consensus estimates (EPS, revenue, EBITDA, net income) with high/low ranges and analyst count. Two recipes share one Silver table:
discriminator=annual writes fiscal-year rows (fiscal_quarter=0); discriminator=quarter writes quarterly rows (fiscal_quarter=1..4). fiscal_year/fiscal_quarter are derived in AnalystEstimatesDTO.transform_df_content from the period-end date using a row-density heuristic (annual if rows/unique_years < 2). 2026-04-14: migrated from the retired v3 endpoint to /stable/analyst-estimates; upstream field names flattened (epsAvg, revenueAvg, ebitdaAvg, numAnalystsEps) and mapped back to unchanged Silver column names via api: keys in dto_schema. TASK-3001: as_of_date (synthesized by SilverService from the Bronze manifest coverage date — row_date_col stays unset) was appended to the natural key of BOTH recipe blocks, so the weekly Saturday re-pull persists a weekly consensus time-series (one row per snapshot date) instead of UPSERT-overwriting a single row per (ticker, fiscal_year, fiscal_quarter). AnalystEstimatesDTO.KEY_COLS is kept in lockstep.
- earnings-surprises: Historical actual-vs-estimated EPS per reporting date. Feeds
gold.fact_earnings_surprises and is aggregated into beat-rate statistics for gold.fact_eps_forecast. 2026-04-14: migrated from retired v3 /api/v3/earnings-surprises to /stable/earnings; fields epsActual / epsEstimated map to Silver actual_earning_result / estimated_earning.
- Key fields — analyst-estimates:
estimated_eps_avg, estimated_eps_high, estimated_eps_low, estimated_revenue_avg, estimated_revenue_high, estimated_revenue_low, estimated_ebitda_avg, estimated_net_income_avg, number_analyst_estimated
- Key fields — earnings-surprises:
actual_earning_result, estimated_earning
- Symbol filter: Both use
symbol_filter_col: ticker — ticker, not symbol (B-041.1 / TASK-3241): symbol_filter_col names the Silver column the allowlist gate filters on, and these DTOs project ticker. It read symbol until 2026-08, which made the Silver gate a silent no-op here (real enforcement was upstream in IngestPlannerService, which only ever requests allowlisted symbols). See CLAUDE.md §2 constraint 10.
- Gold: Drives three Gold fact tables —
fact_analyst_estimates, fact_earnings_surprises, fact_eps_forecast (the last built by EpsForecastService).
- Documentation: FMP /stable/ Analyst Estimates | FMP /stable/ Earnings
Analyst Grades — sell-side rating actions (F-450 / EC-5)
grades |
per_ticker |
fmp_grades |
ticker, date, grading_company, new_grade |
Weekly (6, Sat) |
grades?symbol=__ticker__&limit=5000 |
- Purpose (F-450 / TASK-3220…3226): firm-level sell-side rating actions — one row per grading firm × action. This is the platform’s first analyst rating feed;
analyst-estimates above is forward consensus numbers (EPS / revenue / EBITDA), never a rating. DTO AnalystGradeDTO (sbcontracts.dtos.fundamentals.analyst_grade_dto) maps symbol/date/gradingCompany/previousGrade/newGrade/action → ticker/date/grading_company/previous_grade/new_grade/action.
new_grade is IN the natural key — deliberate. The obvious key (ticker, date, grading_company) is not unique: measured on AAPL, 2 collisions in 1,786 records (one byte-identical Jefferies duplicate, one genuine Wells Fargo Perform-vs-Market Perform variant on the same day). Adding action does not disambiguate. AnalystGradeDTO.KEY_COLS, the keymap key_cols, and the Silver PK are kept in lockstep.
first_seen_at is the survivorship receipt. Declared via the keymap’s insert_only_cols: [first_seen_at], so SilverService stamps it on INSERT and excludes it from the MERGE’s WHEN MATCHED THEN UPDATE SET clause — every other column (incl. the lineage trio) refreshes on re-promote, this one never does. It is the only durable evidence that this platform held a record before the vendor deleted it.
lifecycle_scope: all (B-450.2 / TASK-3649) — the ONLY dataset that carries it. Every other per-ticker dataset resolves its fetch universe as Allowlist ∩ is_actively_trading = TRUE (a join to silver.fmp_company_profile_bulk in sbbronze.domain_ingest._allowlist.resolve_allowlisted_tickers), which is correct for price history — a delisted name has none to fetch. It is self-defeating here: this feed exists because FMP purges grades as names age out, and the coverage detector below compares successive observations of a symbol, so never re-fetching the delisted cohort makes a purge of exactly those names undetectable. Measured 2026-08-30, the default resolved 6,410 of 9,831 allowlisted symbols, stranding 3,385 — 2,802 of them delisted — on a single observation. The default’s stated premise is also false for this endpoint: run 260818_7b8941 fetched all 9,731 symbols, HTTP 200 on every one, and 1,382 of the excluded names returned records (77,929 in total). Costs ~15 min per pull at ~230 files/min; the FMP 3,000/min throttle is not binding. EowBronzeIngester partitions its per-ticker recipes by scope and drives one Bronze batch per distinct scope, so the six other per-ticker EOW datasets are provably unaffected (pinned by a byte-identical-SQL assertion in tests/unit/sbbronze/test_lifecycle_scope_allowlist.py).
silver.fmp_grades_coverage — the vendor-purge detector. A derived Silver table (migration 20260818_004), not a keymap dataset — same status as silver.fmp_promotion_allowlist. One observation row per (ticker, pulled_at) carrying n_records, min_date, max_date, run_id. Written fail-open by GradesCoverageService (src/sbsilver/coverage/) from SilverService._project_artifact; a coverage failure logs a WARNING and the promote continues. Purely observational — nothing gates on it. It exists because the idempotent UPSERT promote can only ever make silver.fmp_grades grow, so a vendor purge leaves no trace there at all; a falling n_records or a forward-moving min_date for a symbol is the only visible purge signal. min_date/max_date are nullable so the most important observation of all — a total purge, n_records = 0 with no span — is representable.
- Gold:
gold.fact_analyst_grade (migration 20260818_005), PK (instrument_sk, date_sk, grading_company, new_grade), built by GoldFactService._build_fact_analyst_grade. Unlike every sibling builder in that file, the symbol → instrument_sk join is lifecycle-aware — explicit ipo_date / delisting_date predicates, so a rating can only attach to an instrument that was alive on the rating’s own date. Without them, ticker recycling staples a live company’s ratings onto a dead instrument_sk (measured: 3% of covered delisted names carry post-delisting records; P / Pandora Media, delisted 2019-01-31, returns 240 records through 2026-08-13 belonging to whoever now holds the ticker). Unresolved rows are counted and logged in two classes (symbol absent from dim_instrument vs instrument not alive on the grade date) rather than silently dropped.
grade_ordinal — fail-open, NULLABLE on purpose. config/analyst_grade_scale.yaml (loaded by sbcontracts.analyst_grade_scale) maps the bounded 23-string vendor vocabulary onto a 5-point ordinal (1 bearish … 3 neutral … 5 bullish). An unmapped string resolves to NULL and is logged once per distinct value — never coerced to the neutral midpoint, which would fabricate a “Hold” no analyst ever issued. The raw new_grade stays authoritative; the ordinal is derived and disposable, so a mapping judged wrong later is fixed additively in the YAML without touching the fact.
- Deliberately NOT a factor: no factor YAML, no
ACTIVE_FACTOR_IDS change, no gate math, no enforcement switch, no SPA surface. Display / diagnostics only.
- HARD NON-GOAL — do not backtest against the vendor’s 2012–2026 history. FMP purges grade history continuously as names age out of the listed universe. Measured 2026-08-18: liquidity-matched delisted names show 35% coverage against a 91% live-liquid baseline (a 56pp gap), and 2016 / 2017 / 2018 delistings return exactly 0%. Beyond roughly three years back this is a survivor list, not a panel — and the missing names are disproportionately failures, so any factor measured on it looks better than reality and would clear the promotion gates for the wrong reason (the gates test whether a signal is real, not whether the panel is complete). The platform’s own accumulated history starts 2026-08 and becomes honestly testable in roughly four quarters. This is the first dataset ingested specifically because the vendor is known to delete it — hence the urgency is data decay, not feature demand.
- Symbol filter:
symbol_filter_col: ticker — ticker, not symbol (B-041.1 / TASK-3241); it names the Silver column, and AnalystGradeDTO projects ticker. See CLAUDE.md §2 constraint 10.
- Plan variant:
SymbolListPlan — a per-ticker full-history feed with no date pagination, so max_date_quantity is null by construction. max_symbol_quantity is also null deliberately: IngestPlannerService._dispatch_variant truncates the symbol list alphabetically at the cap, so any finite value would permanently pin the feed to the alphabetically-first N symbols. Conservatism rides on the weekly cadence gate (run_days: [sat] + min_age_days: 6) instead.
- State series, not an event series: reiterations dominate — all-history 80%
maintain / 10% upgrade / 10% downgrade; rating changes are sparse (~15/trading day across the liquid universe) while rating state is dense (~173 events/trading day).
- Documentation: FMP /stable/ Grades
Revenue Segmentation (product / geographic)
revenue-product-segmentation |
FY |
per_ticker |
fmp_revenue_product_segmentation |
ticker, fiscal_year, segment |
Yearly (365, Sat) |
revenue-product-segmentation?symbol=__ticker__&period=annual |
revenue-geographic-segmentation |
FY |
per_ticker |
fmp_revenue_geographic_segmentation |
ticker, fiscal_year, segment |
Yearly (365, Sat) |
revenue-geographic-segmentation?symbol=__ticker__&period=annual |
- TASK-3175: annual (
period=annual, discriminator: FY) revenue breakdowns. The vendor returns one object per (symbol, fiscal year) carrying a nested data dict whose keys are dynamic segment names ({"iPhone": 209586000000, "Mac": ...} / {"Americas": ..., "Europe": ...}). RevenueSegmentationDTO.transform_df_content melts that dict 1→N into one Silver row per segment, lifting the parent identity columns onto every child (symbol→ticker, fiscalYear→fiscal_year, date, reportedCurrency→reported_currency). Audit-first: a malformed/absent data payload contributes zero rows and never raises.
- Shared DTO, separate tables: both datasets map to the single
RevenueSegmentationDTO (identical vendor shape) but — unlike analyst-estimates — each writes its own Silver table, so a product segment and a geographic segment of the same name never collide on the (ticker, fiscal_year, segment) natural key.
domain: eow is load-bearing: sbshared.settings.DOMAINS is [eod, quarter, annual, eow, etf]; a recipe whose domain is absent from that list is silently skipped with no error. A 2026-03 iteration of these very datasets used domain: fundamentals and consequently never produced a single row.
- Key fields:
segment (dynamic vendor segment name, part of the natural key), revenue, reported_currency, date (row_date_col, the fiscal period-end).
- Symbol filter: both use
symbol_filter_col: ticker — ticker, not symbol (B-041.1 / TASK-3241); it names the Silver column, and RevenueSegmentationDTO projects ticker. See CLAUDE.md §2 constraint 10.
- Gold: both feeds promote into the single
gold.fact_revenue_segment (TASK-3178), one row per (instrument_sk, period_date_sk, segment_kind, segment_name) where segment_kind discriminates 'product' vs 'geographic'. segment_name is a degenerate text column — vendor labels stored verbatim, never resolved to a dimension. Display-only: no _f column, no factor lift, ACTIVE_FACTOR_IDS untouched; the sole consumer is sbscreen (the screen_segments.json sidecar behind GET /api/screen-segments).
- Documentation: FMP /stable/ Revenue Product Segmentation | FMP /stable/ Revenue Geographic Segmentation
Equity Short Interest
equity-short-interest |
finra |
global |
finra_equity_short_interest |
symbol, settlement_date |
Weekly (7, Sat) |
- equity-short-interest: FINRA bi-monthly equity short interest regulatory files. Free, auth-free CSV files fetched by
FinraFileAdapter from settlement-date-templated URLs. Feeds short interest analytics.
- Fields:
symbol, settlement_date, plus FINRA short interest columns.
- Symbol filter:
symbol_filter_col: symbol
- No API key required — FINRA publishes auth-free regulatory data.
- Gold:
gold.fact_short_interest (F-070 / TASK-061) — built by GoldFactService._build_fact_short_interest() after _build_fact_annual() in the Gold core phase. Three _f features: short_interest_delta_f, short_interest_change_pct_f, and short_pct_float_f (the last uses fact_annual.weighted_average_shs_out_diluted as a proxy for true float via a point-in-time LEFT JOIN; resolves to NULL when no annual report is yet available).
- Factor layer (F-299):
gold.fact_short_interest now also feeds the factor pool — the platform’s first positioning-style factors, days_to_cover (a new days_to_cover_f column) and short_pct_float (reuses the existing short_pct_float_f column above), both direction: negative, experimental. FactorValueLiftService snaps each factor row’s knowledge date_sk forward to the first US market day on/after settlement_date + 18 calendar days (FINRA’s ~16-18 day dissemination lag) via a new TABLE_DATE_SK_EXPR["fact_short_interest"] entry — no dataset/schema/cadence change here, consumer-side only.
- Ingestion cadence / n_obs accrual-rate context (TASK-2678): the keymap recipe’s
data_source_path (equity/otcmarket/biweekly/shrt{settlement_date}.csv) and run_days: [sat] confirm FINRA settles this file bi-weekly — twice a month, one file per settlement date — not daily. days_to_cover / short_pct_float are nonetheless evaluated by sbdiag.FactorDiagnosticsService against the daily-cadence min_n_obs=60 bar (fact_short_interest is absent from sbdiag.settings.SOURCE_TABLE_CADENCE, so _resolve_cadence falls back to "daily"). At ~2 new settlement dates per month, organic forward accrual alone would take roughly 2.5 years (60 obs ÷ ~2 obs/month) to clear that bar — the 17 observed dates behind the original TASK-2533/2534/2535 finding came from a historical backfill, not from nightly accrual, and further backfill (not the passage of nightly runs) is what will actually move these two factors past min_n_obs. This is a genuine cadence mismatch in the same family as the F-292/LH-26 annual/quarterly-factor defect, but is deliberately not fixed by TASK-2678 — the fix here (insufficient_data status + auto-promote) works correctly regardless of which cadence bucket a factor is evaluated against; reclassifying fact_short_interest’s cadence bucket (e.g. a new "biweekly" CADENCE_PARAMS entry) is a separate, out-of-scope follow-on if the ~2.5-year accrual horizon proves unacceptable in practice.
- Documentation: FINRA Equity Short Interest
ETF Reference Data (F-343 / EU-2)
etf-info |
fmp |
per_ticker |
fmp_etf_info |
ticker |
Weekly (Sat) |
etf/info?symbol=__ticker__ |
etf-holdings |
fmp |
per_ticker |
fmp_etf_holdings |
ticker, asset |
Weekly (Sat) |
etf/holdings?symbol=__ticker__ |
- Domain:
etf (new recipe domain registered by F-343). Both datasets are Silver-only (no Gold promotion) — reference data for sector-concentration / correlation modeling.
- etf-info: One row per ETF profile (expense ratio, AUM, asset class, domicile, inception, sector/country exposure summary). DTO
EtfInfoDTO.
- etf-holdings: N rows per ETF, one per underlying holding (asset symbol, name, weight, shares, market value). DTO
EtfHoldingsDTO.
- Symbol scope: both carry the declarative
symbol_scope: etf keymap field, which scopes ingestion via IngestPlannerService to is_etf=TRUE allowlist symbols only (gated on SB_ALLOW_ETFS=ON, on since F-247).
- Gold: none. Gold facts + holding→
instrument_sk resolution + holdings-weighted proxies are deferred to EU-4.
- Weekly cadence (F-355 / EU-5 → F-356 / EU-6): both datasets are folded into the EOW cadence bucket via the executor’s EOW Bronze+Silver phase (merged into
result.eow). They auto-ingest deterministically on the weekly deep run (Friday NIGHTLY_FULL, or an ad-hoc --research-mode nightly-full), where the ETF nightly seam rides force_weekly=True → bypass_run_days=True past the run_days:[sat] weekday gate — and past the min_age_days:6 floor with it, since that flag nulls the ingestion watermark (B-102.1; the earlier “min_age_days:6 still gates” was false). A deep run is an operator-requested weekly refresh, so forcing it is the intent; reconciling the two BronzeService cadence paths is Fix C, deferred. This corrects F-355’s wording — the seam previously used bypass_run_days=False and so was rejected on every scheduled Mon–Fri nightly; it now actually fires each Friday. Renders under the weekly group on the Data-hub inventory. The ingest-etf CLI remains the on-demand path.
- Tier-4: the live Bronze pull + constraint-7 field-casing verification + Silver promote is an operator step (TASK-2948).
- Documentation: FMP /stable/ ETF Info | FMP /stable/ ETF Holdings
Gold Layer Summary
Gold tables are built from Silver data by GoldDimService, GoldFactService, and GoldIndexService. No Silver table is modified during Gold promotion.
Dimension Tables
dim_date |
Static |
SQL bootstrap |
dim_instrument_type |
Static |
SQL bootstrap |
dim_country |
Static |
SQL bootstrap |
dim_exchange |
Static |
SQL bootstrap |
dim_sector |
Static |
SQL bootstrap |
dim_industry |
Static |
SQL bootstrap |
dim_index |
Static |
SQL bootstrap (SP500, NASDAQ100, DJIA) |
dim_instrument |
Data-derived |
company-profile-bulk + company-delisted |
dim_company |
Data-derived |
company-profile-bulk |
Fact Tables
fact_eod |
eod-bulk-price (+ _f feature columns computed by EodFeatureService / TechnicalFeatureService, incl. F-266 seasonality_same_month_f — same-calendar-month return seasonality, Heston-Sadka 2008) |
(instrument_sk, date_sk) |
fact_quarter |
quarterly income + balance sheet + cashflow + key metrics |
(instrument_sk, period_date_sk, period) |
fact_annual |
annual income + balance sheet + cashflow + key metrics + ratios |
(instrument_sk, period_date_sk) |
fact_stock_split |
stock-split |
(instrument_sk, split_date) |
fact_index_constituent |
sp500/nasdaq100/djia constituent history |
(index_sk, instrument_sk, effective_from) |
fact_analyst_estimates |
analyst-estimates (annual + quarter) |
(instrument_sk, fiscal_year, fiscal_quarter) |
fact_earnings_surprises |
earnings-surprises |
(instrument_sk, date_sk) |
fact_eps_forecast |
analyst-estimates + earnings-surprises |
(instrument_sk, fiscal_year, fiscal_quarter) |
fact_fundamental_annual |
annual fundamentals + market cap (built by FundamentalFactorService) |
(instrument_sk, fiscal_year) |
fact_signal_score |
_f predictors from fact_eod + fact_fundamental_annual (built by MLSignalService); one row per (instrument_sk, score_date_sk, model_id) so multiple algorithm/hyperparam variants coexist |
(instrument_sk, score_date_sk, model_id) |
fact_portfolio_target |
per-instrument target weights from a portfolio construction algorithm (built by PortfolioConstructionService); one row per (portfolio_id, instrument_sk, rebalance_date_sk) so multiple algorithm/hyperparam variants coexist |
(portfolio_id, instrument_sk, rebalance_date_sk) |
fact_analyst_grade |
grades (F-450) — lifecycle-aware ipo_date/delisting_date instrument join; grade_ordinal NULLABLE (unmapped vendor strings resolve to NULL, never coerced). Display / diagnostics only — never a backtest panel (see the EOW Analyst Grades section) |
(instrument_sk, date_sk, grading_company, new_grade) |
fact_screener |
month-end snapshot joining fact_eod price/technical features + fundamentals + composite_signal_s from fact_signal_score + target_weight_w from fact_portfolio_target |
(instrument_sk, rebalance_date_sk) |
Fundamentals knowledge-date dissemination lag (F-442 / LH-29, default OFF): the factor knowledge date_sk derived from the four reported-fundamentals tables — fact_fundamental_quarter, fact_fundamental_annual, fact_valuation_annual, fact_moat_annual — carries an optional dissemination lag (switch SB_FUNDAMENTALS_DISSEMINATION_LAG_ENABLED, sbfactors.settings; default OFF → byte-identical). When ON, the knowledge date is pushed to period-end + N calendar days (quarterly +60d / annual +90d) snapped forward to the first US market day, so factor values are stamped on-or-after the provable SEC filing date (closing the F-319 EDGAR PIT look-ahead). Announcement-grain / estimate / short-interest tables (fact_earnings_momentum, fact_eps_forecast, fact_short_interest) are excluded — already PIT-safe.
Event-dated factor forward-fill onto the daily grid (F-359): a factor may declare research.forward_fill_market_days: N (factor YAML research: block). When present, the lift emits daily-dense gold.fact_factor_value rows — carrying each event value forward from its knowable date_sk up to N market days, capped at the next event, carry-forward only (PIT-safe). sue_f declares 63 md (PEAD drift window, anchored on the earnings-announcement date), so sue_f is daily-dense in fact_factor_value while its source table fact_earnings_momentum stays event-grain (one row per announcement). earnings_growth_qoq_f also declares 63 md since TASK-3118 and is likewise daily-dense in fact_factor_value while its source fact_fundamental_quarter stays quarter-grain — but only because the dissemination lag above is enforced: the lift anchors the carry on the RESOLVED knowledge date_sk (first US market day on/after period-end + 60 calendar days), never the quarter-end period_date_sk, which is a period-END proxy ~40–60 days before the actual SEC filing. With the lag switch OFF the same declaration would carry each quarter’s value across the whole pre-filing window — a look-ahead wider than the pre-F-359 one. Factors without the key stay event-grain in fact_factor_value, byte-identical to the exact-date_sk lift.
Designed Earliest Date
The designed_earliest_date field on each dataset entry in config/dataset_keymap.yaml is the per-dataset commitment of how far back the platform aspires to ingest. Used by the Combined Coverage Heatmap (F-083) to draw the green per-row marker and to contextualize gaps in MIN(coverage_from) against design intent. null indicates a forward-looking or snapshot dataset where historical depth does not apply.
eod-bulk-price |
— |
1980-01-01 |
Matches platform FROM_DATE. |
eod-price-history |
— |
1980-01-01 |
Per-ticker delisted backfill. |
company-profile-bulk |
— |
— |
Snapshot. |
company-delisted |
— |
— |
Snapshot. |
income-statement-bulk-quarter |
— |
1985-01-01 |
Conservative per-symbol floor. |
balance-sheet-bulk-quarter |
— |
1985-01-01 |
|
cashflow-bulk-quarter |
— |
1985-01-01 |
|
key-metrics-bulk-quarter |
— |
1985-01-01 |
|
income-statement-bulk-annual |
— |
1985-01-01 |
|
balance-sheet-bulk-annual |
— |
1985-01-01 |
|
cashflow-bulk-annual |
— |
1985-01-01 |
|
key-metrics-bulk-annual |
— |
1985-01-01 |
|
ratios-bulk-annual |
— |
1985-01-01 |
|
sp500-constituent-history |
— |
— |
Change-log of current-as-of constituents. |
nasdaq100-constituent-history |
— |
— |
Change-log. |
djia-constituent-history |
— |
— |
Change-log. |
stock-split |
— |
1980-01-01 |
|
analyst-estimates |
annual |
— |
Forward-looking. |
analyst-estimates |
quarter |
— |
Forward-looking. |
earnings-surprises |
— |
1980-01-01 |
Pull-back to platform floor. |
revenue-product-segmentation |
FY |
— |
Vendor returns full available FY history per symbol. |
revenue-geographic-segmentation |
FY |
— |
Vendor returns full available FY history per symbol. |
market-risk-premium |
— |
— |
Snapshot. |
fred-dgs10 |
— |
1962-01-02 |
FRED series start. |
fred-usrecm |
— |
1900-01-01 |
Clamped to align with the pre-1960 bucket. |
fred-dgs1mo |
— |
2001-07-31 |
FRED series start. |
fred-dgs3mo |
— |
1982-01-04 |
FRED series start. |
fred-dgs1 |
— |
1962-01-02 |
FRED series start. |
fred-dgs2 |
— |
1976-06-01 |
FRED series start. |
fred-dgs5 |
— |
1962-01-02 |
FRED series start. |
fred-dgs30 |
— |
1977-02-15 |
FRED series start. |
fred-t10y2y |
— |
1976-06-01 |
FRED series start. |
fred-baa10y |
— |
1986-01-02 |
FRED series start. |
fred-vixcls |
— |
1990-01-02 |
FRED series start. |
fred-dexuseu … fred-dexhkus (19 daily DEX*) + fred-ccusma02ilm618n (ILS, monthly OECD) |
— |
1971-01-04 (DEX*) / 1957-01-01 (ILS) |
Conservative floor; FRED returns from each series’ actual start (TASK-3143; ILS is OECD monthly, task_6f964919). |
equity-short-interest |
— |
2007-01-01 |
FINRA reporting begins 2007. |
Vendor Earliest Date
The vendor_earliest_date field is the hard runtime floor on the dataset — the earliest period the upstream vendor actually serves data for. Distinct from designed_earliest_date (aspirational depth). Consumed by the F-082 quarter_ingest_schedule and annual_ingest_schedule prechecks (TASK-325) to clamp the history-extension window to max(FROM_DATE, vendor_earliest_date), so the schedule never asks FMP for a period it returns empty for.
Seeded from observed MIN(date) of each Silver table after multiple nightly runs. Update this field when the vendor extends coverage (lowering the floor) or when we learn of a stricter floor than what’s currently set. null (or omitted) means no known floor; the precheck falls back to FROM_DATE.
income-statement-bulk-quarter |
1984-12-31 |
silver_min on run 260513_7e0c61 |
balance-sheet-bulk-quarter |
1983-06-30 |
silver_min on run 260513_7e0c61 |
cashflow-bulk-quarter |
1985-06-30 |
silver_min on run 260513_7e0c61; FMP returned empty for all 4 quarters in 1984. |
key-metrics-bulk-quarter |
1983-06-30 |
silver_min on run 260513_7e0c61 |
income-statement-bulk-annual |
1983-12-31 |
FMP returned empty for year=1982 on run 260513_7e0c61. |
balance-sheet-bulk-annual |
1983-12-31 |
same |
cashflow-bulk-annual |
1983-12-31 |
same |
key-metrics-bulk-annual |
1983-12-31 |
same |
ratios-bulk-annual |
1983-12-31 |
same |
Per-Dataset Ingest Caps (F-102)
The max_date_quantity and max_symbol_quantity keymap fields (F-102) replace the four global INGEST_*_COUNT constants formerly in sbshared.settings. The planner (sbbronze.ingest_planner.IngestPlannerService) reads these caps to decide:
- DateList variant —
(ticker_scope=global, date_key set, max_date_quantity > 1). Planner returns up to max_date_quantity ISO dates to ingest.
- SnapshotGate variant —
(date_key=null OR max_date_quantity=null OR max_date_quantity=1). Planner returns a bool gate (ingest yes/no).
- SymbolList variant —
(ticker_scope=per_ticker). Planner returns up to max_symbol_quantity symbols (or all allowlisted when null).
eod-bulk-price |
— |
DateList |
20 |
— |
company-profile-bulk |
— |
Snapshot |
null |
— |
income-statement-bulk-quarter |
— |
DateList |
4 |
— |
balance-sheet-bulk-quarter |
— |
DateList |
4 |
— |
cashflow-bulk-quarter |
— |
DateList |
4 |
— |
income-statement-bulk-annual |
— |
DateList |
4 |
— |
balance-sheet-bulk-annual |
— |
DateList |
4 |
— |
cashflow-bulk-annual |
— |
DateList |
4 |
— |
key-metrics-bulk-quarter |
— |
DateList |
4 |
— |
key-metrics-bulk-annual |
— |
DateList |
4 |
— |
ratios-bulk-annual |
— |
DateList |
4 |
— |
fred-dgs10 |
— |
DateList |
8 |
— |
fred-usrecm |
— |
DateList |
8 |
— |
fred-dgs1mo |
— |
DateList |
8 |
— |
fred-dgs3mo |
— |
DateList |
8 |
— |
fred-dgs1 |
— |
DateList |
8 |
— |
fred-dgs2 |
— |
DateList |
8 |
— |
fred-dgs5 |
— |
DateList |
8 |
— |
fred-dgs30 |
— |
DateList |
8 |
— |
fred-t10y2y |
— |
DateList |
8 |
— |
fred-baa10y |
— |
DateList |
8 |
— |
fred-vixcls |
— |
DateList |
8 |
— |
fred-dexuseu … fred-dexhkus (19 daily DEX*) + fred-ccusma02ilm618n (ILS monthly) |
— |
DateList |
8 |
— |
market-risk-premium |
— |
Snapshot |
null |
— |
company-delisted |
— |
Snapshot |
null |
— |
mergers-acquisitions |
— |
Snapshot |
null |
— |
sp500-constituent-history |
— |
Snapshot |
null |
— |
nasdaq100-constituent-history |
— |
Snapshot |
null |
— |
djia-constituent-history |
— |
Snapshot |
null |
— |
stock-split |
— |
SymbolList |
null |
null |
eod-price-history |
— |
SymbolList |
null |
null |
analyst-estimates |
annual |
SymbolList |
null |
null |
analyst-estimates |
quarter |
SymbolList |
null |
null |
earnings-surprises |
— |
SymbolList |
null |
null |
grades |
— |
SymbolList |
null |
null |
revenue-product-segmentation |
FY |
SymbolList |
null |
null |
revenue-geographic-segmentation |
FY |
SymbolList |
null |
null |
equity-short-interest |
— |
DateList |
8 |
— |
When introducing a new dataset, set max_date_quantity per the variant intent. Defaults: 20 for daily EOD-style data, 8 for weekly cadences, 4 for quarterly/annual fundamentals, null for snapshots.
Self-healing gap-fill (O-093 / TASK-2812): the nightly bulk EOD ingest plan (eod-bulk-price) forward-fills up to max_date_quantity (20) missed market days per night, so a single run can absorb roughly a four-week outage. In the no-backlog steady state the plan is still just [today] (or [] on a non-market day); the cap only affects the saturated/outage path.
Quick Reference
| Total datasets (keymap entries) |
59 |
| DateList variant (F-102) |
42 |
| Snapshot variant (F-102) |
7 |
| SymbolList variant (F-102) |
10 |
| Global-scope datasets |
49 |
| Per-ticker datasets |
10 (stock-split, eod-price-history, analyst-estimates ×2, earnings-surprises, grades, etf-info, etf-holdings, revenue-product-segmentation, revenue-geographic-segmentation) |
| FMP datasets |
27 |
| FRED datasets |
31 |
| FINRA datasets |
1 |
| Daily refresh (eod) |
3 |
| Weekly refresh (Sat, eow) |
40 |
| Monthly refresh |
2 |
| Quarterly refresh |
2 |
| Yearly / cadence refresh |
2 |
| Gold fact tables |
13 (listed above; fact_short_interest + fact_revenue_segment also exist and are documented in their own dataset sections) |
| Gold dimension tables |
9 (7 static + 2 data-derived) |