Obscura

Money Market Fund Holdings

Every security a money-market fund held, month by month, from Form N-MFP: issuer, CUSIP/ISIN/LEI, value, weight, maturity, yield, and whether the position counts as daily-liquid, weekly-liquid or illiquid - plus repo collateral and NRSRO ratings where reported. Coverage begins 2016-05, matching edgar_mmf_filings.

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

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

What one row means

One **security held by a money-market fund**, as reported on one monthly SEC Form N-MFP: the issuer, its identifiers, the three maturity dates Rule 2a-7 cares about, the value including and excluding sponsor support, the percent of the fund's net assets, and the daily/weekly/illiquid classifications that decide whether the fund is compliant. # This is NOT `edgar_nport_holdings`, and the overlap measurement is why Both describe "a fund's position on a date", so the union was the obvious design. Read against real documents it collapses. Of `edgar_nport_holdings`' 24 data columns, an N-MFP position can fill **9 at best** - issuer name, title, CUSIP, ISIN, LEI, value, percent of net assets, a maturity date and a category - and two of those are semantic stretches: N-PORT's `asset_cat` is a short code vocabulary (`EC`, `DBT`) while N-MFP's `investmentCategory` is a Rule 2a-7 sentence (`"U.S. Treasury Repurchase Agreement, if collateralized only by U.S. Treasuries"`), and N-PORT reports ONE `maturityDt` while N-MFP reports THREE distinct maturities whose divergence is the entire point of the form. The other **15 N-PORT columns would be NULL on every N-MFP row** - `ticker`, `balance`, `units`, `cur_cd`, `payoff_profile`, `issuer_cat`, `inv_country`, `fair_val_level`, `is_restricted_sec`, the three securities-lending flags, `coupon_kind`, `annualized_rt`, `is_default` - because a money fund reports value rather than share count and does not report any of them. Running the other way, **~16 fields here have no N-PORT home at all**, four of which are nested one-to-many lists (repo collateral, NRSRO ratings, demand-feature providers, guarantors) that a flat holdings row cannot hold. A union table would be roughly 62 % NULL from both directions and would still lose the nested lists. # FOUR GENERATIONS, AND THE RENAMES ARE SILENT SEC rewrote this form's XML three times, and every rewrite renamed concepts rather than only adding them. Measured field-presence over 28 filings / 2,809 positions across the four vocabularies: | concept | `N-MFP` | `N-MFP1` | `N-MFP2` | `N-MFP3` | |---|---|---|---|---| | issuer | `InvestmentIssuer` | `nameOfIssuer` | `nameOfIssuer` | `nameOfIssuer` | | value | `InvestmentOwnedAtFairValue` | `includingValueOfAnySponsorSupport` | same | same | | pct of NAV | `InvestmentOwnedPercentOfNetAssets` | `percentageOfMoneyMarketFundNetAssets` | same | same | | illiquid | `isThisIlliquidSecurity` | `illiquidSecurityFlag` | same | same | | eligibility | `rating` (category) | `securityRated` (category) | `securityEligibilityFlag` (Y/N) | same | | NRSRO block | `designatedNRSROList` | `designatedNrsro` | `NRSRO` | `assigningNRSRORating` | A parser written to one spelling yields a column that is NULL for an entire era - silently, because NULL is legal. That is the `edgar_abs_assets` defect class, and the defence here is the same: an explicit per-generation alias table plus a parse-time check that every position carries a value and a percent. **The generation is read from the DOCUMENT, never from the date.** The four vocabularies overlap inside single quarters at both transitions - 2016 QTR2 carries 546 `N-MFP` and 1,039 `N-MFP1`, 2016 QTR4 carries 484 `N-MFP1` and 935 `N-MFP2` - and amendments cross eras freely. See `docs/datasets/edgar_mmf_holdings.md`.

One row per One SECURITY POSITION in one SERIES in one REPORT MONTH, keyed by (filename, seq). seq is the 0-based index of the position in the submission's schedule, assigned by the parser via .enumerate() and NOT sourced from the filing - N-MFP gives a position no identifier of its own, and CUSIP is absent on up to 8 % of rows so it cannot serve as the key. NOT one security: the same CUSIP reappears every month for as long as the fund holds it, which is what makes this a monthly panel. The analytic key is (series_id, cusip, occurred_on) and it is indexed, but it is not the identity..

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_mmf_holdings: filing_join: available_date is the EDGAR DISSEMINATION day of the parent N-MFP submission, taken from the bronze object's own day partition (edgar_docs::available_date), never from FILED AS OF DATE. WHY: original N-MFP filings were embargoed 60 days under the original Rule 30b1-7 and are absent from every daily index probed between 2011-07-12 and 2016-04-06, while the quarterly full index attributes thousands of them to those dates; the embargo lifted with N-MFP1, whose originals appear same-day from 2016-05-06. occurred_on is the report month end the position is held AS OF, and is always earlier than availability. Evidence class B; see docs/availability/edgar_mmf_holdings.md.

Refresh cadence

Obscura refreshes edgar_mmf_holdings 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 - 39 columns

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

ColumnTypeDescription
filenametext · not nullPK part 1. EDGAR archive path of the N-MFP submission this position came from (`edgar/data/{cik}/{accession}.txt`); the join back to `edgar_mmf_filings` and to `edgar_filings`.
seqinteger · not nullPK part 2. 0-based index of this position within the submission's schedule of portfolio securities, assigned by the parser via `.enumerate()`. It is NOT a source field - N-MFP gives a position no identifier, and CUSIP is missing on up to 8 % of rows.
reg_cikbigintRegistrant (filer) CIK, denormalized from the parent filing for joins.
series_idtextSEC series identifier of the holding money-market fund (`S000011990`), denormalized from the parent filing. The fund side of every join.
available_datedateThe parent filing's EDGAR dissemination date - when the public could first see this position. Denormalized so a point-in-time query needs no join. The point-in-time anchor.
occurred_ondateThe report month end this position is held AS OF (`reportDate`). The real-world event date; always earlier than `available_date`, and up to 60 days earlier on the pre-2016-05 embargoed generation.
generationtext · not nullWhich N-MFP XML vocabulary this row was parsed from - `N-MFP`, `N-MFP1`, `N-MFP2` or `N-MFP3`. Denormalized from the parent filing because it is what says which source tag fed each column below, and which columns are legitimately NULL for the era rather than missing.
issuer_nametextIssuer name (`nameOfIssuer`, gen-1 `InvestmentIssuer`), e.g. `Australia & New Zealand Banking Group Ltd.`.
titletextSecurity title as filed (`titleOfIssuer`, gen-1 `InvestmentTitle`), normally the issuer plus the coupon, e.g. `Australia & New Zealand Banking Group Ltd. 3.100000%`.
cusiptextSecurity CUSIP (`CUSIPMember`). Present on 92-100 % of positions depending on generation; indexed.
isintextSecurity ISIN (`ISINId`). Introduced with N-MFP1; absent on gen-1, and present on ~73 % of modern positions.
leitextIssuer Legal Entity Identifier (`LEIID`). Introduced with N-MFP1 at ~25 % coverage, reaching 100 % on N-MFP3.
other_unique_idtextA filer-supplied identifier used when the security has no CUSIP, ISIN or LEI (`otherUniqueId`, e.g. `05599Y002_3.00_0102`). Free-form and NOT resolvable to a security master - stored so the position is still traceable, never as a substitute identifier.
issuer_cikbigintSEC CIK of the ISSUER where the filer supplied one (`cik` inside the position block, gen-1 `EntityCentralIndexKey`). Present on 4-40 % of positions; the cleanest join from a money-fund position to an EDGAR filer.
investment_categorytextRule 2a-7 investment category exactly as filed - `U.S. Treasury Debt`, `Variable Rate Demand Note`, `U.S. Treasury Repurchase Agreement, if collateralized only by U.S. Treasuries`, … From `investmentCategory` (gen 2-4) or gen-1 `InvestmentTypeDomain`. Stored verbatim and never normalised: the category vocabulary itself changed with the 2014 reform, and `generation` is what says which vocabulary the string belongs to.
maturity_date_wamdateMaturity date used for the WEIGHTED-AVERAGE MATURITY calculation (`investmentMaturityDateWAM`, gen-1 `InvestmentMaturityDate`). For a floating-rate note this is the next RATE RESET, not the final maturity - which is why it is a different column from the two below.
maturity_date_waldateMaturity date used for the WEIGHTED-AVERAGE LIFE calculation (`investmentMaturityDateWAL`). Ignores interest-rate resets but honours demand features. NULL on gen-1, which does not report a WAL date.
final_legal_maturity_datedateThe security's FINAL LEGAL maturity date (`finalLegalInvestmentMaturityDate`) - when principal is contractually due, ignoring every reset and demand feature. The spread between this and `maturity_date_wam` is the fund's hidden duration.
yield_pctdouble precisionThe security's annualised yield at the reporting date (`yieldOfTheSecurityAsOfReportingDate`) as a decimal fraction exactly as filed (`0.0314` = 3.14 %), never rescaled. Not reported on gen-1.
coupondouble precisionThe security's stated coupon rate as filed. Introduced with N-MFP3 (present on ~97 % of its positions); NULL on all earlier generations, where the coupon is only recoverable by parsing it out of `title`.
principal_amountdouble precisionPrincipal amount held, to the nearest cent (`InvestmentOwnedBalancePrincipalAmount`). **Gen-1 only** - N-MFP1 onwards stopped reporting a position's principal or share count entirely and report only value, which is the single largest information loss across the four generations.
amortized_costdouble precisionAmortized cost of this position (gen-1 `AvailableForSaleSecuritiesAmortizedCost`). Gen-1 only; later generations report amortized cost only at the series level.
value_including_sponsor_supportdouble precisionValue of the position INCLUDING any sponsor/capital support (`includingValueOfAnySponsorSupport`, gen-1 `InvestmentOwnedAtFairValue`). The headline value.
value_excluding_sponsor_supportdouble precisionValue of the position EXCLUDING any sponsor/capital support (`excludingValueOfAnySponsorSupport`, gen-1 `valueOfSecurityExcludingValueOfCapitalSupportAgreement`). Where this is below the including-support value, the sponsor is propping up the position - the fact the pair of columns exists to expose.
pct_of_net_assetsdouble precisionPosition as a fraction of the fund's net assets (`percentageOfMoneyMarketFundNetAssets`, gen-1 `InvestmentOwnedPercentOfNetAssets`) exactly as filed (`0.0883` = 8.83 %), never rescaled.
eligibility_categorytextRule 2a-7 eligibility CATEGORY as a string - `First Tier Security` / `Second Tier Security`. From gen-1 `rating` and N-MFP1 `securityRated` ONLY. The tier vocabulary was abolished by the 2014 reform, which replaced it with the boolean below; the two are a genuine regime break and are never collapsed into one column.
is_eligible_securitybooleanWhether the security is an eligible security under Rule 2a-7 (`securityEligibilityFlag`, Y/N). N-MFP2 and N-MFP3 only - it is the post-2014-reform replacement for `eligibility_category`, not a synonym.
has_demand_featurebooleanWhether the security carries a demand feature (`securityDemandFeatureFlag`, gen-1 `doesSecurityHaveDemandFeature`) - a put that shortens its effective maturity.
has_guaranteebooleanWhether the security carries a guarantee (`securityGuaranteeFlag`, gen-1 `doesSecurityHaveGuarantee`).
has_enhancementsbooleanWhether the fund relies on credit enhancements for this security (`securityEnhancementsFlag`).
is_daily_liquidbooleanWhether the position counts toward the fund's DAILY liquid assets (`dailyLiquidAssetSecurityFlag`). Not reported on gen-1. Present on ~69 % of N-MFP2 positions - a genuinely optional tag there, not a parse gap.
is_weekly_liquidbooleanWhether the position counts toward the fund's WEEKLY liquid assets (`weeklyLiquidAssetSecurityFlag`). The 30 % Rule 2a-7 constraint is computed over these.
is_illiquidbooleanWhether the fund classifies the position as illiquid (`illiquidSecurityFlag`, gen-1 `isThisIlliquidSecurity`).
is_level3booleanWhether the position is carried at ASC 820 fair-value level 3 (`securityCategorizedAtLevel3Flag`) - unobservable inputs, i.e. a mark nobody can verify. Not reported on gen-1. Note this is a BOOLEAN, unlike N-PORT's three-valued `fair_val_level`, which is why the two datasets do not share a column.
treats_as_acquisition_of_underlyingbooleanWhether the fund treats this as an acquisition of the securities underlying a repo (`fundAcqstnUndrlyngSecurityFlag`, gen-1 `isFundTreatingAsAcquisitionUnderlyingSecurities`) - i.e. looks through the repo to its collateral for diversification purposes.
is_repurchase_agreementboolean · not nullWhether this position is a repurchase agreement at all - true when the source carried a `repurchaseAgreement` block (gen-1 `repurchaseAgreementList`). Stored as its own flag so a repo can be counted without inspecting the JSON below.
is_open_repurchase_agreementbooleanWhether the repo is an OPEN (no stated maturity) repo (`repurchaseAgreementOpenFlag`). NULL when the position is not a repo.
repo_collateraljsonb · not nullThe repo's collateral, one JSON object per `collateralIssuers` block: issuer name, maturity date, coupon, principal amount, value and the collateral category. A nested one-to-many with no flat home - and the only place the fund's ACTUAL credit exposure is stated, since the repo counterparty is what `issuer_name` names. Empty array when not a repo.
nrsro_ratingsjsonb · not nullNRSRO credit ratings assigned to the security, one JSON object per rating block: the rating agency and the rating. A nested one-to-many. The source element is renamed in EVERY generation (`designatedNRSROList` → `designatedNrsro` → `NRSRO` → `assigningNRSRORating`), which is exactly the silent-rename trap this table's alias map exists to defeat. Empty array when unrated.

Access edgar_mmf_holdings

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_mmf_holdings",
    symbols=["NVDA", "AAPL"],
    start="2024-01-01",
)

Create a free account Browse all 122 datasets

Frequently asked questions

What is in the edgar_mmf_holdings dataset?

Every security a money-market fund held, month by month, from Form N-MFP: issuer, CUSIP/ISIN/LEI, value, weight, maturity, yield, and whether the position counts as daily-liquid, weekly-liquid or illiquid - plus repo collateral and NRSRO ratings where reported. Coverage begins 2016-05, matching edgar_mmf_filings. One **security held by a money-market fund**, as reported on one monthly SEC Form N-MFP: the issuer, its identifiers, the three maturity dates Rule 2a-7 cares about, the value including and excluding sponsor support, the percent of the fund's net assets, and the daily/weekly/illiquid classifications that decide whether the fund is compliant. # This is NOT `edgar_nport_holdings`, and the overlap measurement is why Both describe "a fund's position on a date", so the union was the obvious design. Read against real documents it collapses. Of `edgar_nport_holdings`' 24 data columns, an N-MFP position can fill **9 at best** - issuer name, title, CUSIP, ISIN, LEI, value, percent of net assets, a maturity date and a category - and two of those are semantic stretches: N-PORT's `asset_cat` is a short code vocabulary (`EC`, `DBT`) while N-MFP's `investmentCategory` is a Rule 2a-7 sentence (`"U.S. Treasury Repurchase Agreement, if collateralized only by U.S. Treasuries"`), and N-PORT reports ONE `maturityDt` while N-MFP reports THREE distinct maturities whose divergence is the entire point of the form. The other **15 N-PORT columns would be NULL on every N-MFP row** - `ticker`, `balance`, `units`, `cur_cd`, `payoff_profile`, `issuer_cat`, `inv_country`, `fair_val_level`, `is_restricted_sec`, the three securities-lending flags, `coupon_kind`, `annualized_rt`, `is_default` - because a money fund reports value rather than share count and does not report any of them. Running the other way, **~16 fields here have no N-PORT home at all**, four of which are nested one-to-many lists (repo collateral, NRSRO ratings, demand-feature providers, guarantors) that a flat holdings row cannot hold. A union table would be roughly 62 % NULL from both directions and would still lose the nested lists. # FOUR GENERATIONS, AND THE RENAMES ARE SILENT SEC rewrote this form's XML three times, and every rewrite renamed concepts rather than only adding them. Measured field-presence over 28 filings / 2,809 positions across the four vocabularies: | concept | `N-MFP` | `N-MFP1` | `N-MFP2` | `N-MFP3` | |---|---|---|---|---| | issuer | `InvestmentIssuer` | `nameOfIssuer` | `nameOfIssuer` | `nameOfIssuer` | | value | `InvestmentOwnedAtFairValue` | `includingValueOfAnySponsorSupport` | same | same | | pct of NAV | `InvestmentOwnedPercentOfNetAssets` | `percentageOfMoneyMarketFundNetAssets` | same | same | | illiquid | `isThisIlliquidSecurity` | `illiquidSecurityFlag` | same | same | | eligibility | `rating` (category) | `securityRated` (category) | `securityEligibilityFlag` (Y/N) | same | | NRSRO block | `designatedNRSROList` | `designatedNrsro` | `NRSRO` | `assigningNRSRORating` | A parser written to one spelling yields a column that is NULL for an entire era - silently, because NULL is legal. That is the `edgar_abs_assets` defect class, and the defence here is the same: an explicit per-generation alias table plus a parse-time check that every position carries a value and a percent. **The generation is read from the DOCUMENT, never from the date.** The four vocabularies overlap inside single quarters at both transitions - 2016 QTR2 carries 546 `N-MFP` and 1,039 `N-MFP1`, 2016 QTR4 carries 484 `N-MFP1` and 935 `N-MFP2` - and amendments cross eras freely. See `docs/datasets/edgar_mmf_holdings.md`.

How do I avoid look-ahead bias with edgar_mmf_holdings?

Filter on edgar_mmf_holdings.available_date, the day the publisher made the row public. For this dataset that date is derived as follows - filing_join: available_date is the EDGAR DISSEMINATION day of the parent N-MFP submission, taken from the bronze object's own day partition (edgar_docs::available_date), never from FILED AS OF DATE. WHY: original N-MFP filings were embargoed 60 days under the original Rule 30b1-7 and are absent from every daily index probed between 2011-07-12 and 2016-04-06, while the quarterly full index attributes thousands of them to those dates; the embargo lifted with N-MFP1, whose originals appear same-day from 2016-05-06. occurred_on is the report month end the position is held AS OF, and is always earlier than availability. Evidence class B; see docs/availability/edgar_mmf_holdings.md. 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_mmf_holdings?

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_mmf_holdings". The column schema is public at https://api.obscura.trade/v1/catalog/edgar_mmf_holdings.

Can I filter edgar_mmf_holdings by company or symbol?

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

How often is edgar_mmf_holdings updated?

Obscura refreshes edgar_mmf_holdings 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…