diff options
author | Danglewood <85772166+deeleeramone@users.noreply.github.com> | 2024-02-09 12:14:11 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-09 20:14:11 +0000 |
commit | 6881314b6980f728ed8ebe879f73cde5bb910802 (patch) | |
tree | f7762bdfb3027c7e6ee692975ca824dbb7aae77b | |
parent | bc24709cbaeb5eb19a8c4ce4ece45a65f99d91f2 (diff) |
[Feature] FMP Current Holdings (#6043)
* add fmp current holdings to etf holdings
* pylint unused argument
---------
Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
-rw-r--r-- | openbb_platform/providers/fmp/openbb_fmp/models/etf_holdings.py | 75 |
1 files changed, 62 insertions, 13 deletions
diff --git a/openbb_platform/providers/fmp/openbb_fmp/models/etf_holdings.py b/openbb_platform/providers/fmp/openbb_fmp/models/etf_holdings.py index d7df4ec76cd..94f91942959 100644 --- a/openbb_platform/providers/fmp/openbb_fmp/models/etf_holdings.py +++ b/openbb_platform/providers/fmp/openbb_fmp/models/etf_holdings.py @@ -1,8 +1,15 @@ """FMP ETF Holdings Model.""" -from datetime import date as dateType +# pylint: disable=unused-argument + +import warnings +from datetime import ( + date as dateType, + datetime, +) from typing import Any, Dict, List, Optional, Union +from openbb_core.provider.abstract.data import ForceInt from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.etf_holdings import ( EtfHoldingsData, @@ -10,7 +17,9 @@ from openbb_core.provider.standard_models.etf_holdings import ( ) from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS from openbb_fmp.utils.helpers import create_url, get_data_many -from pydantic import Field +from pydantic import Field, field_validator + +_warn = warnings.warn class FMPEtfHoldingsQueryParams(EtfHoldingsQueryParams): @@ -21,6 +30,7 @@ class FMPEtfHoldingsQueryParams(EtfHoldingsQueryParams): date: Optional[Union[str, dateType]] = Field( description=QUERY_DESCRIPTIONS.get("date", "") + + " 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.", default=None, @@ -36,26 +46,36 @@ class FMPEtfHoldingsQueryParams(EtfHoldingsQueryParams): class FMPEtfHoldingsData(EtfHoldingsData): """FMP ETF Holdings Data.""" + __alias_dict__ = { + "weight": "weightPercentage", + "value": "marketValue", + "symbol": "asset", + "balance": "sharesNumber", + } + 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 + balance: Optional[ForceInt] = Field( + description="The balance of the holding, in shares or units.", default=None ) units: Optional[Union[float, str]] = Field( - description="The units of the holding.", default=None + description="The type of units.", 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 + description="The value of the holding, in dollars.", + alias="valUsd", + default=None, ) weight: Optional[float] = Field( - description="The weight of the holding in ETF in %.", + description="The weight of the holding, as a normalized percent.", alias="pctVal", default=None, + json_schema_extra={"unit_measurement": "percent", "frontend_multiply": 100}, ) payoff_profile: Optional[str] = Field( description="The payoff profile of the holding.", @@ -104,6 +124,25 @@ class FMPEtfHoldingsData(EtfHoldingsData): alias="acceptanceTime", default=None, ) + updated: Optional[Union[dateType, datetime]] = Field( + description="The date the data was updated.", default=None + ) + + @field_validator("weight", mode="before", check_fields=False) + @classmethod + def normalize_percent(cls, v): + """Normalize percent values.""" + return float(v) / 100 if v else None + + @field_validator("cusip", "isin", "balance", "name", "symbol", "value") + @classmethod + def replace_empty(cls, v): + """Replace empty strings and 0s with None.""" + if isinstance(v, str): + return v if v not in ("", "0") else None + if isinstance(v, (float, int)): + return v if v and v not in (0.0, 0) else None + return v if v else None class FMPEtfHoldingsFetcher( @@ -127,12 +166,22 @@ class FMPEtfHoldingsFetcher( ) -> 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 await get_data_many(url, **kwargs) + data = [] + if query.date is not None: + url = create_url( + version=4, endpoint="etf-holdings", api_key=api_key, query=query + ) + try: + data = await get_data_many(url, **kwargs) + except Exception: + _warn( + "No data found for this symbol and date, attempting to retrieve the most recent data available." + ) + + if query.date is None or not data: + url = f"https://financialmodelingprep.com/api/v3/etf-holder/{query.symbol}?apikey={api_key}" + data = await get_data_many(url, **kwargs) + return data @staticmethod def transform_data( |