summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-03-05 06:21:38 -0800
committerGitHub <noreply@github.com>2024-03-05 14:21:38 +0000
commit6efab3fd15983563a36282a2e90caa031b7b5021 (patch)
tree383095ed328446e66018695a847bc243f57f363d
parente7896a839eeb5b8eba809aae5197a05867aae415 (diff)
[Feature] Add `form_13f` Endpoint to `equity.ownership` (#6122)
* add form_13f endpoint * review things * option_type validator * black * codespell * add some comments to the code * remove duplicated process * static file * remove warning * static why no one updates you :-( --------- Co-authored-by: Pratyush Shukla <ps4534@nyu.edu> Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
-rw-r--r--openbb_platform/core/openbb_core/provider/standard_models/form_13FHR.py107
-rw-r--r--openbb_platform/extensions/equity/integration/test_equity_api.py24
-rw-r--r--openbb_platform/extensions/equity/integration/test_equity_python.py23
-rw-r--r--openbb_platform/extensions/equity/openbb_equity/ownership/ownership_router.py31
-rw-r--r--openbb_platform/openbb/assets/module_map.json1
-rw-r--r--openbb_platform/openbb/package/economy_gdp.py4
-rw-r--r--openbb_platform/openbb/package/equity_ownership.py120
-rw-r--r--openbb_platform/openbb/package/etf.py4
-rw-r--r--openbb_platform/providers/sec/openbb_sec/__init__.py2
-rw-r--r--openbb_platform/providers/sec/openbb_sec/models/form_13FHR.py92
-rw-r--r--openbb_platform/providers/sec/openbb_sec/utils/parse_13f.py209
-rw-r--r--openbb_platform/providers/sec/tests/record/http/test_sec_fetchers/test_sec_form_13FHR_fetcher.yaml5082
-rw-r--r--openbb_platform/providers/sec/tests/test_sec_fetchers.py10
13 files changed, 5705 insertions, 4 deletions
diff --git a/openbb_platform/core/openbb_core/provider/standard_models/form_13FHR.py b/openbb_platform/core/openbb_core/provider/standard_models/form_13FHR.py
new file mode 100644
index 00000000000..38bc62a98c5
--- /dev/null
+++ b/openbb_platform/core/openbb_core/provider/standard_models/form_13FHR.py
@@ -0,0 +1,107 @@
+"""From 13F-HR Standard Model."""
+
+from datetime import date as dateType
+from typing import Literal, Optional
+
+from pydantic import Field, field_validator
+
+from openbb_core.provider.abstract.data import Data
+from openbb_core.provider.abstract.query_params import QueryParams
+from openbb_core.provider.utils.descriptions import (
+ QUERY_DESCRIPTIONS,
+)
+
+
+class Form13FHRQueryParams(QueryParams):
+ """Form 13F-HR Query."""
+
+ __validator_dict__ = {"check_single": ("symbol")}
+
+ symbol: str = Field(
+ description=QUERY_DESCRIPTIONS.get("symbol", "")
+ + " A CIK or Symbol can be used."
+ )
+ date: Optional[dateType] = Field(
+ default=None,
+ description=QUERY_DESCRIPTIONS.get("date", "")
+ + " The date represents the end of the reporting period."
+ + " All form 13F-HR filings are based on the calendar year"
+ + " and are reported quarterly."
+ + " If a date is not supplied, the most recent filing is returned."
+ + " Submissions beginning 2013-06-30 are supported.",
+ )
+ limit: Optional[int] = Field(
+ default=1,
+ description=QUERY_DESCRIPTIONS.get("limit", "")
+ + " The number of previous filings to return."
+ + " The date parameter takes priority over this parameter.",
+ )
+
+ @field_validator("symbol", mode="before", check_fields=False)
+ @classmethod
+ def upper_symbol(cls, v: str):
+ """Convert symbol to uppercase."""
+ return str(v).upper()
+
+
+class Form13FHRData(Data):
+ """
+ Form 13F-HR Data.
+
+ Detailed documentation of the filing can be found here:
+ https://www.sec.gov/pdf/form13f.pdf
+ """
+
+ period_ending: dateType = Field(
+ description="The end-of-quarter date of the filing."
+ )
+ issuer: str = Field(description="The name of the issuer.")
+ cusip: str = Field(description="The CUSIP of the security.")
+ asset_class: str = Field(
+ description="The title of the asset class for the security."
+ )
+ security_type: Optional[Literal["SH", "PRN"]] = Field(
+ default=None,
+ description="The total number of shares of the class of security"
+ + " or the principal amount of such class."
+ + " 'SH' for shares. 'PRN' for principal amount."
+ + " Convertible debt securities are reported as 'PRN'.",
+ )
+ option_type: Optional[Literal["call", "put"]] = Field(
+ default=None,
+ description="Defined when the holdings being reported are put or call options."
+ + " Only long positions are reported.",
+ )
+ voting_authority_sole: Optional[int] = Field(
+ default=None,
+ description="The number of shares for which the Manager"
+ + " exercises sole voting authority (none).",
+ )
+ voting_authority_shared: Optional[int] = Field(
+ default=None,
+ description="The number of shares for which the Manager"
+ + " exercises a defined shared voting authority (none).",
+ )
+ voting_authority_other: Optional[int] = Field(
+ default=None,
+ description="The number of shares for which the Manager"
+ + " exercises other shared voting authority (none).",
+ )
+ principal_amount: int = Field(
+ description="The total number of shares of the class of security"
+ + " or the principal amount of such class."
+ + " Only long positions are reported"
+ )
+ value: int = Field(
+ description="The fair market value of the holding of the particular class of security."
+ + " The value reported for options is the fair market value of the underlying security"
+ + " with respect to the number of shares controlled."
+ + " Values are rounded to the nearest US dollar"
+ + " and use the closing price of the last trading day of the calendar year or quarter.",
+ )
+
+ @field_validator("option_type", mode="before", check_fields=False)
+ @classmethod
+ def validate_option_type(cls, v: str):
+ """Validate and convert to lower case."""
+ return v.lower() if v else None
diff --git a/openbb_platform/extensions/equity/integration/test_equity_api.py b/openbb_platform/extensions/equity/integration/test_equity_api.py
index 8bee0fe1569..401ae64cd8a 100644
--- a/openbb_platform/extensions/equity/integration/test_equity_api.py
+++ b/openbb_platform/extensions/equity/integration/test_equity_api.py
@@ -1775,3 +1775,27 @@ def test_equity_fundamental_reported_financials(params, headers):
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
+
+
+@parametrize(
+ "params",
+ [
+ (
+ {
+ "symbol": "NVDA",
+ "date": None,
+ "limit": 1,
+ "provider": "sec",
+ }
+ ),
+ ],
+)
+@pytest.mark.integration
+def test_equity_ownership_form_13f(params, headers):
+ params = {p: v for p, v in params.items() if v}
+
+ query_str = get_querystring(params, [])
+ url = f"http://0.0.0.0:8000/api/v1/equity/ownership/form_13f?{query_str}"
+ result = requests.get(url, headers=headers, timeout=10)
+ assert isinstance(result, requests.Response)
+ assert result.status_code == 200
diff --git a/openbb_platform/extensions/equity/integration/test_equity_python.py b/openbb_platform/extensions/equity/integration/test_equity_python.py
index ff627eb82c2..6c1150d24f5 100644
--- a/openbb_platform/extensions/equity/integration/test_equity_python.py
+++ b/openbb_platform/extensions/equity/integration/test_equity_python.py
@@ -1664,3 +1664,26 @@ def test_equity_fundamental_reported_financials(params, obb):
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
+
+
+@parametrize(
+ "params",
+ [
+ (
+ {
+ "symbol": "NVDA",
+ "date": None,
+ "limit": 1,
+ "provider": "sec",
+ }
+ ),
+ ],
+)
+@pytest.mark.integration
+def test_equity_ownership_form_13f(params, obb):
+ params = {p: v for p, v in params.items() if v}
+
+ result = obb.equity.ownership.form_13f(**params)
+ assert result
+ assert isinstance(result, OBBject)
+ assert len(result.results) > 0
diff --git a/openbb_platform/extensions/equity/openbb_equity/ownership/ownership_router.py b/openbb_platform/extensions/equity/openbb_equity/ownership/ownership_router.py
index ccaf9115ff5..e88daab971c 100644
--- a/openbb_platform/extensions/equity/openbb_equity/ownership/ownership_router.py
+++ b/openbb_platform/extensions/equity/openbb_equity/ownership/ownership_router.py
@@ -57,3 +57,34 @@ async def share_statistics(
) -> OBBject:
"""Get data about share float for a given company."""
return await OBBject.from_query(Query(**locals()))
+
+
+@router.command(
+ model="Form13FHR",
+ exclude_auto_examples=True,
+ examples=[
+ "### Enter the symbol as either the stock ticker or the CIK number as a string. ###",
+ 'obb.equity.ownership.form_13f(symbol="NVDA").to_df()',
+ "### Enter a date (calendar quarter ending) for a specific report. ###",
+ 'obb.equity.ownership.form_13f(symbol="BRK-A", date="2016-09-30")',
+ "### Use the `limit` parameter to return N number of reports from the most recent. ###",
+ "### Example finding Michael Burry's filings. ###",
+ 'cik = obb.regulators.sec.institutions_search("Scion Asset Management").results[0].cik',
+ "obb.equity.ownership.form_13f(cik, limit=2).to_df()",
+ ],
+)
+async def form_13f(
+ cc: CommandContext,
+ provider_choices: ProviderChoices,
+ standard_params: StandardParams,
+ extra_params: ExtraParams,
+) -> OBBject:
+ """
+ The Securities and Exchange Commission's (SEC) Form 13F is a quarterly report
+ that is required to be filed by all institutional investment managers with at least
+ $100 million in assets under management.
+ Managers are required to file Form 13F within 45 days after the last day of the calendar quarter.
+ Most funds wait until the end of this period in order to conceal
+ their investment strategy from competitors and the public.
+ """
+ return await OBBject.from_query(Query(**locals()))
diff --git a/openbb_platform/openbb/assets/module_map.json b/openbb_platform/openbb/assets/module_map.json
index d3ee8837e71..0163f3c6803 100644
--- a/openbb_platform/openbb/assets/module_map.json
+++ b/openbb_platform/openbb/assets/module_map.json
@@ -82,6 +82,7 @@
"equity_fundamental_transcript": "/equity/fundamental/transcript",
"equity_market_snapshots": "/equity/market_snapshots",
"equity_ownership": "/equity/ownership",
+ "equity_ownership_form_13f": "/equity/ownership/form_13f",
"equity_ownership_insider_trading": "/equity/ownership/insider_trading",
"equity_ownership_institutional": "/equity/ownership/institutional",
"equity_ownership_major_holders": "/equity/ownership/major_holders",
diff --git a/openbb_platform/openbb/package/economy_gdp.py b/openbb_platform/openbb/package/economy_gdp.py
index 8b2c779ea8f..12d7fd20179 100644
--- a/openbb_platform/openbb/package/economy_gdp.py
+++ b/openbb_platform/openbb/package/economy_gdp.py
@@ -157,7 +157,7 @@ class ROUTER_economy_gdp(Container):
The provider to use for the query, by default None.
If None, the provider specified in defaults is selected or 'oecd' if there is
no default.
- country : Literal['australia', 'austria', 'belgium', 'brazil', 'canada', 'chile', 'colombia', 'costa_rica', 'czech_republic', 'denmark', 'estonia', 'euro_area', 'european_union', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'mexico', 'netherlands', 'new_zealand', 'norway', 'poland', 'portugal', 'russia', 'slovak_republic', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states']
+ country : Literal['australia', 'austria', 'belgium', 'brazil', 'canada', 'chile', 'colombia', 'costa_rica', 'czech_republic', 'denmark', 'estonia', 'euro_area', 'european_union', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'mexico', 'netherlands', 'new_zealand', 'norway', 'poland', 'portugal', 'russia', 'slovak_republic', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states', 'all']
Country to get GDP for. (provider: oecd)
Returns
@@ -245,7 +245,7 @@ class ROUTER_economy_gdp(Container):
The provider to use for the query, by default None.
If None, the provider specified in defaults is selected or 'oecd' if there is
no default.
- country : Literal['G20', 'G7', 'argentina', 'australia', 'austria', 'belgium', 'brazil', 'bulgaria', 'canada', 'chile', 'china', 'colombia', 'costa_rica', 'croatia', 'czech_republic', 'denmark', 'estonia', 'euro_area_19', 'europe', 'european_union_27', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'india', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'mexico', 'netherlands', 'new_zealand', 'norway', 'oecd_total', 'poland', 'portugal', 'romania', 'russia', 'saudi_arabia', 'slovak_republic', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states']
+ country : Literal['G20', 'G7', 'argentina', 'australia', 'austria', 'belgium', 'brazil', 'bulgaria', 'canada', 'chile', 'china', 'colombia', 'costa_rica', 'croatia', 'czech_republic', 'denmark', 'estonia', 'euro_area_19', 'europe', 'european_union_27', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'india', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'mexico', 'netherlands', 'new_zealand', 'norway', 'oecd_total', 'poland', 'portugal', 'romania', 'russia', 'saudi_arabia', 'slovak_republic', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states', 'all']
Country to get GDP for. (provider: oecd)
Returns
diff --git a/openbb_platform/openbb/package/equity_ownership.py b/openbb_platform/openbb/package/equity_ownership.py
index 723605a73ee..6597acbb9fd 100644
--- a/openbb_platform/openbb/package/equity_ownership.py
+++ b/openbb_platform/openbb/package/equity_ownership.py
@@ -13,6 +13,7 @@ from typing_extensions import Annotated
class ROUTER_equity_ownership(Container):
"""/equity/ownership
+ form_13f
insider_trading
institutional
major_holders
@@ -24,6 +25,125 @@ class ROUTER_equity_ownership(Container):
@exception_handler
@validate
+ def form_13f(
+ self,
+ symbol: Annotated[
+ str,
+ OpenBBCustomParameter(
+ description="Symbol to get data for. A CIK or Symbol can be used."
+ ),
+ ],
+ date: Annotated[
+ Union[datetime.date, None, str],
+ OpenBBCustomParameter(
+ description="A specific date to get data for. The date represents the end of the reporting period. All form 13F-HR filings are based on the calendar year and are reported quarterly. If a date is not supplied, the most recent filing is returned. Submissions beginning 2013-06-30 are supported."
+ ),
+ ] = None,
+ limit: Annotated[
+ Optional[int],
+ OpenBBCustomParameter(
+ description="The number of data entries to return. The number of previous filings to return. The date parameter takes priority over this parameter."
+ ),
+ ] = 1,
+ provider: Optional[Literal["sec"]] = None,
+ **kwargs
+ ) -> OBBject:
+ """The Securities and Exchange Commission's (SEC) Form 13F is a quarterly report
+ that is required to be filed by all institutional investment managers with at least
+ $100 million in assets under management.
+ Managers are required to file Form 13F within 45 days after the last day of the calendar quarter.
+ Most funds wait until the end of this period in order to conceal
+ their investment strategy from competitors and the public.
+
+
+ Parameters
+ ----------
+ symbol : str
+ Symbol to get data for. A CIK or Symbol can be used.
+ date : Union[datetime.date, None, str]
+ A specific date to get data for. The date represents the end of the reporting period. All form 13F-HR filings are based on the calendar year and are reported quarterly. If a date is not supplied, the most recent filing is returned. Submissions beginning 2013-06-30 are supported.
+ limit : Optional[int]
+ The number of data entries to return. The number of previous filings to return. The date parameter takes priority over this parameter.
+ provider : Optional[Literal['sec']]
+ The provider to use for the query, by default None.
+ If None, the provider specified in defaults is selected or 'sec' if there is
+ no default.
+
+ Returns
+ -------
+ OBBject
+ results : List[Form13FHR]
+ Serializable results.
+ provider : Optional[Literal['sec']]
+ Provider name.
+ warnings : Optional[List[Warning_]]
+ List of warnings.
+ chart : Optional[Chart]
+ Chart object.
+ extra : Dict[str, Any]
+ Extra info.
+
+ Form13FHR
+ ---------
+ period_ending : date
+ The end-of-quarter date of the filing.
+ issuer : str
+ The name of the issuer.
+ cusip : str
+ The CUSIP of the security.
+ asset_class : str
+ The title of the asset class for the security.
+ security_type : Optional[Literal['SH', 'PRN']]
+ The total number of shares of the class of security or the principal amount of such class. 'SH' for shares. 'PRN' for principal amount. Convertible debt securities are reported as 'PRN'.
+ option_type : Optional[Literal['call', 'put']]
+ Defined when the holdings being reported are put or call options. Only long positions are reported.
+ voting_authority_sole : Optional[int]
+ The number of shares for which the Manager exercises sole voting authority (none).
+ voting_authority_shared : Optional[int]
+ The number of shares for which the Manager exercises a defined shared voting authority (none).
+ voting_authority_other : Optional[int]
+ The number of shares for which the Manager exercises other shared voting authority (none).
+ principal_amount : int
+ The total number of shares of the class of security or the principal amount of such class. Only long positions are reported
+ value : int
+ The fair market value of the holding of the particular class of security. The value reported for options is the fair market value of the underlying security with respect to the number of shares controlled. Values are rounded to the nearest US dollar and use the closing price of the last trading day of the calendar year or quarter.
+ weight : Optional[float]
+ The weight of the security relative to the market value of all securities in the filing , as a normalized percent. (provider: sec)
+
+ Example
+ -------
+ >>> from openbb import obb
+ >>> ### Enter the symbol as either the stock ticker or the CIK number as a string. ###
+ >>> obb.equity.ownership.form_13f(symbol="NVDA").to_df()
+ >>> ### Enter a date (calendar quarter ending) for a specific report. ###
+ >>> obb.equity.ownership.form_13f(symbol="BRK-A", date="2016-09-30")
+ >>> ### Use the `limit` parameter to return N number of reports from the most recent. ###
+ >>> ### Example finding Michael Burry's filings. ###
+ >>> cik = obb.regulators.sec.institutions_search("Scion Asset Management").results[0].cik
+ >>> obb.equity.ownership.form_13f(cik, limit=2).to_df()
+ """ # noqa: E501
+
+ return self._run(
+ "/equity/ownership/form_13f",
+ **filter_inputs(
+ provider_choices={
+ "provider": self._get_provider(
+ provider,
+ "/equity/ownership/form_13f",
+ ("sec",),
+ )
+ },
+ standard_params={
+ "symbol": symbol,
+ "date": date,
+ "limit": limit,
+ },
+ extra_params=kwargs,
+ )
+ )
+
+ @exception_handler
+ @validate
def insider_trading(
self,
symbol: Annotated[
diff --git a/openbb_platform/openbb/package/etf.py b/openbb_platform/openbb/package/etf.py
index 0ece89314d7..2f6ddf5aca1 100644
--- a/openbb_platform/openbb/package/etf.py
+++ b/openbb_platform/openbb/package/etf.py
@@ -290,7 +290,7 @@ class ROUTER_etf(Container):
no default.
date : Optional[Union[str, datetime.date]]
A specific date to get data for. Entering a date will attempt to return the NPORT-P filing for the entered date. This needs to be _exactly_ the date of the filing. Use the holdings_date command/endpoint to find available filing dates for the ETF. (provider: fmp);
- A specific date to get data for. The date represents the period ending. The date entered will return the closest filing. (provider: sec)
+ A specific date to get data for. The date represents the period ending. The date entered will return the closest filing. (provider: sec)
cik : Optional[str]
The CIK of the filing entity. Overrides symbol. (provider: fmp)
use_cache : bool
@@ -382,7 +382,7 @@ class ROUTER_etf(Container):
in_arrears : Optional[str]
If the debt security is in arrears. (provider: sec)
is_paid_kind : Optional[str]
- If the debt security payments are are paid in kind. (provider: sec)
+ If the debt security payments are paid in kind. (provider: sec)
derivative_category : Optional[str]
The derivative category of the holding. (provider: sec)
counterparty : Optional[str]
diff --git a/openbb_platform/providers/sec/openbb_sec/__init__.py b/openbb_platform/providers/sec/openbb_sec/__init__.py
index 4b17917cdf8..410f3718186 100644
--- a/openbb_platform/providers/sec/openbb_sec/__init__.py
+++ b/openbb_platform/providers/sec/openbb_sec/__init__.py
@@ -6,6 +6,7 @@ from openbb_sec.models.company_filings import SecCompanyFilingsFetcher
from openbb_sec.models.equity_ftd import SecEquityFtdFetcher
from openbb_sec.models.equity_search import SecEquitySearchFetcher
from openbb_sec.models.etf_holdings import SecEtfHoldingsFetcher
+from openbb_sec.models.form_13FHR import SecForm13FHRFetcher
from openbb_sec.models.institutions_search import SecInstitutionsSearchFetcher
from openbb_sec.models.rss_litigation import SecRssLitigationFetcher
from openbb_sec.models.schema_files import SecSchemaFilesFetcher
@@ -24,6 +25,7 @@ sec_provider = Provider(
"EquitySearch": SecEquitySearchFetcher,
"EtfHoldings": SecEtfHoldingsFetcher,
"Filings": SecCompanyFilingsFetcher,
+ "Form13FHR": SecForm13FHRFetcher,
"InstitutionsSearch": SecInstitutionsSearchFetcher,
"RssLitigation": SecRssLitigationFetcher,
"SchemaFiles": SecSchemaFilesFetcher,
diff --git a/openbb_platform/providers/sec/openbb_sec/models/form_13FHR.py b/openbb_platform/providers/sec/openbb_sec/models/form_13FHR.py
new file mode 100644
index 00000000000..cc981cb2e75
--- /dev/null
+++ b/openbb_platform/providers/sec/openbb_sec/models/form_13FHR.py
@@ -0,0 +1,92 @@
+"""SEC Form 13F-HR Model."""
+
+# pylint: disable =unused-argument
+
+import asyncio
+from typing import Any, Dict, List, Optional
+
+from openbb_core.provider.abstract.fetcher import Fetcher
+from openbb_core.provider.standard_models.form_13FHR import (
+ Form13FHRData,
+ Form13FHRQueryParams,
+)
+from openbb_sec.utils import parse_13f
+from pydantic import Field
+
+
+class SecForm13FHRQueryParams(Form13FHRQueryParams):
+ """SEC Form 13F-HR Query Params.
+
+ Source: https://www.sec.gov/Archives/edgar/data/
+ """
+
+
+class SecForm13FHRData(Form13FHRData):
+ """SEC Form 13F-HR Data."""
+
+ __alias_dict__ = {
+ "issuer": "nameOfIssuer",
+ "asset_class": "titleOfClass",
+ "option_type": "putCall",
+ }
+
+ weight: float = Field(
+ description="The weight of the security relative to the market value of all securities in the filing"
+ + " , as a normalized percent.",
+ json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
+ )
+
+
+class SecForm13FHRFetcher(Fetcher[SecForm13FHRQueryParams, List[SecForm13FHRData]]):
+ """SEC Form 13F-HR Fetcher."""
+
+ @staticmethod
+ def transform_query(params: Dict[str, Any]) -> SecForm13FHRQueryParams:
+ """Transform the query."""
+ return SecForm13FHRQueryParams(**params)
+
+ @staticmethod
+ async def aextract_data(
+ query: SecForm13FHRQueryParams,
+ credentials: Optional[Dict[str, str]],
+ **kwargs: Any,
+ ) -> List[Dict]:
+ """Return the raw data from the SEC endpoint."""
+ symbol = query.symbol
+ urls = []
+ cik = symbol.isnumeric()
+ filings = (
+ parse_13f.get_13f_candidates(symbol=symbol)
+ if cik is False
+ else parse_13f.get_13f_candidates(cik=symbol)
+ )
+ if query.limit and query.date is None:
+ urls = filings.iloc[: query.limit].to_list()
+ if query.date is not None:
+ date = parse_13f.date_to_quarter_end(query.date.strftime("%Y-%m-%d"))
+ urls = [filings.loc[date]]
+
+ results = []
+
+ async def get_filing(url):
+ """Get a single 13F-HR filing and parse it."""
+
+ data = await parse_13f.parse_13f_hr(url)
+
+ if len(data) > 0:
+ results.extend(data.to_dict("records"))
+
+ await asyncio.gather(*[get_filing(url) for url in urls])
+
+ return sorted(
+ results, key=lambda d: [d["period_ending"], d["weight"]], reverse=True
+ )
+
+ @staticmethod
+ def transform_data(
+ query: SecForm13FHRQueryParams,
+ data: List[Dict],
+ **kwargs: Any,
+ ) -> List[SecForm13FHRData]:
+ """Transform the data."""
+ return [SecForm13FHRData.model_validate(d) for d in data]
diff --git a/openbb_platform/providers/sec/openbb_sec/utils/parse_13f.py b/openbb_platform/providers/sec/openbb_sec/utils/parse_13f.py
new file mode 100644
index 00000000000..eda060c17bc
--- /dev/null
+++ b/openbb_platform/providers/sec/openbb_sec/utils/parse_13f.py
@@ -0,0 +1,209 @@
+"""Utility functions for parsing SEC Form 13F-HR."""
+
+from typing import Dict, Optional
+
+import xmltodict
+from bs4 import BeautifulSoup
+from openbb_core.provider.utils.helpers import amake_request
+from openbb_sec.models.company_filings import SecCompanyFilingsFetcher
+from openbb_sec.utils.definitions import HEADERS
+from pandas import DataFrame, offsets, to_datetime
+
+
+def date_to_quarter_end(date: str) -> str:
+ """Convert a date to the end of the calendar quarter."""
+ return (
+ (to_datetime(date).to_period("Q").to_timestamp("D") + offsets.QuarterEnd())
+ .date()
+ .strftime("%Y-%m-%d")
+ )
+
+
+def get_13f_candidates(symbol: Optional[str] = None, cik: Optional[str] = None):
+ """Get the 13F-HR filings for a given symbol or CI