summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-03-15 08:05:20 -0700
committerGitHub <noreply@github.com>2024-03-15 15:05:20 +0000
commitc55f93c86533c8a2a53aa5c24b454e63d6dc01cf (patch)
tree2dcf7d9cd55d751a52bcb1646a5579f4f61f8037
parent0239e6edbbaed83305108d1a4fbe9c8740563a22 (diff)
add Intrinio ETF Holdings (#6208)
-rw-r--r--openbb_platform/extensions/etf/integration/test_etf_api.py6
-rw-r--r--openbb_platform/extensions/etf/integration/test_etf_python.py6
-rw-r--r--openbb_platform/openbb/assets/reference.json126
-rw-r--r--openbb_platform/openbb/package/etf.py44
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/__init__.py2
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/models/etf_holdings.py202
-rw-r--r--openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_etf_holdings_fetcher.yaml70
-rw-r--r--openbb_platform/providers/intrinio/tests/test_intrinio_fetchers.py10
8 files changed, 453 insertions, 13 deletions
diff --git a/openbb_platform/extensions/etf/integration/test_etf_api.py b/openbb_platform/extensions/etf/integration/test_etf_api.py
index d0b03385a36..850998dd540 100644
--- a/openbb_platform/extensions/etf/integration/test_etf_api.py
+++ b/openbb_platform/extensions/etf/integration/test_etf_api.py
@@ -333,6 +333,12 @@ def test_etf_holdings_date(params, headers):
"use_cache": False,
}
),
+ (
+ {
+ "symbol": "DJIA",
+ "provider": "intrinio",
+ }
+ ),
],
)
@pytest.mark.integration
diff --git a/openbb_platform/extensions/etf/integration/test_etf_python.py b/openbb_platform/extensions/etf/integration/test_etf_python.py
index 417c4eae3a9..f3d966b831b 100644
--- a/openbb_platform/extensions/etf/integration/test_etf_python.py
+++ b/openbb_platform/extensions/etf/integration/test_etf_python.py
@@ -325,6 +325,12 @@ def test_etf_holdings_date(params, obb):
"use_cache": False,
}
),
+ (
+ {
+ "symbol": "DJIA",
+ "provider": "intrinio",
+ }
+ ),
],
)
@pytest.mark.integration
diff --git a/openbb_platform/openbb/assets/reference.json b/openbb_platform/openbb/assets/reference.json
index beb7f8439a4..fd9de6d9d5d 100644
--- a/openbb_platform/openbb/assets/reference.json
+++ b/openbb_platform/openbb/assets/reference.json
@@ -21582,7 +21582,7 @@
},
{
"name": "provider",
- "type": "Literal['fmp', 'sec']",
+ "type": "Literal['fmp', 'intrinio', 'sec']",
"description": "The provider to use for the query, by default None. If None, the provider specified in defaults is selected or 'fmp' if there is no default.",
"default": "fmp",
"optional": true
@@ -21604,6 +21604,7 @@
"optional": true
}
],
+ "intrinio": [],
"sec": [
{
"name": "date",
@@ -21630,7 +21631,7 @@
},
{
"name": "provider",
- "type": "Optional[Literal['fmp', 'sec']]",
+ "type": "Optional[Literal['fmp', 'intrinio', 'sec']]",
"description": "Provider name."
},
{
@@ -21816,6 +21817,127 @@
"optional": true
}
],
+ "intrinio": [
+ {
+ "name": "security_type",
+ "type": "str",
+ "description": "The type of instrument for this holding. Examples(Bond='BOND', Equity='EQUI')",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "isin",
+ "type": "str",
+ "description": "The International Securities Identification Number.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "ric",
+ "type": "str",
+ "description": "The Reuters Instrument Code.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "sedol",
+ "type": "str",
+ "description": "The Stock Exchange Daily Official List.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "share_class_figi",
+ "type": "str",
+ "description": "The OpenFIGI symbol for the holding.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "country",
+ "type": "str",
+ "description": "The country or region of the holding.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "maturity_date",
+ "type": "date",
+ "description": "The maturity date for the debt security, if available.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "contract_expiry_date",
+ "type": "date",
+ "description": "Expiry date for the futures contract held, if available.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "coupon",
+ "type": "float",
+ "description": "The coupon rate of the debt security, if available.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "balance",
+ "type": "Union[float, int]",
+ "description": "The number of units of the security held, if available.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "unit",
+ "type": "str",
+ "description": "The units of the 'balance' field.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "units_per_share",
+ "type": "float",
+ "description": "Number of units of the security held per share outstanding of the ETF, if available.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "face_value",
+ "type": "float",
+ "description": "The face value of the debt security, if available.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "derivatives_value",
+ "type": "float",
+ "description": "The notional value of derivatives contracts held.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "value",
+ "type": "float",
+ "description": "The market value of the holding, on the 'as_of' date.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "weight",
+ "type": "float",
+ "description": "The weight of the holding, as a normalized percent.",
+ "default": null,
+ "optional": true
+ },
+ {
+ "name": "updated",
+ "type": "date",
+ "description": "The 'as_of' date for the holding.",
+ "default": null,
+ "optional": true
+ }
+ ],
"sec": [
{
"name": "lei",
diff --git a/openbb_platform/openbb/package/etf.py b/openbb_platform/openbb/package/etf.py
index 74703b23143..fba9012a51e 100644
--- a/openbb_platform/openbb/package/etf.py
+++ b/openbb_platform/openbb/package/etf.py
@@ -371,7 +371,7 @@ class ROUTER_etf(Container):
str, OpenBBCustomParameter(description="Symbol to get data for. (ETF)")
],
provider: Annotated[
- Optional[Literal["fmp", "sec"]],
+ Optional[Literal["fmp", "intrinio", "sec"]],
OpenBBCustomParameter(
description="The provider to use for the query, by default None.\n If None, the provider specified in defaults is selected or 'fmp' if there is\n no default."
),
@@ -384,7 +384,7 @@ class ROUTER_etf(Container):
----------
symbol : str
Symbol to get data for. (ETF)
- provider : Optional[Literal['fmp', 'sec']]
+ provider : Optional[Literal['fmp', 'intrinio', 'sec']]
The provider to use for the query, by default None.
If None, the provider specified in defaults is selected or 'fmp' if there is
no default.
@@ -401,7 +401,7 @@ class ROUTER_etf(Container):
OBBject
results : List[EtfHoldings]
Serializable results.
- provider : Optional[Literal['fmp', 'sec']]
+ provider : Optional[Literal['fmp', 'intrinio', 'sec']]
Provider name.
warnings : Optional[List[Warning_]]
List of warnings.
@@ -423,9 +423,10 @@ class ROUTER_etf(Container):
cusip : Optional[str]
The CUSIP of the holding. (provider: fmp, sec)
isin : Optional[str]
- The ISIN of the holding. (provider: fmp, sec)
+ The ISIN of the holding. (provider: fmp, intrinio, sec)
balance : Optional[int]
The balance of the holding, in shares or units. (provider: fmp);
+ The number of units of the security held, if available. (provider: intrinio);
The balance of the holding. (provider: sec)
units : Optional[Union[str, float]]
The type of units. (provider: fmp);
@@ -433,9 +434,9 @@ class ROUTER_etf(Container):
currency : Optional[str]
The currency of the holding. (provider: fmp, sec)
value : Optional[float]
- The value of the holding, in dollars. (provider: fmp, sec)
+ The value of the holding, in dollars. (provider: fmp, intrinio, sec)
weight : Optional[float]
- The weight of the holding, as a normalized percent. (provider: fmp);
+ The weight of the holding, as a normalized percent. (provider: fmp, intrinio);
The weight of the holding in ETF in %. (provider: sec)
payoff_profile : Optional[str]
The payoff profile of the holding. (provider: fmp, sec)
@@ -444,7 +445,7 @@ class ROUTER_etf(Container):
issuer_category : Optional[str]
The issuer category of the holding. (provider: fmp, sec)
country : Optional[str]
- The country of the holding. (provider: fmp, sec)
+ The country of the holding. (provider: fmp, intrinio, sec)
is_restricted : Optional[str]
Whether the holding is restricted. (provider: fmp, sec)
fair_value_level : Optional[int]
@@ -460,7 +461,30 @@ class ROUTER_etf(Container):
acceptance_datetime : Optional[str]
The acceptance datetime of the filing. (provider: fmp)
updated : Optional[Union[date, datetime]]
- The date the data was updated. (provider: fmp)
+ The date the data was updated. (provider: fmp);
+ The 'as_of' date for the holding. (provider: intrinio)
+ security_type : Optional[str]
+ The type of instrument for this holding. Examples(Bond='BOND', Equity='EQUI') (provider: intrinio)
+ ric : Optional[str]
+ The Reuters Instrument Code. (provider: intrinio)
+ sedol : Optional[str]
+ The Stock Exchange Daily Official List. (provider: intrinio)
+ share_class_figi : Optional[str]
+ The OpenFIGI symbol for the holding. (provider: intrinio)
+ maturity_date : Optional[date]
+ The maturity date for the debt security, if available. (provider: intrinio, sec)
+ contract_expiry_date : Optional[date]
+ Expiry date for the futures contract held, if available. (provider: intrinio)
+ coupon : Optional[float]
+ The coupon rate of the debt security, if available. (provider: intrinio)
+ unit : Optional[str]
+ The units of the 'balance' field. (provider: intrinio)
+ units_per_share : Optional[float]
+ Number of units of the security held per share outstanding of the ETF, if available. (provider: intrinio)
+ face_value : Optional[float]
+ The face value of the debt security, if available. (provider: intrinio)
+ derivatives_value : Optional[float]
+ The notional value of derivatives contracts held. (provider: intrinio)
other_id : Optional[str]
Internal identifier for the holding. (provider: sec)
loan_value : Optional[float]
@@ -469,8 +493,6 @@ class ROUTER_etf(Container):
The issuer conditions of the holding. (provider: sec)
asset_conditional : Optional[str]
The asset conditions of the holding. (provider: sec)
- maturity_date : Optional[date]
- The maturity date of the debt security. (provider: sec)
coupon_kind : Optional[str]
The type of coupon for the debt security. (provider: sec)
rate_type : Optional[str]
@@ -591,7 +613,7 @@ class ROUTER_etf(Container):
"provider": self._get_provider(
provider,
"/etf/holdings",
- ("fmp", "sec"),
+ ("fmp", "intrinio", "sec"),
)
},
standard_params={
diff --git a/openbb_platform/providers/intrinio/openbb_intrinio/__init__.py b/openbb_platform/providers/intrinio/openbb_intrinio/__init__.py
index f4d001dcc1b..554cc5b8687 100644
--- a/openbb_platform/providers/intrinio/openbb_intrinio/__init__.py
+++ b/openbb_platform/providers/intrinio/openbb_intrinio/__init__.py
@@ -11,6 +11,7 @@ from openbb_intrinio.models.equity_historical import IntrinioEquityHistoricalFet
from openbb_intrinio.models.equity_info import IntrinioEquityInfoFetcher
from openbb_intrinio.models.equity_quote import IntrinioEquityQuoteFetcher
from openbb_intrinio.models.equity_search import IntrinioEquitySearchFetcher
+from openbb_intrinio.models.etf_holdings import IntrinioEtfHoldingsFetcher
from openbb_intrinio.models.etf_info import IntrinioEtfInfoFetcher
from openbb_intrinio.models.etf_price_performance import (
IntrinioEtfPricePerformanceFetcher,
@@ -60,6 +61,7 @@ intrinio_provider = Provider(
"EquityQuote": IntrinioEquityQuoteFetcher,
"EquitySearch": IntrinioEquitySearchFetcher,
"EtfHistorical": IntrinioEquityHistoricalFetcher,
+ "EtfHoldings": IntrinioEtfHoldingsFetcher,
"EtfInfo": IntrinioEtfInfoFetcher,
"EtfPricePerformance": IntrinioEtfPricePerformanceFetcher,
"EtfSearch": IntrinioEtfSearchFetcher,
diff --git a/openbb_platform/providers/intrinio/openbb_intrinio/models/etf_holdings.py b/openbb_platform/providers/intrinio/openbb_intrinio/models/etf_holdings.py
new file mode 100644
index 00000000000..52d546bf002
--- /dev/null
+++ b/openbb_platform/providers/intrinio/openbb_intrinio/models/etf_holdings.py
@@ -0,0 +1,202 @@
+"""Intrinio ETF Holdings Model."""
+
+# pylint: disable=unused-argument
+
+from datetime import date as dateType
+from typing import Any, Dict, List, Optional, Union
+
+from openbb_core.provider.abstract.fetcher import Fetcher
+from openbb_core.provider.standard_models.etf_holdings import (
+ EtfHoldingsData,
+ EtfHoldingsQueryParams,
+)
+from openbb_core.provider.utils.helpers import (
+ ClientResponse,
+ ClientSession,
+ amake_request,
+)
+from pydantic import Field, model_validator
+
+
+class IntrinioEtfHoldingsQueryParams(EtfHoldingsQueryParams):
+ """
+ Intrinio ETF Holdings Query Params.
+
+ Source: https://docs.intrinio.com/documentation/web_api/get_etf_holdings_v2
+ """
+
+
+class IntrinioEtfHoldingsData(EtfHoldingsData):
+ """Intrinio ETF Holdings Data."""
+
+ __alias_dict__ = {
+ "symbol": "ticker",
+ "security_type": "type",
+ "unit": "quantity_units",
+ "face_value": "face",
+ "balance": "quantity_held",
+ "value": "market_value_held",
+ "derivatives_value": "notional_value",
+ "units_per_share": "quantity_per_share",
+ "weight": "weighting",
+ "updated": "as_of_date",
+ "country": "location",
+ "maturity_date": "maturity",
+ }
+
+ name: Optional[str] = Field(
+ default=None,
+ description="The common name for the holding.",
+ )
+ security_type: Optional[str] = Field(
+ default=None,
+ description="The type of instrument for this holding. Examples(Bond='BOND', Equity='EQUI')",
+ )
+ isin: Optional[str] = Field(
+ default=None,
+ description="The International Securities Identification Number.",
+ )
+ ric: Optional[str] = Field(
+ default=None,
+ description="The Reuters Instrument Code.",
+ )
+ sedol: Optional[str] = Field(
+ default=None,
+ description="The Stock Exchange Daily Official List.",
+ )
+ share_class_figi: Optional[str] = Field(
+ default=None,
+ description="The OpenFIGI symbol for the holding.",
+ )
+ country: Optional[str] = Field(
+ default=None,
+ description="The country or region of the holding.",
+ )
+ maturity_date: Optional[dateType] = Field(
+ default=None,
+ description="The maturity date for the debt security, if available.",
+ )
+ contract_expiry_date: Optional[dateType] = Field(
+ default=None,
+ description="Expiry date for the futures contract held, if available.",
+ )
+ coupon: Optional[float] = Field(
+ default=None,
+ description="The coupon rate of the debt security, if available.",
+ )
+ balance: Optional[Union[int, float]] = Field(
+ default=None,
+ description="The number of units of the security held, if available.",
+ )
+ unit: Optional[str] = Field(
+ default=None,
+ description="The units of the 'balance' field.",
+ )
+ units_per_share: Optional[float] = Field(
+ default=None,
+ description="Number of units of the security held per share outstanding of the ETF, if available.",
+ )
+ face_value: Optional[float] = Field(
+ default=None,
+ description="The face value of the debt security, if available.",
+ )
+ derivatives_value: Optional[float] = Field(
+ default=None,
+ description="The notional value of derivatives contracts held.",
+ )
+ value: Optional[float] = Field(
+ default=None,
+ description="The market value of the holding, on the 'as_of' date.",
+ )
+ weight: Optional[float] = Field(
+ default=None,
+ description="The weight of the holding, as a normalized percent.",
+ )
+ updated: Optional[dateType] = Field(
+ default=None,
+ description="The 'as_of' date for the holding.",
+ )
+
+ @model_validator(mode="before")
+ @classmethod
+ def replace_zero(cls, values):
+ """Check for zero values and replace with None."""
+ return (
+ {k: None if v == 0 else v for k, v in values.items()}
+ if isinstance(values, dict)
+ else values
+ )
+
+
+class IntrinioEtfHoldingsFetcher(
+ Fetcher[IntrinioEtfHoldingsQueryParams, List[IntrinioEtfHoldingsData]]
+):
+ """Intrinio ETF Holdings Fetcher."""
+
+ @staticmethod
+ def transform_query(params: Dict[str, Any]) -> IntrinioEtfHoldingsQueryParams:
+ """Transform query."""
+ return IntrinioEtfHoldingsQueryParams(**params)
+
+ @staticmethod
+ async def aextract_data(
+ query: IntrinioEtfHoldingsQueryParams,
+ credentials: Optional[Dict[str, str]],
+ **kwargs: Any,
+ ) -> List[Dict]:
+ """Return the raw data from the Intrinio endpoint."""
+
+ api_key = credentials.get("intrinio_api_key") if credentials else ""
+
+ symbol = query.symbol + ":US" if ":" not in query.symbol else query.symbol
+
+ URL = f"https://api-v2.intrinio.com/etfs/{symbol}/holdings?page_size=10000&api_key={api_key}"
+
+ data = []
+
+ async def response_callback(response: ClientResponse, session: ClientSession):
+ """Async response callback."""
+ results = await response.json()
+
+ if results.get("error"): # type: ignore
+ return results
+
+ if results.get("holdings") and len(results.get("holdings")) > 0: # type: ignore
+ data.extend(results.get("holdings")) # type: ignore
+ while results.get("next_page"): # type: ignore
+ next_page = results["next_page"] # type: ignore
+ next_url = f"{URL}&next_page={next_page}"
+ results = await amake_request(next_url, session=session, **kwargs)
+ if (
+ "holdings" in results
+ and len(results.get("holdings")) > 0 # type: ignore
+ ):
+ data.extend(results.get("holdings")) # type: ignore
+ return data
+
+ return await amake_request(URL, response_callback=response_callback, **kwargs) # type: ignore
+
+ @staticmethod
+ def transform_data(
+ query: IntrinioEtfHoldingsQueryParams,
+ data: List[Dict],
+ **kwargs: Any,
+ ) -> List[IntrinioEtfHoldingsData]:
+ """Transform data."""
+
+ if not data or isinstance(data, dict) and data.get("error"):
+ if isinstance(data, list) and data == []:
+ raise RuntimeError(
+ str(
+ f"No holdings were found for {query.symbol}, and the response from Intrinio was empty."
+ )
+ )
+ raise RuntimeError(str(f"{data.get('message')} {query.symbol}: {data['error']}")) # type: ignore
+
+ results: List[IntrinioEtfHoldingsData] = []
+ for d in sorted(data, key=lambda x: x["weighting"], reverse=True):
+ # This field is deprecated and is dupilcated in the response.
+ _ = d.pop("composite_figi", None)
+ results.append(IntrinioEtfHoldingsData.model_validate(d))
+
+ return results
diff --git a/openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_etf_holdings_fetcher.yaml b/openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_etf_holdings_fetcher.yaml
new file mode 100644
index 00000000000..ef8c27abd22
--- /dev/null
+++ b/openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_etf_holdings_fetcher.yaml
@@ -0,0 +1,70 @@
+interactions:
+- request:
+ body: null
+ headers:
+ Accept:
+ - application/json
+ Accept-Encoding:
+ - gzip, deflate
+ Connection:
+ - keep-alive
+ method: GET
+ uri: https://api-v2.intrinio.com/etfs/DJIA:US/holdings?api_key=MOCK_API_KEY&page_size=10000
+ response:
+ body:
+ string: !!binary |
+ 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
+ Dp6WX6//AwAA//8DAMc4QPyTNwAA
+ headers:
+ Connection:
+ - keep-alive
+ Content-Encoding:
+ - gzip
+ Content-Type:
+ - application/json
+ Date:
+ - Thu, 14 Mar 2024 00:48:14 GMT
+ Transfer-Encoding:
+ - chunked
+ Vary:
+ - Origin,Accept-Encoding
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/openbb_platform/providers/intrinio/tests/test_intrinio_fetchers.py b/openbb_platform/providers/intrinio/tests/test_intrinio_fetchers.py
index 8c0ac8340e2..a6edc047eed 100644
--- a/openbb_platform/providers/intrinio/tests/test_intrinio_fetchers.py
+++ b/openbb_platform/providers/intrinio/tests/test_intrinio_fetchers.py
@@ -13,6 +13,7 @@ from openbb_intrinio.models.equity_historical import IntrinioEquityHistoricalFet
from openbb_intrinio.models.equity_info import IntrinioEquityInfoFetcher
from openbb_intrinio.models.equity_quote import IntrinioEquityQuoteFetcher
from openbb_intrinio.models.equity_search import IntrinioEquitySearchFetcher
+from openbb_intrinio.models.etf_holdings import IntrinioEtfHoldingsFetcher
from openbb_intrinio.models.etf_info import IntrinioEtfInfoFetcher
from openbb_intrinio.models.etf_price_performance import (
IntrinioEtfPricePerformanceFetcher,
@@ -388,6 +389,15 @@ def test_intrinio_etf_info_fetcher(credentials=test_credentials):
@pytest.mark.record_http
+def test_intrinio_etf_holdings_fetcher(credentials=test_credentials):
+ params = {"symbol": "DJIA"}
+
+ fetcher = IntrinioEtfHoldingsFetcher()
+ result = fetcher.test(params, credentials)
+ assert result is None
+
+
+@pytest.mark.record_http
def test_intrinio_etf_price_performance