forked from OpenBB-finance/OpenBB
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ETF holdings and holding report dates from FMP (OpenBB-finance#5629)
* Add ETF info from FMP * Add integration tests * Add unit tests * Add etf sectors endpoint * Add etf sectors fmp endpoint * Omit nested sector list from fmp info data * Update tests and test data * Update test data * Specify provider explicitly in integration tests * Update tests * Skip yfinance etf historical test * Add etf holdings and holdings date routes and standard models * Add fmp holdings and holding dates fetchers * Update holdings fetcher to properly handle date object inputs * Add unit tests * Add integration tests * Black after merge * Fix test input data format
- Loading branch information
Showing
11 changed files
with
671 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
openbb_platform/platform/provider/openbb_provider/standard_models/etf_holdings_date.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
"""ETF Holdings Date data model.""" | ||
|
||
from datetime import date as dateType | ||
|
||
from pydantic import Field | ||
|
||
from openbb_provider.abstract.data import Data | ||
from openbb_provider.abstract.query_params import QueryParams | ||
from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS | ||
|
||
|
||
class EtfHoldingsDateQueryParams(QueryParams): | ||
"""ETF Holdings Query Params.""" | ||
|
||
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", "") + " (ETF)") | ||
|
||
|
||
class EtfHoldingsDateData(Data): | ||
"""ETF Holdings Data.""" | ||
|
||
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
openbb_platform/providers/fmp/openbb_fmp/models/etf_holdings.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
"""FMP ETF Holdings fetcher.""" | ||
|
||
from datetime import ( | ||
date as dateType, | ||
datetime, | ||
timedelta, | ||
) | ||
from typing import Any, Dict, List, Optional, Union | ||
|
||
from openbb_fmp.utils.helpers import create_url, get_data_many | ||
from openbb_provider.abstract.fetcher import Fetcher | ||
from openbb_provider.standard_models.etf_holdings import ( | ||
EtfHoldingsData, | ||
EtfHoldingsQueryParams, | ||
) | ||
from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS | ||
from pydantic import Field | ||
|
||
|
||
class FMPEtfHoldingsQueryParams(EtfHoldingsQueryParams): | ||
"""FMP ETF Holdings query. | ||
Source: https://site.financialmodelingprep.com/developer/docs#Historical-ETF-Holdings | ||
""" | ||
|
||
date: Optional[Union[str, dateType]] = Field( | ||
description=QUERY_DESCRIPTIONS.get("date", "") | ||
+ " The input date is adjusted to the nearest previous quarter-end date." | ||
+ " Holdings are returned as of the adjusted date if available, with no data from the subsequent quarter.", | ||
default=None, | ||
) | ||
|
||
cik: Optional[str] = Field( | ||
description=QUERY_DESCRIPTIONS.get("cik", "") | ||
+ "The CIK of the filing entity. Overrides symbol.", | ||
default=None, | ||
) | ||
|
||
|
||
class FMPEtfHoldingsData(EtfHoldingsData): | ||
"""FMP ETF Holdings Data.""" | ||
|
||
lei: Optional[str] = Field(description="The LEI of the holding.", default=None) | ||
title: Optional[str] = Field(description="The title of the holding.", default=None) | ||
cusip: Optional[str] = Field(description="The CUSIP of the holding.", default=None) | ||
isin: Optional[str] = Field(description="The ISIN of the holding.", default=None) | ||
balance: Optional[float] = Field( | ||
description="The balance of the holding.", default=None | ||
) | ||
units: Optional[Union[float, str]] = Field( | ||
description="The units of the holding.", default=None | ||
) | ||
currency: Optional[str] = Field( | ||
description="The currency of the holding.", alias="cur_cd", default=None | ||
) | ||
value: Optional[float] = Field( | ||
description="The value of the holding in USD.", alias="valUsd", default=None | ||
) | ||
weight: Optional[float] = Field( | ||
description="The weight of the holding in ETF in %.", | ||
alias="pctVal", | ||
default=None, | ||
) | ||
payoff_profile: Optional[str] = Field( | ||
description="The payoff profile of the holding.", | ||
alias="payoffProfile", | ||
default=None, | ||
) | ||
asset_category: Optional[str] = Field( | ||
description="The asset category of the holding.", alias="assetCat", default=None | ||
) | ||
issuer_category: Optional[str] = Field( | ||
description="The issuer category of the holding.", | ||
alias="issuerCat", | ||
default=None, | ||
) | ||
country: Optional[str] = Field( | ||
description="The country of the holding.", alias="invCountry", default=None | ||
) | ||
is_restricted: Optional[str] = Field( | ||
description="Whether the holding is restricted.", | ||
alias="isRestrictedSec", | ||
default=None, | ||
) | ||
fair_value_level: Optional[int] = Field( | ||
description="The fair value level of the holding.", | ||
alias="fairValLevel", | ||
default=None, | ||
) | ||
is_cash_collateral: Optional[str] = Field( | ||
description="Whether the holding is cash collateral.", | ||
alias="isCashCollateral", | ||
default=None, | ||
) | ||
is_non_cash_collateral: Optional[str] = Field( | ||
description="Whether the holding is non-cash collateral.", | ||
alias="isNonCashCollateral", | ||
default=None, | ||
) | ||
is_loan_by_fund: Optional[str] = Field( | ||
description="Whether the holding is loan by fund.", | ||
alias="isLoanByFund", | ||
default=None, | ||
) | ||
cik: Optional[str] = Field(description="The CIK of the filing.", default=None) | ||
acceptance_datetime: Optional[str] = Field( | ||
description="The acceptance datetime of the filing.", | ||
alias="acceptanceTime", | ||
default=None, | ||
) | ||
|
||
|
||
class FMPEtfHoldingsFetcher( | ||
Fetcher[ | ||
FMPEtfHoldingsQueryParams, | ||
List[FMPEtfHoldingsData], | ||
] | ||
): | ||
"""Transform the query, extract and transform the data from the FMP endpoints.""" | ||
|
||
@staticmethod | ||
def transform_query(params: Dict[str, Any]) -> FMPEtfHoldingsQueryParams: | ||
"""Transform the query. | ||
Adjust input date to the nearest previous quarter-end date. | ||
""" | ||
date_str = params.get("date", datetime.today().strftime("%Y-%m-%d")) | ||
date_str = ( | ||
date_str.strftime("%Y-%m-%d") | ||
if isinstance(date_str, dateType) | ||
else date_str | ||
) | ||
date_obj = datetime.strptime(date_str, "%Y-%m-%d") | ||
quarter_month = ((date_obj.month - 1) // 3) * 3 + 1 | ||
quarter_start = date_obj.replace(month=quarter_month, day=1) | ||
previous_quarter_end = quarter_start - timedelta(days=1) | ||
params["date"] = previous_quarter_end.strftime("%Y-%m-%d") | ||
return FMPEtfHoldingsQueryParams(**params) | ||
|
||
@staticmethod | ||
def extract_data( | ||
query: FMPEtfHoldingsQueryParams, | ||
credentials: Optional[Dict[str, str]], | ||
**kwargs: Any, | ||
) -> List[Dict]: | ||
"""Return the raw data from the FMP endpoint.""" | ||
api_key = credentials.get("fmp_api_key") if credentials else "" | ||
|
||
url = create_url( | ||
version=4, endpoint="etf-holdings", api_key=api_key, query=query | ||
) | ||
|
||
return get_data_many(url, **kwargs) | ||
|
||
@staticmethod | ||
def transform_data( | ||
query: FMPEtfHoldingsQueryParams, | ||
data: List[Dict], | ||
**kwargs: Any, | ||
) -> List[FMPEtfHoldingsData]: | ||
"""Return the transformed data.""" | ||
return [FMPEtfHoldingsData.model_validate(d) for d in data] |
Oops, something went wrong.