summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-02-09 12:14:11 -0800
committerGitHub <noreply@github.com>2024-02-09 20:14:11 +0000
commit6881314b6980f728ed8ebe879f73cde5bb910802 (patch)
treef7762bdfb3027c7e6ee692975ca824dbb7aae77b
parentbc24709cbaeb5eb19a8c4ce4ece45a65f99d91f2 (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.py75
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(