Files
OpenBB/openbb_platform/providers/sec/openbb_sec/utils/nmfp.py
Danglewood dc4b26030f [V5] openbb-sec: Refactor for V5 (#7525)
* refactor openbb-sec for v5

* linting

* ty

* normalize compare_company_facts

* add workflows

* python 3.10 fix

* add more thorough url validation and tests

* ty check

* ty for python 3.10

* reconfigure with apps

* temp pyproject overrride

* pyproject

* pyproject

* pyproject

* codespell

* pyproject

* fix uk schema file links in schema_files

* test coverage

* test_cli

* port #7547 into this pr

* no hard coded prefix

* improve filing rendering capabilities

* fix xml text detection

* make codeql happy

* missed a file in the commit

* test coverage

* codespell

* fix flakey test

* some updates

* management_ownership --> management_profiles

* proxy statement markdown

* ruff

* fix test coverage

* more tests

* test coverage

* fix lint errors

* Add SEC adviser search, profile, and universe routes (#7559)

* Add SEC institutional data fetchers

* Add SEC filing index and Form D routes

* Add SEC investment adviser people data

* Expose SEC adviser and institutional routes

* fix(sec): address adviser review feedback

* refactor(sec): narrow adviser support to IAPD search

* feat(sec): add adviser firm and individual searches

* feat(sec): add adviser profile drill-down

* fix: repair pre-commit configuration

* feat(sec): add adviser universe route

* feat(sec): expose complete Form ADV data

---------

Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>

* fix test coverage

---------

Co-authored-by: deeleeramone <>
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
2026-09-24 08:47:49 -07:00

68 lines
2.7 KiB
Python

"""SEC N-MFP (money market fund) filing parser."""
def _to_float(value) -> float | None:
"""Coerce a value to float, returning None when not parseable."""
try:
return float(value)
except (TypeError, ValueError):
return None
def parse_nmfp(response: dict) -> tuple[list[dict], dict]:
"""Parse an N-MFP money market fund filing into holdings and series metrics."""
form = response.get("edgarSubmission", {}).get("formData", {}) or {}
general = form.get("generalInfo", {}) or {}
series = form.get("seriesLevelInfo", {}) or {}
schedule = form.get("scheduleOfPortfolioSecuritiesInfo")
items = schedule if isinstance(schedule, list) else [schedule] if schedule else []
holdings: list[dict] = []
for item in items:
if not isinstance(item, dict):
continue
weight = _to_float(item.get("percentageOfMoneyMarketFundNetAssets"))
security_yield = _to_float(item.get("yieldOfTheSecurityAsOfReportingDate"))
holdings.append(
{
"name": item.get("nameOfIssuer"),
"title": item.get("titleOfIssuer"),
"cusip": item.get("CUSIPMember"),
"isin": item.get("ISINId"),
"lei": item.get("LEIID"),
"asset_category": item.get("investmentCategory"),
"maturity_date": item.get("finalLegalInvestmentMaturityDate"),
"value": _to_float(item.get("includingValueOfAnySponsorSupport")),
"weight": weight * 100 if weight is not None else None,
"annualized_return": (
security_yield * 100 if security_yield is not None else None
),
"currency": "USD",
}
)
holdings.sort(
key=lambda d: (d["weight"] is not None, d["weight"] or 0), reverse=True
)
yields = series.get("sevenDayGrossYield") or []
if isinstance(yields, (dict, str)):
yields = [yields]
latest = yields[-1] if yields else None
seven_day_yield = _to_float(
latest.get("sevenDayGrossYieldValue") if isinstance(latest, dict) else latest
)
metadata = {
"fund_name": general.get("nameOfSeries"),
"series_id": general.get("seriesId"),
"period_ending": general.get("reportDate"),
"net_assets": _to_float(series.get("netAssetOfSeries")),
"total_assets": _to_float(series.get("totalValuePortfolioSecurities")),
"cash_and_equivalents": _to_float(series.get("cash")),
"seven_day_gross_yield": seven_day_yield,
"weighted_average_maturity": _to_float(series.get("averagePortfolioMaturity")),
"weighted_average_life": _to_float(series.get("averageLifeMaturity")),
}
return holdings, metadata