Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add form_13f Endpoint to equity.ownership #6122

Merged
merged 22 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions openbb_platform/extensions/equity/integration/test_equity_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
1 change: 1 addition & 0 deletions openbb_platform/openbb/assets/module_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions openbb_platform/openbb/package/economy_gdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def nominal(
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
Expand Down Expand Up @@ -245,7 +245,7 @@ def real(
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
Expand Down
120 changes: 120 additions & 0 deletions openbb_platform/openbb/package/equity_ownership.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

class ROUTER_equity_ownership(Container):
"""/equity/ownership
form_13f
insider_trading
institutional
major_holders
Expand All @@ -22,6 +23,125 @@ class ROUTER_equity_ownership(Container):
def __repr__(self) -> str:
return self.__doc__ or ""

@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(
Expand Down
4 changes: 2 additions & 2 deletions openbb_platform/openbb/package/etf.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def holdings(
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
Expand Down Expand Up @@ -382,7 +382,7 @@ def holdings(
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]
Expand Down
2 changes: 2 additions & 0 deletions openbb_platform/providers/sec/openbb_sec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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
Expand All @@ -24,6 +25,7 @@
"EquitySearch": SecEquitySearchFetcher,
"EtfHoldings": SecEtfHoldingsFetcher,
"Filings": SecCompanyFilingsFetcher,
"Form13FHR": SecForm13FHRFetcher,
"InstitutionsSearch": SecInstitutionsSearchFetcher,
"RssLitigation": SecRssLitigationFetcher,
"SchemaFiles": SecSchemaFilesFetcher,
Expand Down
Loading
Loading