Obscura

Fund Fees, Expenses and Return History

Fee tables, expense examples and standardised return histories for US registered funds, taken from the XBRL riding on their prospectus filings: management fee, 12b-1 fee, other expenses, gross and net expense ratio, sales loads, portfolio turnover, and the 1-, 5- and 10-year average annual returns including the after-tax and benchmark rows. One row per share class per filing.

edgar_fund_risk_return - the dataset name to pass to the Obscura API.

Category Regulatory Columns 51 Refresh Every 6 hours Entity key series_id Point-in-time available_date

What one row means

One **share class's fee table and standardised return history** as a US registered fund states it in one prospectus filing - the Risk/Return (`rr:`) inline-XBRL facts riding on a `485BPOS` or a `497`. Management fee, 12b-1 fee, other expenses, acquired-fund fees, gross and net expense ratio, the contractual waiver, sales loads, the SEC's $10,000 expense example at four horizons, the annual-return bar chart, best/worst quarter, and the 1/5/10-year and since-inception average annual returns including the two after-tax rows. Per share class, per filing, dated. **This is what makes `485BPOS` worth claiming.** The form's PROSE is 863 KB per filing → 62 GB across the corpus, past the exporter's practical ceiling and analytically almost worthless. The XBRL riding on the same filing is a few hundred facts. No prospectus text is stored on this table, and none ever should be: ~35 of the instance's 64 `rr:` tags are `…TextBlock` / `…Heading` / `…Narrative` prose containers (`RiskTextBlock` occurs 227 times in one sampled instance) and every one of them is dropped. # SERIES-LEVEL FACTS ARE NOT COPIED ONTO CLASS ROWS `PortfolioTurnoverRate`, `RiskReturnHeading` and the benchmark returns carry only the series dimension - MEASURED on both sampled instances. They live on a **class-less row** (`class_id = ''`), never fanned across the classes. Copying one portfolio-turnover figure onto four class rows would look like four measurements of the same thing, and averaging them would be wrong. The migration CHECKs it rather than trusting the writer. # Not every prospectus carries XBRL, and `497K` never does A `497K` summary prospectus carries no `rr:` facts at all (measured: zero on `0001193125-24-131423`), and a `497` may or may not - `0001137439-24-001011` (26 KB) has none, `0001213900-24-039977` (6.7 MB) has 3 series and 6 classes of them. A filing with no instance produces **zero rows, not null rows**, and is recorded done in the shared `edgar_doc_processed` marker so it is never re-examined. See `docs/datasets/edgar_fund_risk_return.md`.

One row per One row per (filing, series, share class) Risk/Return fact set, keyed by (accession, series_id, class_id). class_id is the EMPTY STRING on the SERIES-LEVEL row, which is where the facts whose XBRL context names only dei:LegalEntityAxis live - portfolio turnover, the risk/return heading and the benchmark returns. Facts whose context also names rr:ProspectusShareClassAxis live on that class's row. A combined prospectus covering 30 funds produces 30 series rows plus one row per share class. The key is the ACCESSION and not the archive path, for the same per-CIK fan-out reason as edgar_fund_series_classes..

Point-in-time availability

Every Obscura dataset carries available_date: the calendar day the publisher made the row available, day-of, with no session rounding. It is the one column a backtest filters on, and it means the same thing on every dataset in the catalog.

For edgar_fund_risk_return: filing_join_occurred: available_date is the EDGAR DISSEMINATION day from the bronze object's own day partition; occurred_on is rr:ProspectusDate, the day the prospectus and therefore this fee schedule TAKES EFFECT. It is frequently in the FUTURE relative to publication - T. Rowe Price 0001741773-24-001700 was disseminated 2024-04-25 and its rr:ProspectusDate is 2024-05-01 - so occurred_is_forecast carries that and the usual occurred_on <= available_date monotonicity does not apply to those rows. Both dates are stated by the publisher (one in the daily index, one as a tagged fact), so both are hard: evidence class A + B, CONFIRMED.

Refresh cadence

Obscura refreshes edgar_fund_risk_return every 6 hours - the most frequent scheduled job that re-collects or re-exports it. This is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when a row became public is recorded per row in available_date.

Schema - 51 columns

The full public column list for edgar_fund_risk_return, with the meaning of every field. The same schema is served unauthenticated at https://api.obscura.trade/v1/catalog/edgar_fund_risk_return.

ColumnTypeDescription
accessiontext · not nullPK part. SEC accession number of the prospectus filing, e.g. `0001741773-24-001700`.
series_idtext · not nullPK part. SEC series identifier from the context's `dei:LegalEntityAxis` member (`cik0001002624:S000002088Member` → `S000002088`). The namespace prefix is the FILER's own, so the identifier is extracted from the member rather than assumed to sit under a fixed prefix.
class_idtext · not nullPK part. SEC class identifier from the context's `rr:ProspectusShareClassAxis` member. The EMPTY STRING (never NULL) on the SERIES-LEVEL row that holds the facts stated without a class dimension.
filenametext · not nullThe registrant's canonical SEC EDGAR Archives path (`edgar/data/{cik}/{accession}.txt`), derived from the payload rather than from whichever index row was walked. The join key into `edgar_filings`.
available_datedatePublic availability date: the EDGAR DISSEMINATION day, from the bronze object's own day partition. The point-in-time anchor and the only date a backtest may key on.
occurred_ondate**The prospectus's own effective date** (`rr:ProspectusDate`) - the day these fees and disclosures take effect. Stated once per filing, on the dimensionless default context. Often AFTER `available_date`: 2024-05-01 on a filing disseminated 2024-04-25.
occurred_is_forecastboolean · not nullTRUE exactly when `occurred_on` has not yet arrived (`occurred_on > available_date`), so monotonicity checks skip the row. The migration CHECKs the biconditional rather than trusting the writer.
form_typetext · not nullThe submission's form type - `485BPOS` or `497`. `497K` never carries Risk/Return XBRL and so never produces a row here.
cikbigintRegistrant CIK, read from the XBRL context's own `<identifier scheme=".../CIK">` rather than from the SGML header, so it is the entity the FACTS are filed under.
risk_return_headingtextThe fund's own heading (`rr:RiskReturnHeading`), e.g. `Elfun Trusts Ticker Symbol: ELFNX`. SERIES-LEVEL - it frequently carries the ticker inline and is an independent cross-check on `edgar_fund_series_classes`.
portfolio_turnovernumericAnnual portfolio turnover (`rr:PortfolioTurnoverRate`); `0.484` = 48.4 %. SERIES-LEVEL - measured stated against the series context on both sampled instances, so it sits on the class-less row and is never copied onto the classes.
benchmark_returnsjsonb · not nullSERIES-LEVEL. JSON array of the benchmarks the fund reports against, one entry per `rr:PerformanceMeasureAxis` member that is not one of the two standard after-tax members: `{"members": [...], "name": "S&P 500 Index", "return_1y": …, "return_5y": …, "return_10y": …, "return_since_inception": …}`. An array because funds report one, two or three benchmarks and the set is not fixed. **`members` is retained so the merge is auditable**: filers routinely split ONE benchmark across TWO members, one carrying `rr:AverageAnnualReturnLabel` and the other the returns (`SP500Index1Member` / `SP500Index2Member` on `0001741773-24-001700`), and the two are joined only when their digit-stripped stems match AND exactly one of the group carries a label and one carries returns.
management_feenumericManagement fee as a fraction of assets (`rr:ManagementFeesOverAssets`); `0.0064` = 64 bp.
distribution_12b1_feenumericRule 12b-1 distribution/service fee (`rr:DistributionAndService12b1FeesOverAssets`).
other_expensesnumericOther annual operating expenses (`rr:OtherExpensesOverAssets`).
acquired_fund_feesnumericAcquired fund fees and expenses (`rr:AcquiredFundFeesAndExpensesOverAssets`) - the fees of underlying funds a fund-of-funds holds, which are inside the gross ratio but not inside the management fee.
gross_expense_rationumeric**Total annual fund operating expenses BEFORE waivers** (`rr:ExpensesOverAssets`); `0.0080` = 80 bp.
net_expense_rationumericExpenses AFTER fee waivers and reimbursements (`rr:NetExpensesOverAssets`). NULL when there is no waiver - the tag is simply absent, and NULL is not the same claim as "equal to gross".
fee_waivernumericThe contractual waiver/reimbursement (`rr:FeeWaiverOrReimbursementOverAssets`), as a fraction of assets.
max_front_loadnumericMaximum front-end sales load as a fraction of the offering price (`rr:MaximumSalesChargeImposedOnPurchasesOverOfferingPrice`). NULL on a no-load class, which is a fact and not a gap.
max_deferred_loadnumericMaximum contingent deferred sales charge. Two different tags state it against two different DENOMINATORS, so the value carries its basis instead of being silently coalesced - see `max_deferred_load_basis`.
max_deferred_load_basistextWHICH tag supplied `max_deferred_load`: `offering_price` (`rr:MaximumDeferredSalesChargeOverOfferingPrice`) or `other` (`rr:MaximumDeferredSalesChargeOverOther`, i.e. against purchase price or redemption proceeds). Recorded because the two are different measures and a silent precedence rule would be unauditable. NULL exactly when `max_deferred_load` is NULL - the migration CHECKs the biconditional.
max_account_feenumericMaximum annual account fee in currency units (`rr:MaximumAccountFee`), e.g. `20`.
expense_example_1ynumericSEC's standard $10,000 expense example at 1 year, in dollars (`rr:ExpenseExampleYear01`).
expense_example_3ynumericThe same example at 3 years (`rr:ExpenseExampleYear03`).
expense_example_5ynumericThe same example at 5 years (`rr:ExpenseExampleYear05`).
expense_example_10ynumericThe same example at 10 years (`rr:ExpenseExampleYear10`).
expense_example_no_redemption_1ynumericThe expense example assuming NO redemption at 1 year (`rr:ExpenseExampleNoRedemptionYear01`). Differs from the redeeming example only where a deferred load applies, which is why both are kept.
expense_example_no_redemption_3ynumericNo-redemption expense example at 3 years.
expense_example_no_redemption_5ynumericNo-redemption expense example at 5 years.
expense_example_no_redemption_10ynumericNo-redemption expense example at 10 years.
annual_returnsjsonb · not nullThe bar chart, as `{"2014": 0.3194, …, "2023": 0.0308}` from the `rr:AnnualReturnYYYY` tags. A JSON map rather than ten columns because the ten years slide forward every January and fixed columns would need a migration a year. Empty object when the class states no bar chart (a class younger than a full calendar year).
best_quarter_returnnumericBest quarterly return in the bar-chart window (`rr:BarChartHighestQuarterlyReturn`).
best_quarter_enddateThe quarter-end date of `best_quarter_return` (`rr:BarChartHighestQuarterlyReturnDate`), e.g. 2020-06-30.
worst_quarter_returnnumericWorst quarterly return in the bar-chart window (`rr:BarChartLowestQuarterlyReturn`).
worst_quarter_enddateThe quarter-end date of `worst_quarter_return` (`rr:BarChartLowestQuarterlyReturnDate`).
ytd_returnnumericYear-to-date return quoted beside the bar chart (`rr:BarChartYearToDateReturn`), when the class states one.
ytd_return_throughdateThe as-of date of `ytd_return` (`rr:BarChartYearToDateReturnDate`).
return_1ynumericStandardised average annual total return over 1 year, BEFORE tax (`rr:AverageAnnualReturnYear01` on the class's own context, with no `rr:PerformanceMeasureAxis`).
return_5ynumericStandardised average annual total return over 5 years, before tax.
return_10ynumericStandardised average annual total return over 10 years, before tax.
return_since_inceptionnumericStandardised average annual total return since inception, before tax (`rr:AverageAnnualReturnSinceInception`). The row a class younger than 10 years reports in place of the 10-year figure.
return_1y_after_taxnumeric1-year return AFTER TAXES ON DISTRIBUTIONS - the same tag under `rr:PerformanceMeasureAxis = rr:AfterTaxesOnDistributionsMember`, which MEASURED carries the share-class dimension too, so it belongs to this class and not to the series.
return_5y_after_taxnumeric5-year return after taxes on distributions.
return_10y_after_taxnumeric10-year return after taxes on distributions.
return_since_inception_after_taxnumericSince-inception return after taxes on distributions.
return_1y_after_tax_salenumeric1-year return AFTER TAXES ON DISTRIBUTIONS AND SALE of shares (`rr:AfterTaxesOnDistributionsAndSalesMember`).
return_5y_after_tax_salenumeric5-year return after taxes on distributions and sale.
return_10y_after_tax_salenumeric10-year return after taxes on distributions and sale.
return_since_inception_after_tax_salenumericSince-inception return after taxes on distributions and sale.
inception_datedateShare-class inception date as tagged (`rr:AverageAnnualReturnInceptionDate`), e.g. 1995-12-29. The denominator of every since-inception figure on the row.

Access edgar_fund_risk_return

Two delivery paths, one identifier. Both require an Obscura account and an active subscription; the catalog entry and the schema above are public.

import obscura

client = obscura.Client("obs_live_…")

df = client.query(
    dataset="edgar_fund_risk_return",
    symbols=["NVDA", "AAPL"],
    start="2024-01-01",
)

Create a free account Browse all 122 datasets

Frequently asked questions

What is in the edgar_fund_risk_return dataset?

Fee tables, expense examples and standardised return histories for US registered funds, taken from the XBRL riding on their prospectus filings: management fee, 12b-1 fee, other expenses, gross and net expense ratio, sales loads, portfolio turnover, and the 1-, 5- and 10-year average annual returns including the after-tax and benchmark rows. One row per share class per filing. One **share class's fee table and standardised return history** as a US registered fund states it in one prospectus filing - the Risk/Return (`rr:`) inline-XBRL facts riding on a `485BPOS` or a `497`. Management fee, 12b-1 fee, other expenses, acquired-fund fees, gross and net expense ratio, the contractual waiver, sales loads, the SEC's $10,000 expense example at four horizons, the annual-return bar chart, best/worst quarter, and the 1/5/10-year and since-inception average annual returns including the two after-tax rows. Per share class, per filing, dated. **This is what makes `485BPOS` worth claiming.** The form's PROSE is 863 KB per filing → 62 GB across the corpus, past the exporter's practical ceiling and analytically almost worthless. The XBRL riding on the same filing is a few hundred facts. No prospectus text is stored on this table, and none ever should be: ~35 of the instance's 64 `rr:` tags are `…TextBlock` / `…Heading` / `…Narrative` prose containers (`RiskTextBlock` occurs 227 times in one sampled instance) and every one of them is dropped. # SERIES-LEVEL FACTS ARE NOT COPIED ONTO CLASS ROWS `PortfolioTurnoverRate`, `RiskReturnHeading` and the benchmark returns carry only the series dimension - MEASURED on both sampled instances. They live on a **class-less row** (`class_id = ''`), never fanned across the classes. Copying one portfolio-turnover figure onto four class rows would look like four measurements of the same thing, and averaging them would be wrong. The migration CHECKs it rather than trusting the writer. # Not every prospectus carries XBRL, and `497K` never does A `497K` summary prospectus carries no `rr:` facts at all (measured: zero on `0001193125-24-131423`), and a `497` may or may not - `0001137439-24-001011` (26 KB) has none, `0001213900-24-039977` (6.7 MB) has 3 series and 6 classes of them. A filing with no instance produces **zero rows, not null rows**, and is recorded done in the shared `edgar_doc_processed` marker so it is never re-examined. See `docs/datasets/edgar_fund_risk_return.md`.

How do I avoid look-ahead bias with edgar_fund_risk_return?

Filter on edgar_fund_risk_return.available_date, the day the publisher made the row public. For this dataset that date is derived as follows - filing_join_occurred: available_date is the EDGAR DISSEMINATION day from the bronze object's own day partition; occurred_on is rr:ProspectusDate, the day the prospectus and therefore this fee schedule TAKES EFFECT. It is frequently in the FUTURE relative to publication - T. Rowe Price 0001741773-24-001700 was disseminated 2024-04-25 and its rr:ProspectusDate is 2024-05-01 - so occurred_is_forecast carries that and the usual occurred_on <= available_date monotonicity does not apply to those rows. Both dates are stated by the publisher (one in the daily index, one as a tagged fact), so both are hard: evidence class A + B, CONFIRMED. A query of the form WHERE available_date <= '<as-of date>' never sees a row before it existed.

In what formats can I get edgar_fund_risk_return?

As a Parquet bulk export (POST https://api.obscura.trade/v1/download) or as JSON from the typed query API (POST https://api.obscura.trade/v1/query), both with dataset="edgar_fund_risk_return". The column schema is public at https://api.obscura.trade/v1/catalog/edgar_fund_risk_return.

Can I filter edgar_fund_risk_return by company or symbol?

Yes. edgar_fund_risk_return carries series_id, the column the API's symbols filter resolves against.

How often is edgar_fund_risk_return updated?

Obscura refreshes edgar_fund_risk_return on a every 6 hours schedule - that is the most frequent scheduled job that re-collects or re-exports the table. It is Obscura's own pipeline cadence, not the upstream publisher's release schedule; when the publisher makes a row available is described by the availability rule above, and is recorded per row in available_date.

Related datasets

CFTC Commitments of Traders (COT)The CFTC's weekly Commitments of Traders report: how commercial, non-commercial and small traders are positioned in each… EPA Environmental Enforcement ActionsEnvironmental enforcement actions and penalties from the EPA's ECHO database. One row per case, with the facility, the s… EPA Regulated Facilities and ComplianceEPA-regulated facilities and their compliance history, from ECHO: one row per facility, with location, industry, permit … 13F Filing MetadataMetadata for every 13F report, the quarterly filing where large institutional managers list their U.S. equity holdings. … 13F Institutional HoldingsThe line-item positions inside each 13F: which manager held which security, and how much, each quarter. Deep history is … 13F Other Included ManagersWhich other manager reports a fund's 13F holdings, from the 13F-NT notices managers file when their positions appear on … SEC 8-K Item Codes and Material EventsThe item numbers reported on each 8-K (5.02 officer changes, 2.01 acquisitions, 1.01 material agreements, and so on), so… Asset-Backed Securities Distributions (Form 10-D)Form 10-D distribution reports for asset-backed securities: which trust paid out, for which distribution period, its dep…