From 650912bf0c21714a109ff50ff8ed94651eaf1d63 Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Sun, 16 Jun 2024 14:30:46 -0700 Subject: add mortgage indices (#6501) --- .../provider/standard_models/mortgage_indices.py | 46 +++ .../integration/test_fixedincome_api.py | 26 ++ .../integration/test_fixedincome_python.py | 25 ++ .../openbb_fixedincome/fixedincome_router.py | 26 ++ openbb_platform/openbb/assets/reference.json | 185 ++++++++++++ openbb_platform/openbb/package/fixedincome.py | 127 +++++++++ .../providers/fred/openbb_fred/__init__.py | 2 + .../fred/openbb_fred/models/mortgage_indices.py | 312 +++++++++++++++++++++ .../test_fred_bond_mortgage_fetcher.yaml | 105 +++++++ .../providers/fred/tests/test_fred_fetchers.py | 15 + 10 files changed, 869 insertions(+) create mode 100644 openbb_platform/core/openbb_core/provider/standard_models/mortgage_indices.py create mode 100644 openbb_platform/providers/fred/openbb_fred/models/mortgage_indices.py create mode 100644 openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_bond_mortgage_fetcher.yaml diff --git a/openbb_platform/core/openbb_core/provider/standard_models/mortgage_indices.py b/openbb_platform/core/openbb_core/provider/standard_models/mortgage_indices.py new file mode 100644 index 00000000000..bfba892bb2d --- /dev/null +++ b/openbb_platform/core/openbb_core/provider/standard_models/mortgage_indices.py @@ -0,0 +1,46 @@ +"""Mortgage Indices Standard Model.""" + +from datetime import ( + date as dateType, +) +from typing import Optional + +from pydantic import Field + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.descriptions import ( + DATA_DESCRIPTIONS, + QUERY_DESCRIPTIONS, +) + + +class MortgageIndicesQueryParams(QueryParams): + """Mortgage Indices Query.""" + + start_date: Optional[dateType] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("start_date", ""), + ) + end_date: Optional[dateType] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("end_date", ""), + ) + + +class MortgageIndicesData(Data): + """Mortgage Indices Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + symbol: Optional[str] = Field( + default=None, + description=DATA_DESCRIPTIONS.get("symbol", ""), + ) + name: Optional[str] = Field( + default=None, + description="Name of the index.", + ) + rate: float = Field( + description="Mortgage rate.", + json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100}, + ) diff --git a/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py b/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py index 1e9844767f3..521040398c2 100644 --- a/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py +++ b/openbb_platform/extensions/fixedincome/integration/test_fixedincome_api.py @@ -694,3 +694,29 @@ def test_fixedincome_bond_indices(params, headers): result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 + + +@parametrize( + "params", + [ + { + "provider": "fred", + "index": "usda_30y,fha_30y", + "start_date": "2023-05-31", + "end_date": "2024-06-01", + "transform": None, + "frequency": None, + "aggregation_method": "avg", + }, + ], +) +@pytest.mark.integration +def test_fixedincome_mortgage_indices(params, headers): + """Test the mortgage indices endpoint.""" + 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/fixedincome/mortgage_indices?{query_str}" + result = requests.get(url, headers=headers, timeout=10) + assert isinstance(result, requests.Response) + assert result.status_code == 200 diff --git a/openbb_platform/extensions/fixedincome/integration/test_fixedincome_python.py b/openbb_platform/extensions/fixedincome/integration/test_fixedincome_python.py index 433b96a3d8f..6058c18ecc9 100644 --- a/openbb_platform/extensions/fixedincome/integration/test_fixedincome_python.py +++ b/openbb_platform/extensions/fixedincome/integration/test_fixedincome_python.py @@ -647,3 +647,28 @@ def test_fixedincome_bond_indices(params, obb): assert result assert isinstance(result, OBBject) assert len(result.results) > 0 + + +@parametrize( + "params", + [ + { + "provider": "fred", + "index": "usda_30y,fha_30y", + "start_date": "2023-05-31", + "end_date": "2024-06-01", + "transform": None, + "frequency": None, + "aggregation_method": "avg", + }, + ], +) +@pytest.mark.integration +def test_fixedincome_mortgage_indices(params, obb): + """Test the mortgage indices endpoint.""" + params = {p: v for p, v in params.items() if v} + + result = obb.fixedincome.mortgage_indices(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 diff --git a/openbb_platform/extensions/fixedincome/openbb_fixedincome/fixedincome_router.py b/openbb_platform/extensions/fixedincome/openbb_fixedincome/fixedincome_router.py index fb346dd3cc3..f7d62a1b16b 100644 --- a/openbb_platform/extensions/fixedincome/openbb_fixedincome/fixedincome_router.py +++ b/openbb_platform/extensions/fixedincome/openbb_fixedincome/fixedincome_router.py @@ -81,3 +81,29 @@ async def bond_indices( ) -> OBBject: # type: ignore """Bond Indices.""" return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="MortgageIndices", + examples=[ + APIEx( + description="The default state for FRED are the primary mortgage indices from Optimal Blue.", + parameters={"provider": "fred"}, + ), + APIEx( + description="Multiple indices can be requested.", + parameters={ + "index": "jumbo_30y,conforming_30y,conforming_15y", + "provider": "fred", + }, + ), + ], +) +async def mortgage_indices( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: # type: ignore + """Mortgage Indices.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/openbb/assets/reference.json b/openbb_platform/openbb/assets/reference.json index 7b5bf13451f..ca1dcb3f376 100644 --- a/openbb_platform/openbb/assets/reference.json +++ b/openbb_platform/openbb/assets/reference.json @@ -32235,6 +32235,191 @@ }, "model": "BondIndices" }, + "/fixedincome/mortgage_indices": { + "deprecated": { + "flag": null, + "message": null + }, + "description": "Mortgage Indices.", + "examples": "\nExamples\n--------\n\n```python\nfrom openbb import obb\n# The default state for FRED are the primary mortgage indices from Optimal Blue.\nobb.fixedincome.mortgage_indices(provider='fred')\n# Multiple indices can be requested.\nobb.fixedincome.mortgage_indices(index=jumbo_30y,conforming_30y,conforming_15y, provider='fred')\n```\n\n", + "parameters": { + "standard": [ + { + "name": "start_date", + "type": "Union[date, str]", + "description": "Start date of the data, in YYYY-MM-DD format.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "end_date", + "type": "Union[date, str]", + "description": "End date of the data, in YYYY-MM-DD format.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "provider", + "type": "Literal['fred']", + "description": "The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: f, r, e, d.", + "default": null, + "optional": true + } + ], + "fred": [ + { + "name": "index", + "type": "Union[Union[Literal['primary', 'ltv_lte_80', 'ltv_gt_80', 'conforming_30y', 'conforming_30y_na', 'jumbo_30y', 'fha_30y', 'va_30y', 'usda_30y', 'conforming_15y', 'ltv_lte80_fico_ge740', 'ltv_lte80_fico_a720b739', 'ltv_lte80_fico_a700b719', 'ltv_lte80_fico_a680b699', 'ltv_lte80_fico_lt680', 'ltv_gt80_fico_ge740', 'ltv_gt80_fico_a720b739', 'ltv_gt80_fico_a700b719', 'ltv_gt80_fico_a680b699', 'ltv_gt80_fico_lt680'], str], List[Union[Literal['primary', 'ltv_lte_80', 'ltv_gt_80', 'conforming_30y', 'conforming_30y_na', 'jumbo_30y', 'fha_30y', 'va_30y', 'usda_30y', 'conforming_15y', 'ltv_lte80_fico_ge740', 'ltv_lte80_fico_a720b739', 'ltv_lte80_fico_a700b719', 'ltv_lte80_fico_a680b699', 'ltv_lte80_fico_lt680', 'ltv_gt80_fico_ge740', 'ltv_gt80_fico_a720b739', 'ltv_gt80_fico_a700b719', 'ltv_gt80_fico_a680b699', 'ltv_gt80_fico_lt680'], str]]]", + "description": "The specific index, or index group, to query. Default is the 'primary' group. Multiple items allowed for provider(s): fred.", + "default": "primary", + "optional": true, + "choices": [ + "primary", + "ltv_lte_80", + "ltv_gt_80", + "conforming_30y", + "conforming_30y_na", + "jumbo_30y", + "fha_30y", + "va_30y", + "usda_30y", + "conforming_15y", + "ltv_lte80_fico_ge740", + "ltv_lte80_fico_a720b739", + "ltv_lte80_fico_a700b719", + "ltv_lte80_fico_a680b699", + "ltv_lte80_fico_lt680", + "ltv_gt80_fico_ge740", + "ltv_gt80_fico_a720b739", + "ltv_gt80_fico_a700b719", + "ltv_gt80_fico_a680b699", + "ltv_gt80_fico_lt680" + ] + }, + { + "name": "frequency", + "type": "Literal['a', 'q', 'm', 'w', 'd', 'wef', 'weth', 'wew', 'wetu', 'wem', 'wesu', 'wesa', 'bwew', 'bwem']", + "description": "Frequency aggregation to convert daily data to lower frequency. None = No change a = Annual q = Quarterly m = Monthly w = Weekly d = Daily wef = Weekly, Ending Friday weth = Weekly, Ending Thursday wew = Weekly, Ending Wednesday wetu = Weekly, Ending Tuesday wem = Weekly, Ending Monday wesu = Weekly, Ending Sunday wesa = Weekly, Ending Saturday bwew = Biweekly, Ending Wednesday bwem = Biweekly, Ending Monday", + "default": null, + "optional": true, + "choices": [ + "a", + "q", + "m", + "w", + "d", + "wef", + "weth", + "wew", + "wetu", + "wem", + "wesu", + "wesa", + "bwew", + "bwem" + ] + }, + { + "name": "aggregation_method", + "type": "Literal['avg', 'sum', 'eop']", + "description": "A key that indicates the aggregation method used for frequency aggregation. This parameter has no affect if the frequency parameter is not set, default is 'avg'. avg = Average sum = Sum eop = End of Period", + "default": "avg", + "optional": true, + "choices": [ + "avg", + "sum", + "eop" + ] + }, + { + "name": "transform", + "type": "Literal['chg', 'ch1', 'pch', 'pc1', 'pca', 'cch', 'cca', 'log']", + "description": "Transformation type None = No transformation chg = Change ch1 = Change from Year Ago pch = Percent Change pc1 = Percent Change from Year Ago pca = Compounded Annual Rate of Change cch = Continuously Compounded Rate of Change cca = Continuously Compounded Annual Rate of Change log = Natural Log", + "default": null, + "optional": true, + "choices": [ + "chg", + "ch1", + "pch", + "pc1", + "pca", + "cch", + "cca", + "log" + ] + } + ] + }, + "returns": { + "OBBject": [ + { + "name": "results", + "type": "List[MortgageIndices]", + "description": "Serializable results." + }, + { + "name": "provider", + "type": "Optional[Literal['fred']]", + "description": "Provider name." + }, + { + "name": "warnings", + "type": "Optional[List[Warning_]]", + "description": "List of warnings." + }, + { + "name": "chart", + "type": "Optional[Chart]", + "description": "Chart object." + }, + { + "name": "extra", + "type": "Dict[str, Any]", + "description": "Extra info." + } + ] + }, + "data": { + "standard": [ + { + "name": "date", + "type": "date", + "description": "The date of the data.", + "default": "", + "optional": false, + "choices": null + }, + { + "name": "symbol", + "type": "str", + "description": "Symbol representing the entity requested in the data.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "name", + "type": "str", + "description": "Name of the index.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "rate", + "type": "float", + "description": "Mortgage rate.", + "default": "", + "optional": false, + "choices": null + } + ], + "fred": [] + }, + "model": "MortgageIndices" + }, "/index/price/historical": { "deprecated": { "flag": null, diff --git a/openbb_platform/openbb/package/fixedincome.py b/openbb_platform/openbb/package/fixedincome.py index 456845abcf0..6f5dbb27f60 100644 --- a/openbb_platform/openbb/package/fixedincome.py +++ b/openbb_platform/openbb/package/fixedincome.py @@ -16,6 +16,7 @@ class ROUTER_fixedincome(Container): bond_indices /corporate /government + mortgage_indices /rate sofr /spreads @@ -184,6 +185,132 @@ class ROUTER_fixedincome(Container): command_runner=self._command_runner ) + @exception_handler + @validate + def mortgage_indices( + self, + start_date: Annotated[ + Union[datetime.date, None, str], + OpenBBField(description="Start date of the data, in YYYY-MM-DD format."), + ] = None, + end_date: Annotated[ + Union[datetime.date, None, str], + OpenBBField(description="End date of the data, in YYYY-MM-DD format."), + ] = None, + provider: Annotated[ + Optional[Literal["fred"]], + OpenBBField( + description="The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: fred." + ), + ] = None, + **kwargs + ) -> OBBject: + """Mortgage Indices. + + Parameters + ---------- + start_date : Union[datetime.date, None, str] + Start date of the data, in YYYY-MM-DD format. + end_date : Union[datetime.date, None, str] + End date of the data, in YYYY-MM-DD format. + provider : Optional[Literal['fred']] + The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: fred. + index : Union[Literal['primary', 'ltv_lte_80', 'ltv_gt_80', 'conforming_30y', 'conforming_30y_na', 'jumbo_30y', 'fha_30y', 'va_30y', 'usda_30y', 'conforming_15y', 'ltv_lte80_fico_ge740', 'ltv_lte80_fico_a720b739', 'ltv_lte80_fico_a700b719', 'ltv_lte80_fico_a680b699', 'ltv_lte80_fico_lt680', 'ltv_gt80_fico_ge740', 'ltv_gt80_fico_a720b739', 'ltv_gt80_fico_a700b719', 'ltv_gt80_fico_a680b699', 'ltv_gt80_fico_lt680'], str] + The specific index, or index group, to query. Default is the 'primary' group. Multiple comma separated items allowed. (provider: fred) + frequency : Optional[Literal['a', 'q', 'm', 'w', 'd', 'wef', 'weth', 'wew', 'wetu', 'wem', 'wesu', 'wesa', 'bwew', 'bwem']] + + Frequency aggregation to convert daily data to lower frequency. + None = No change + a = Annual + q = Quarterly + m = Monthly + w = Weekly + d = Daily + wef = Weekly, Ending Friday + weth = Weekly, Ending Thursday + wew = Weekly, Ending Wednesday + wetu = Weekly, Ending Tuesday + wem = Weekly, Ending Monday + wesu = Weekly, Ending Sunday + wesa = Weekly, Ending Saturday + bwew = Biweekly, Ending Wednesday + bwem = Biweekly, Ending Monday + (provider: fred) + aggregation_method : Literal['avg', 'sum', 'eop'] + + A key that indicates the aggregation method used for frequency aggregation. + This parameter has no affect if the frequency parameter is not set, default is 'avg'. + avg = Average + sum = Sum + eop = End of Period + (provider: fred) + transform : Optional[Literal['chg', 'ch1', 'pch', 'pc1', 'pca', 'cch', 'cca', 'log']] + + Transformation type + None = No transformation + chg = Change + ch1 = Change from Year Ago + pch = Percent Change + pc1 = Percent Change from Year Ago + pca = Compounded Annual Rate of Change + cch = Continuously Compounded Rate of Change + cca = Continuously Compounded Annual Rate of Change + log = Natural Log + (provider: fred) + + Returns + ------- + OBBject + results : List[MortgageIndices] + Serializable results. + provider : Optional[Literal['fred']] + Provider name. + warnings : Optional[List[Warning_]] + List of warnings. + chart : Optional[Chart] + Chart object. + extra : Dict[str, Any] + Extra info. + + MortgageIndices + --------------- + date : date + The date of the data. + symbol : Optional[str] + Symbol representing the entity requested in the data. + name : Optional[str] + Name of the index. + rate : float + Mortgage rate. + + Examples + -------- + >>> from openbb import obb + >>> # The default state for FRED are the primary mortgage indices from Optimal Blue. + >>> obb.fixedincome.mortgage_indices(provider='fred') + >>> # Multiple indices can be requested. + >>> obb.fixedincome.mortgage_indices(index='jumbo_30y,conforming_30y,conforming_15y', provider='fred') + """ # noqa: E501 + + return self._run( + "/fixedincome/mortgage_indices", + **filter_inputs( + provider_choices={ + "provider": self._get_provider( + provider, + "fixedincome.mortgage_indices", + ("fred",), + ) + }, + standard_params={ + "start_date": start_date, + "end_date": end_date, + }, + extra_params=kwargs, + info={"index": {"fred": {"multiple_items_allowed": True}}}, + ) + ) + @property def rate(self): # pylint: disable=import-outside-toplevel diff --git a/openbb_platform/providers/fred/openbb_fred/__init__.py b/openbb_platform/providers/fred/openbb_fred/__init__.py index d68a8d1558e..c55e2d55e16 100644 --- a/openbb_platform/providers/fred/openbb_fred/__init__.py +++ b/openbb_platform/providers/fred/openbb_fred/__init__.py @@ -18,6 +18,7 @@ from openbb_fred.models.hqm import FREDHighQualityMarketCorporateBondFetcher from openbb_fred.models.ice_bofa import FREDICEBofAFetcher from openbb_fred.models.iorb_rates import FREDIORBFetcher from openbb_fred.models.moody import FREDMoodyCorporateBondIndexFetcher +from openbb_fred.models.mortgage_indices import FredMortgageIndicesFetcher from openbb_fred.models.regional import FredRegionalDataFetcher from openbb_fred.models.retail_prices import FredRetailPricesFetcher from openbb_fred.models.search import ( @@ -57,6 +58,7 @@ Research division of the Federal Reserve Bank of St. Louis that has more than "EuropeanCentralBankInterestRates": FREDEuropeanCentralBankInterestRatesFetcher, "ICEBofA": FREDICEBofAFetcher, "MoodyCorporateBondIndex": FREDMoodyCorporateBondIndexFetcher, + "MortgageIndices": FredMortgageIndicesFetcher, "CommercialPaper": FREDCommercialPaperFetcher, "FredSearch": FredSearchFetcher, "FredSeries": FredSeriesFetcher, diff --git a/openbb_platform/providers/fred/openbb_fred/models/mortgage_indices.py b/openbb_platform/providers/fred/openbb_fred/models/mortgage_indices.py new file mode 100644 index 00000000000..68a59dca4fd --- /dev/null +++ b/openbb_platform/providers/fred/openbb_fred/models/mortgage_indices.py @@ -0,0 +1,312 @@ +"""FRED Mortgage Indices Model.""" + +# pylint: disable=unused-argument + +from typing import Any, Dict, List, Literal, Optional, Union +from warnings import warn + +from openbb_core.app.model.abstract.error import OpenBBError +from openbb_core.provider.abstract.annotated_result import AnnotatedResult +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.mortgage_indices import ( + MortgageIndicesData, + MortgageIndicesQueryParams, +) +from openbb_core.provider.utils.errors import EmptyDataError +from openbb_fred.models.series import FredSeriesFetcher +from pandas import Categorical, DataFrame +from pydantic import Field, field_validator + +MORTGAGE_ID_TO_TITLE = { + "OBMMIC30YF": "30-Year Fixed Rate Conforming", + "OBMMIC30YFNA": "30-Year Fixed Rate Conforming Non-Adjusted", + "OBMMIJUMBO30YF": "30-Year Fixed Rate Jumbo", + "OBMMIFHA30YF": "30-Year Fixed Rate FHA", + "OBMMIVA30YF": "30-Year Fixed Rate Veterans Affairs", + "OBMMIUSDA30YF": "30-Year Fixed Rate USDA", + "OBMMIC15YF": "15-Year Fixed Rate Conforming", + "OBMMIC30YFLVLE80FGE740": "30-Year Fixed Rate Conforming LTV <= 80 FICO >= 740", + "OBMMIC30YFLVLE80FB720A739": "30-Year Fixed Rate Conforming LTV <= 80 FICO 720-739", + "OBMMIC30YFLVLE80FB700A719": "30-Year Fixed Rate Conforming LTV <= 80 FICO 700-719", + "OBMMIC30YFLVLE80FB680A699": "30-Year Fixed Rate Conforming LTV <= 80 FICO 680-699", + "OBMMIC30YFLVLE80FLT680": "30-Year Fixed Rate Conforming LTV <= 80 FICO < 680", + "OBMMIC30YFLVGT80FGE740": "30-Year Fixed Rate Conforming LTV > 80 FICO >= 740", + "OBMMIC30YFLVGT80FB720A739": "30-Year Fixed Rate Conforming LTV > 80 FICO 720-739", + "OBMMIC30YFLVGT80FB700A719": "30-Year Fixed Rate Conforming LTV > 80 FICO 700-719", + "OBMMIC30YFLVGT80FB680A699": "30-Year Fixed Rate Conforming LTV > 80 FICO 680-699", + "OBMMIC30YFLVGT80FLT680": "30-Year Fixed Rate Conforming LTV > 80 FICO < 680", +} + +MORTGAGE_GROUPS = { + "primary": [ + "OBMMIC30YF", + "OBMMIC30YFNA", + "OBMMIJUMBO30YF", + "OBMMIFHA30YF", + "OBMMIVA30YF", + "OBMMIUSDA30YF", + "OBMMIC15YF", + ], + "ltv_lte_80": [ + "OBMMIC30YFLVLE80FGE740", + "OBMMIC30YFLVLE80FB720A739", + "OBMMIC30YFLVLE80FB700A719", + "OBMMIC30YFLVLE80FB680A699", + "OBMMIC30YFLVLE80FLT680", + ], + "ltv_gt_80": [ + "OBMMIC30YFLVGT80FGE740", + "OBMMIC30YFLVGT80FB720A739", + "OBMMIC30YFLVGT80FB700A719", + "OBMMIC30YFLVGT80FB680A699", + "OBMMIC30YFLVGT80FLT680", + ], +} + +MORTGAGE_CHOICES_TO_ID = { + "primary": ",".join(MORTGAGE_GROUPS["primary"]), + "ltv_lte_80": ",".join(MORTGAGE_GROUPS["ltv_lte_80"]), + "ltv_gt_80": ",".join(MORTGAGE_GROUPS["ltv_gt_80"]), + "conforming_30y": "OBMMIC30YF", + "conforming_30y_na": "OBMMIC30YFNA", + "jumbo_30y": "OBMMIJUMBO30YF", + "fha_30y": "OBMMIFHA30YF", + "va_30y": "OBMMIVA30YF", + "usda_30y": "OBMMIUSDA30YF", + "conforming_15y": "OBMMIC15YF", + "ltv_lte80_fico_ge740": "OBMMIC30YFLVLE80FGE740", + "ltv_lte80_fico_a720b739": "OBMMIC30YFLVLE80FB720A739", + "ltv_lte80_fico_a700b719": "OBMMIC30YFLVLE80FB700A719", + "ltv_lte80_fico_a680b699": "OBMMIC30YFLVLE80FB680A699", + "ltv_lte80_fico_lt680": "OBMMIC30YFLVLE80FLT680", + "ltv_gt80_fico_ge740": "OBMMIC30YFLVGT80FGE740", + "ltv_gt80_fico_a720b739": "OBMMIC30YFLVGT80FB720A739", + "ltv_gt80_fico_a700b719": "OBMMIC30YFLVGT80FB700A719", + "ltv_gt80_fico_a680b699": "OBMMIC30YFLVGT80FB680A699", + "ltv_gt80_fico_lt680": "OBMMIC30YFLVGT80FLT680", +} + +MortgageChoices = Literal[ + "primary", + "ltv_lte_80", + "ltv_gt_80", + "conforming_30y", + "conforming_30y_na", + "jumbo_30y", + "fha_30y", + "va_30y", + "usda_30y", + "conforming_15y", + "ltv_lte80_fico_ge740", + "ltv_lte80_fico_a720b739", + "ltv_lte80_fico_a700b719", + "ltv_lte80_fico_a680b699", + "ltv_lte80_fico_lt680", + "ltv_gt80_fico_ge740", + "ltv_gt80_fico_a720b739", + "ltv_gt80_fico_a700b719", + "ltv_gt80_fico_a680b699", + "ltv_gt80_fico_lt680", +] + + +class FredMortgageIndicesQueryParams(MortgageIndicesQueryParams): + """FRED Mortgage Indices Query.""" + + __json_schema_extra__ = {"index": {"multiple_items_allowed": True}} + + index: Union[MortgageChoices, str] = Field( + default="primary", + description="The specific index, or index group, to query. Default is the 'primary' group.", + choices=list(MORTGAGE_CHOICES_TO_ID.keys()), + ) + frequency: Union[ + None, + Literal[ + "a", + "q", + "m", + "w", + "d", + "wef", + "weth", + "wew", + "wetu", + "wem", + "wesu", + "wesa", + "bwew", + "bwem", + ], + ] = Field( + default=None, + description=""" + Frequency aggregation to convert daily data to lower frequency. + None = No change + a = Annual + q = Quarterly + m = Monthly + w = Weekly + d = Daily + wef = Weekly, Ending Friday + weth = Weekly, Ending Thursday + wew = Weekly, Ending Wednesday + wetu = Weekly, Ending Tuesday + wem = Weekly, Ending Monday + wesu = Weekly, Ending Sunday + wesa = Weekly, Ending Saturday + bwew = Biweekly, Ending Wednesday + bwem = Biweekly, Ending Monday + """, + json_schema_extra={ + "choices": [ + "a", + "q", + "m", + "w", + "d", + "wef", + "weth", + "wew", + "wetu", + "wem", + "wesu", + "wesa", + "bwew", + "bwem", + ] + }, + ) + aggregation_method: Literal["avg", "sum", "eop"] = Field( + default="avg", + description=""" + A key that indicates the aggregation method used for frequency aggregation. + This parameter has no affect if the frequency parameter is not set, default is 'avg'. + avg = Average + sum = Sum + eop = End of Period + """, + json_schema_extra={"choices": ["avg", "sum", "eop"]}, + ) + transform: Union[ + None, Literal["chg", "ch1", "pch", "pc1", "pca", "cch", "cca", "log"] + ] = Field( + default=None, + description=""" + Transformation type + None = No transformation + chg = Change + ch1 = Change from Year Ago + pch = Percent Change + pc1 = Percent Change from Year Ago + pca = Compounded Annual Rate of Change + cch = Continuously Compounded Rate of Change + cca = Continuously Compounded Annual Rate of Change + log = Natural Log + """, + json_schema_extra={ + "choices": ["chg", "ch1", "pch", "pc1", "pca", "cch", "cca", "log"] + }, + ) + + @field_validator("index", mode="before", check_fields=False) + @classmethod + def validate_index(cls, v): + """Validate index.""" + indices = v.split(",") + new_indices: List = [] + for index in indices: + if index in MORTGAGE_CHOICES_TO_ID: + new_indices.append(index) + else: + warn(f"Invalid index '{index}' will be ignored.") + if not new_indices: + raise OpenBBError( + f"No valid indices found. Must be any of: {list(MORTGAGE_CHOICES_TO_ID.keys())}" + ) + return ",".join(new_indices) + + +class FredMortgageIndicesData(MortgageIndicesData): + """FRED Mortgage Indices Data.""" + + +class FredMortgageIndicesFetcher( + Fetcher[ + FredMortgageIndicesQueryParams, + List[FredMortgageIndicesData], + ] +): + """FRED Mortgage Indices Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> FredMortgageIndicesQueryParams: + """Transform query.""" + return FredMortgageIndicesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: FredMortgageIndicesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict: + """Extract data.""" + indices = query.index.split(",") + ids = [MORTGAGE_CHOICES_TO_ID[index] for index in indices] + try: + response = await FredSeriesFetcher.fetch_data( + dict( + symbol=",".join(ids), + start_date=query.start_date, + end_date=query.end_date, + transform=query.transform, + frequency=query.frequency, + aggregation_method=query.aggregation_method, + ), + credentials, + ) + except Exception as e: + raise e from e + + return { + "metadata": response.metadata, + "data": [d.model_dump() for d in response.result], + } + + @staticmethod + def transform_data( + query: FredMortgageIndicesQueryParams, + data: Dict, + **kwargs: Any, + ) -> AnnotatedResult[List[FredMortgageIndicesData]]: + """Transform data.""" + if not data.get("data"): + raise EmptyDataError("The request was returned empty.") + df = DataFrame.from_records(data["data"]) + metadata = data.get("metadata", {}) + # Flatten the data. + df = ( + df.melt(id_vars="date", var_name="symbol", value_name="value") + .query("value.notnull()") + .rename(columns={"value": "rate"}) + ) + df["name"] = df.symbol.map(MORTGAGE_ID_TO_TITLE) + # Normalize the percent values. + df["rate"] = df["rate"] / 100 + df = df.fillna("N/A").replace("N/A", None) + df["name"] = Categorical( + df["name"], + categories=[ + d + for d in list(MORTGAGE_ID_TO_TITLE.values()) + if d in df["name"].unique() + ], + ordered=True, + ) + df.sort_values(["date", "name"], inplace=True) + records = df.to_dict(orient="records") + + return AnnotatedResult( + result=[FredMortgageIndicesData.model_validate(r) for r in records], + metadata=metadata, + ) diff --git a/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_bond_mortgage_fetcher.yaml b/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_bond_mortgage_fetcher.yaml new file mode 100644 index 00000000000..c86b1408abb --- /dev/null +++ b/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_bond_mortgage_fetcher.yaml @@ -0,0 +1,105 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://api.stlouisfed.org/fred/series/observations?aggregation_method=avg&api_key=MOCK_API_KEY&file_type=json&limit=100000&observation_end=2024-06-04&observation_start=2024-06-01&series_id=OBMMIJUMBO30YF + response: + body: + string: !!binary | + H4sIAAAAAAAEA6pWKkpNzCnJzE2NLy5JLCpRslIyMjAy0TUw0zU0UdJByKbmpaDL5ScVpxaVJZZk + 5udhaDYwVNJRQlaAqt8AZHZpXmZJsZKVUk5mHkhxaUlBaUl8SWVBqpKVoY5SWmZOKpSnlFWcD1ZS + lJJaFJ9UqWSFYnRKYkmqko5ScX5RSXw+SImSlVJicbKSjlJyfmleiZKVkY5SflpacWqJkpWBjlJO + Zm5miZKVoQEIoDiyWMkqmpIAATsEEYAGxko6SmWJOaWpSlZK5nqGppZKtTrUNB8UiEjmA2ZmrFQb + WwsAU848LNIBAAA= + headers: + Cache-Control: + - max-age=0, no-cache + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '239' + Content-Type: + - application/json; charset=UTF-8 + Date: + - Fri, 14 Jun 2024 05:34:53 GMT + Expires: + - Fri, 14 Jun 2024 05:34:53 GMT + Last-Modified: + - Thu, 13 Jun 2024 12:01:06 GMT + Pragma: + - no-cache + Server: + - Apache + Strict-Transport-Security: + - max-age=86400 + Vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://api.stlouisfed.org/fred/series?api_key=MOCK_API_KEY&file_type=json&series_id=OBMMIJUMBO30YF + response: + body: + string: !!binary | + H4sIAAAAAAAEA6pWKkpNzCnJzE2NLy5JLCpRslIyMjAy0TUw0zU0VtJByKbmpaDLFacWZaYWFytZ + RVcrZYJk/Z18fT29Qn2d/I0NIt2QdZNsdklmSU6qkpWSsYFuZGpikYJbZkVqikJQYkmqgldpblK+ + gm9+UUl6YnqqgmdeSmqFko5SflJxalFZYklmfh6SVwzNdQ0MdQ1AXkFWgOYbIyUdpbSi1MLS1Lzk + SiUrJZfEzJxKZLH44ox8cOC4KOkoleZllhQrWSkFpBYlp+aVwETgalSVdJSKUxOL8/MSc+ITU7JK + i0tyQeqslPzySxSCoTI5lQqOYLnUFOzq4eb5BTsq6SjlJBaXxJcWpCSWpILCGhFLCgbmVgaGVgZm + ugamSjpKBfkFpTmJRZkllUpWZqY6Snn5Jakg1/oXlGTmJuYoOOWUpiJCzzexKDu1BBSImcmpxQoa + GSUlBcVWMfox+uXl5UZ6+RBNSTmlqXrJ+bkx+vlJubmZMfqaMaVGhkZGChr+oCiHcDQVMosVkhNz + kktzQG5USCvKz1VITC4pTcxRyMlPzk5NUShKLEktVijPLMlQSM7PKy7NTS0qVkhMLsovLlbIL0st + UsjPS9UtycgsSlHIT1NIzMlRyIVFc0lRYl5xYjIoeosV8sDRXJ6ZkqqnAHaBQmZeck5pSmqxQm5p + TklmQU4qQmdBUWZyZl66QmZeCtiPKallqTn5BakpColF+aV5KQolGSDVxSUK0KBTKCjKTylNLilW + SMxLUSguSE3OTMtMVkjKLyrKL08tAovm5CfmKeSATFJILCkpykwqLUkt1ovJi8lzTUzOANmVWoEW + HonFYJsSy1KLQOkW6sHEgoKi/IKiTFDKBgUPOKiKwWQqyGlF+aXpGWCNKBEIdaJCak5memZSZk5m + SSXYXTDPpualZ+alKuTnKSQqpGeWpeYppCRW6oHiPVUhJbUkMTOnWCExKb+0RCE3tSQjPyU/Jz8d + YkJKalpmXiYknBOLUhUSyxIzcxKTclIVMlKLUolPIYDpKdXG1gIAFK52ql8EAAA= + headers: + Cache-Control: + - max-age=0, no-cache + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '788' + Content-Type: + - application/json; charset=UTF-8 + Date: + - Fri, 14 Jun 2024 05:34:53 GMT + Expires: + - Fri, 14 Jun 2024 05:34:53 GMT + Last-Modified: + - Thu, 13 Jun 2024 12:01:06 GMT + Pragma: + - no-cache + Server: + - Apache + Strict-Transport-Security: + - max-age=86400 + Vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/openbb_platform/providers/fred/tests/test_fred_fetchers.py b/openbb_platform/providers/fred/tests/test_fred_fetchers.py index 736098c7247..16ab9ae7916 100644 --- a/openbb_platform/providers/fred/tests/test_fred_fetchers.py +++ b/openbb_platform/providers/fred/tests/test_fred_fetchers.py @@ -21,6 +21,7 @@ from openbb_fred.models.hqm import FREDHighQualityMarketCorporateBondFetcher from openbb_fred.models.ice_bofa import FREDICEBofAFetcher from openbb_fred.models.iorb_rates import FREDIORBFetcher from openbb_fred.models.moody import FREDMoodyCorporateBondIndexFetcher +from openbb_fred.models.mortgage_indices import FredMortgageIndicesFetcher from openbb_fred.models.regional import FredRegionalDataFetcher from openbb_fred.models.retail_prices import FredRetailPricesFetcher from openbb_fred.models.search import ( @@ -357,3 +358,17 @@ def test_fred_bond_indices_fetcher(credentials=test_credentials): fetcher = FredBondIndicesFetcher() result = fetcher.test(params, credentials) assert result is None + + +@pytest.mark.record_http +def test_fred_bond_mortgage_fetcher(credentials=test_credentials): + """Test FredMortgageIndicesFetcher.""" + params = { + "index": "jumbo_30y", + "start_date": datetime.date(2024, 6, 1), + "end_date": datetime.date(2024, 6, 4), + } + + fetcher = FredMortgageIndicesFetcher() + result = fetcher.test(params, credentials) + assert result is None -- cgit v1.2.3 From c5e16684baab63ce93deeb7641169ae4d3a1cf4a Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Sun, 16 Jun 2024 15:13:46 -0700 Subject: [Feature] Intrinio Delayed Options (#6505) * stashing * intrinio delayed options * stuff and things * test cassettes and static assets * options snapshots * description * commit intrinio files --------- Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com> --- .../provider/standard_models/options_chains.py | 42 +- .../provider/standard_models/options_snapshots.py | 75 + .../integration/test_derivatives_api.py | 37 +- .../integration/test_derivatives_python.py | 34 +- .../openbb_derivatives/options/options_router.py | 18 + openbb_platform/openbb/assets/reference.json | 483 +- .../openbb/package/derivatives_options.py | 168 +- .../cboe/openbb_cboe/models/options_chains.py | 22 +- .../providers/intrinio/openbb_intrinio/__init__.py | 2 + .../openbb_intrinio/models/options_chains.py | 253 +- .../openbb_intrinio/models/options_snapshots.py | 329 + .../test_intrinio_options_chains_fetcher.yaml | 7211 +++++++++++--------- .../intrinio/tests/test_intrinio_fetchers.py | 11 + .../tmx/openbb_tmx/models/options_chains.py | 14 +- .../providers/tmx/openbb_tmx/utils/helpers.py | 2 +- .../test_tmx_options_chains_fetcher.yaml | 2541 +++---- .../openbb_tradier/models/options_chains.py | 70 +- ...tradier_derivatives_options_chains_fetcher.yaml | 4189 +++++------- .../openbb_yfinance/models/options_chains.py | 29 +- 19 files changed, 8708 insertions(+), 6822 deletions(-) create mode 100644 openbb_platform/core/openbb_core/provider/standard_models/options_snapshots.py create mode 100644 openbb_platform/providers/intrinio/openbb_intrinio/models/options_snapshots.py diff --git a/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py b/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py index 6a23f687f5d..38aca52b57d 100644 --- a/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py +++ b/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py @@ -31,23 +31,29 @@ class OptionsChainsQueryParams(QueryParams): class OptionsChainsData(Data): """Options Chains Data.""" - symbol: Optional[str] = Field( - description=DATA_DESCRIPTIONS.get("symbol", "") - + " Here, it is the underlying symbol for the option.", + underlying_symbol: Optional[str] = Field( default=None, + description="Underlying symbol for the option.", + ) + underlying_price: Optional[float] = Field( + default=None, + description="Price of the underlying stock.", ) contract_symbol: str = Field(description="Contract symbol for the option.") eod_date: Optional[dateType] = Field( default=None, description="Date for which the options chains are returned." ) expiration: dateType = Field(description="Expiration date of the contract.") + dte: Optional[int] = Field( + default=None, description="Days to expiration of the contract." + ) strike: float = Field(description="Strike price of the contract.") option_type: str = Field(description="Call or Put.") open_interest: Optional[int] = Field( - default=None, description="Open interest on the contract." + default=0, description="Open interest on the contract." ) volume: Optional[int] = Field( - default=None, description=DATA_DESCRIPTIONS.get("volume", "") + default=0, description=DATA_DESCRIPTIONS.get("volume", "") ) theoretical_price: Optional[float] = Field( default=None, description="Theoretical value of the option." @@ -55,6 +61,13 @@ class OptionsChainsData(Data): last_trade_price: Optional[float] = Field( default=None, description="Last trade price of the option." ) + last_trade_size: Optional[int] = Field( + default=None, description="Last trade size of the option." + ) + last_trade_time: Optional[datetime] = Field( + default=None, + description="The timestamp of the last trade.", + ) tick: Optional[str] = Field( default=None, description="Whether the last tick was up or down in price." ) @@ -64,12 +77,26 @@ class OptionsChainsData(Data): bid_size: Optional[int] = Field( default=None, description="Bid size for the option." ) + bid_time: Optional[datetime] = Field( + default=None, + description="The timestamp of the bid price.", + ) + bid_exchange: Optional[str] = Field( + default=None, description="The exchange of the bid price." + ) ask: Optional[float] = Field( default=None, description="Current ask price for the option." ) ask_size: Optional[int] = Field( default=None, description="Ask size for the option." ) + ask_time: Optional[datetime] = Field( + default=None, + description="The timestamp of the ask price.", + ) + ask_exchange: Optional[str] = Field( + default=None, description="The exchange of the ask price." + ) mark: Optional[float] = Field( default=None, description="The mid-price between the latest bid and ask." ) @@ -138,7 +165,8 @@ class OptionsChainsData(Data): ) change_percent: Optional[float] = Field( default=None, - description="Change, in normalizezd percentage points, of the option.", + description="Change, in normalized percentage points, of the option.", + json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100}, ) implied_volatility: Optional[float] = Field( default=None, description="Implied volatility of the option." @@ -151,7 +179,7 @@ class OptionsChainsData(Data): @field_validator("expiration", mode="before", check_fields=False) @classmethod - def date_validate(cls, v): # pylint: disable=E0213 + def date_validate(cls, v): """Return the datetime object from the date string.""" if isinstance(v, datetime): return datetime.strftime(v, "%Y-%m-%d") diff --git a/openbb_platform/core/openbb_core/provider/standard_models/options_snapshots.py b/openbb_platform/core/openbb_core/provider/standard_models/options_snapshots.py new file mode 100644 index 00000000000..8c14af09ffd --- /dev/null +++ b/openbb_platform/core/openbb_core/provider/standard_models/options_snapshots.py @@ -0,0 +1,75 @@ +"""Options Snapshots Standard Model.""" + +from datetime import ( + date as dateType, + datetime, +) +from typing import Optional + +from pydantic import Field + +from openbb_core.provider.abstract.data import Data +from openbb_core.provider.abstract.query_params import QueryParams +from openbb_core.provider.utils.descriptions import DATA_DESCRIPTIONS + + +class OptionsSnapshotsQueryParams(QueryParams): + """Options Snapshots Query.""" + + +class OptionsSnapshotsData(Data): + """Options Snapshots Data.""" + + underlying_symbol: str = Field(description="Ticker symbol of the underlying asset.") + contract_symbol: str = Field(description="Symbol of the options contract.") + expiration: dateType = Field(description="Expiration date of the options contract.") + dte: Optional[int] = Field( + default=None, + description="Number of days to expiration of the options contract.", + ) + strike: float = Field( + description="Strike price of the options contract.", + json_schema_extra={"x-unit_measurement": "currency"}, + ) + option_type: str = Field(description="The type of option.") + volume: Optional[int] = Field( + default=None, + description=DATA_DESCRIPTIONS.get("volume", ""), + ) + open_interest: Optional[int] = Field( + default=None, + description="Open interest at the time.", + ) + last_price: Optional[float] = Field( + default=None, + description="Last trade price at the time.", + json_schema_extra={"x-unit_measurement": "currency"}, + ) + last_size: Optional[int] = Field( + default=None, + description="Lot size of the last trade.", + ) + last_timestamp: Optional[datetime] = Field( + default=None, + description="Timestamp of the last price.", + ) + open: Optional[float] = Field( + default=None, + description=DATA_DESCRIPTIONS.get("open", ""), + json_schema_extra={"x-unit_measurement": "currency"}, + ) + high: Optional[float] = Field( + default=None, + description=DATA_DESCRIPTIONS.get("high", ""), + json_schema_extra={"x-unit_measurement": "currency"}, + ) + low: Optional[float] = Field( + default=None, + description=DATA_DESCRIPTIONS.get("low", ""), + json_schema_extra={"x-unit_measurement": "currency"}, + ) + close: Optional[float] = Field( + default=None, + description=DATA_DESCRIPTIONS.get("close", ""), + json_schema_extra={"x-unit_measurement": "currency"}, + ) diff --git a/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py b/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py index 210ab79190c..97e9a3551eb 100644 --- a/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py +++ b/openbb_platform/extensions/derivatives/integration/test_derivatives_api.py @@ -24,7 +24,24 @@ def headers(): @parametrize( "params", [ - ({"provider": "intrinio", "symbol": "AAPL", "date": "2023-01-25"}), + ( + { + "provider": "intrinio", + "symbol": "AAPL", + "date": "2023-01-25", + "option_type": None, + "moneyness": "all", + "strike_gt": None, + "strike_lt": None, + "volume_gt": None, + "volume_lt": None, + "oi_gt": None, + "oi_lt": None, + "model": "black_scholes", + "show_extended_price": False, + "include_related_symbols": False, + } + ), ({"provider": "cboe", "symbol": "AAPL", "use_cache": False}), ({"provider": "tradier", "symbol": "AAPL"}), ({"provider": "yfinance", "symbol": "AAPL"}), @@ -125,3 +142,21 @@ def test_derivatives_futures_curve(params, headers): result = requests.get(url, headers=headers, timeout=60) assert isinstance(result, requests.Response) assert result.status_code == 200 + + +@parametrize( + "params", + [ + ({"provider": "intrinio", "date": None, "only_traded": True}), + ], +) +@pytest.mark.integration +def test_derivatives_options_snapshots(params, headers): + """Test the options snapshots endpoint.""" + 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/derivatives/options/snapshots?{query_str}" + result = requests.get(url, headers=headers, timeout=60) + assert isinstance(result, requests.Response) + assert result.status_code == 200 diff --git a/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py b/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py index 1bc0597855f..4e04039bc7a 100644 --- a/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py +++ b/openbb_platform/extensions/derivatives/integration/test_derivatives_python.py @@ -20,7 +20,24 @@ def obb(pytestconfig): @parametrize( "params", [ - ({"provider": "intrinio", "symbol": "AAPL", "date": "2023-01-25"}), + ( + { + "provider": "intrinio", + "symbol": "AAPL", + "date": "2023-01-25", + "option_type": None, + "moneyness": "all", + "strike_gt": None, + "strike_lt": None, + "volume_gt": None, + "volume_lt": None, + "oi_gt": None, + "oi_lt": None, + "model": "black_scholes", + "show_extended_price": False, + "include_related_symbols": False, + } + ), ({"provider": "cboe", "symbol": "AAPL", "use_cache": False}), ({"provider": "tradier", "symbol": "AAPL"}), ({"provider": "yfinance", "symbol": "AAPL"}), @@ -109,3 +126,18 @@ def test_derivatives_futures_curve(params, obb): assert result assert isinstance(result, OBBject) assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ({"provider": "intrinio", "date": None, "only_traded": True}), + ], +) +@pytest.mark.integration +def test_derivatives_options_snapshots(params, obb): + """Test the options snapshots endpoint.""" + result = obb.derivatives.options.snapshots(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 diff --git a/openbb_platform/extensions/derivatives/openbb_derivatives/options/options_router.py b/openbb_platform/extensions/derivatives/openbb_derivatives/options/options_router.py index dbed2aa674d..bcdb00be4b7 100644 --- a/openbb_platform/extensions/derivatives/openbb_derivatives/options/options_router.py +++ b/openbb_platform/extensions/derivatives/openbb_derivatives/options/options_router.py @@ -54,3 +54,21 @@ async def unusual( ) -> OBBject: """Get the complete options chain for a ticker.""" return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="OptionsSnapshots", + examples=[ + APIEx( + parameters={"provider": "intrinio"}, + ), + ], +) +async def snapshots( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get a snapshot of the options market universe.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/openbb/assets/reference.json b/openbb_platform/openbb/assets/reference.json index ca1dcb3f376..efdab571fc0 100644 --- a/openbb_platform/openbb/assets/reference.json +++ b/openbb_platform/openbb/assets/reference.json @@ -1213,6 +1213,101 @@ "default": null, "optional": true, "choices": null + }, + { + "name": "option_type", + "type": "Literal[None, Union[ForwardRef('call'), ForwardRef('put')]]", + "description": "The option type, call or put, 'None' is both (default).", + "default": null, + "optional": true, + "choices": [ + "call", + "put" + ] + }, + { + "name": "moneyness", + "type": "Literal['otm', 'itm', 'all']", + "description": "Return only contracts that are in or out of the money, default is 'all'. Parameter is ignored when a date is supplied.", + "default": "all", + "optional": true, + "choices": [ + "otm", + "itm", + "all" + ] + }, + { + "name": "strike_gt", + "type": "int", + "description": "Return options with a strike price greater than the given value. Parameter is ignored when a date is supplied.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "strike_lt", + "type": "int", + "description": "Return options with a strike price less than the given value. Parameter is ignored when a date is supplied.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "volume_gt", + "type": "int", + "description": "Return options with a volume greater than the given value. Parameter is ignored when a date is supplied.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "volume_lt", + "type": "int", + "description": "Return options with a volume less than the given value. Parameter is ignored when a date is supplied.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "oi_gt", + "type": "int", + "description": "Return options with an open interest greater than the given value. Parameter is ignored when a date is supplied.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "oi_lt", + "type": "int", + "description": "Return options with an open interest less than the given value. Parameter is ignored when a date is supplied.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "model", + "type": "Literal['black_scholes', 'bjerk']", + "description": "The pricing model to use for options chains data, default is 'black_scholes'. Parameter is ignored when a date is supplied.", + "default": "black_scholes", + "optional": true, + "choices": null + }, + { + "name": "show_extended_price", + "type": "bool", + "description": "Whether to include OHLC type fields, default is True. Parameter is ignored when a date is supplied.", + "default": true, + "optional": true, + "choices": null + }, + { + "name": "include_related_symbols", + "type": "bool", + "description": "Include related symbols that end in a 1 or 2 because of a corporate action, default is False.", + "default": false, + "optional": true, + "choices": null } ], "yfinance": [] @@ -1249,9 +1344,17 @@ "data": { "standard": [ { - "name": "symbol", + "name": "underlying_symbol", "type": "str", - "description": "Symbol representing the entity requested in the data. Here, it is the underlying symbol for the option.", + "description": "Underlying symbol for the option.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "underlying_price", + "type": "float", + "description": "Price of the underlying stock.", "default": null, "optional": true, "choices": null @@ -1280,6 +1383,14 @@ "optional": false, "choices": null }, + { + "name": "dte", + "type": "int", + "description": "Days to expiration of the contract.", + "default": null, + "optional": true, + "choices": null + }, { "name": "strike", "type": "float", @@ -1300,7 +1411,7 @@ "name": "open_interest", "type": "int", "description": "Open interest on the contract.", - "default": null, + "default": 0, "optional": true, "choices": null }, @@ -1308,7 +1419,7 @@ "name": "volume", "type": "int", "description": "The trading volume.", - "default": null, + "default": 0, "optional": true, "choices": null }, @@ -1328,6 +1439,22 @@ "optional": true, "choices": null }, + { + "name": "last_trade_size", + "type": "int", + "description": "Last trade size of the option.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "last_trade_time", + "type": "datetime", + "description": "The timestamp of the last trade.", + "default": null, + "optional": true, + "choices": null + }, { "name": "tick", "type": "str", @@ -1352,6 +1479,22 @@ "optional": true, "choices": null }, + { + "name": "bid_time", + "type": "datetime", + "description": "The timestamp of the bid price.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "bid_exchange", + "type": "str", + "description": "The exchange of the bid price.", + "default": null, + "optional": true, + "choices": null + }, { "name": "ask", "type": "float", @@ -1368,6 +1511,22 @@ "optional": true, "choices": null }, + { + "name": "ask_time", + "type": "datetime", + "description": "The timestamp of the ask price.", + "default": null, + "optional": true, + "choices": null + }, + {