diff --git a/openbb_platform/core/openbb_core/provider/standard_models/personal_consumption_expenditures.py b/openbb_platform/core/openbb_core/provider/standard_models/personal_consumption_expenditures.py new file mode 100644 index 00000000000..1c621951c71 --- /dev/null +++ b/openbb_platform/core/openbb_core/provider/standard_models/personal_consumption_expenditures.py @@ -0,0 +1,31 @@ +"""Personal Consumption Expenditures Standard Model.""" + +from datetime import date as dateType +from typing import Optional, Union + +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 PersonalConsumptionExpendituresQueryParams(QueryParams): + """Personal Consumption Expenditures Query.""" + + date: Optional[Union[dateType, str]] = Field( + default=None, + description=QUERY_DESCRIPTIONS.get("date", "") + + " Default is the latest report.", + ) + + +class PersonalConsumptionExpendituresData(Data): + """Personal Consumption Expenditures Data.""" + + date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", "")) + symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", "")) + value: float = Field(description=DATA_DESCRIPTIONS.get("value", "")) diff --git a/openbb_platform/extensions/economy/integration/test_economy_api.py b/openbb_platform/extensions/economy/integration/test_economy_api.py index e0ac235994f..247fedab539 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_api.py +++ b/openbb_platform/extensions/economy/integration/test_economy_api.py @@ -973,3 +973,27 @@ def test_economy_survey_nonfarm_payrolls(params, headers): result = requests.get(url, headers=headers, timeout=10) assert isinstance(result, requests.Response) assert result.status_code == 200 + + +@parametrize( + "params", + [ + ( + { + "provider": "fred", + "date": "2024-05-01,2024-04-01,2023-05-01", + "category": "pce_price_index", + } + ), + ], +) +@pytest.mark.integration +def test_economy_pce(params, headers): + """Test the economy pce 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/economy/pce?{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/economy/integration/test_economy_python.py b/openbb_platform/extensions/economy/integration/test_economy_python.py index 33665c2d1ba..b65a8b75a6a 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_python.py +++ b/openbb_platform/extensions/economy/integration/test_economy_python.py @@ -921,3 +921,26 @@ def test_economy_survey_nonfarm_payrolls(params, obb): assert result assert isinstance(result, OBBject) assert len(result.results) > 0 + + +@parametrize( + "params", + [ + ( + { + "provider": "fred", + "date": "2024-05-01,2024-04-01,2023-05-01", + "category": "pce_price_index", + } + ), + ], +) +@pytest.mark.integration +def test_economy_pce(params, obb): + """Test the economy pce endpoint""" + params = {p: v for p, v in params.items() if v} + + result = obb.economy.pce(**params) + assert result + assert isinstance(result, OBBject) + assert len(result.results) > 0 diff --git a/openbb_platform/extensions/economy/openbb_economy/economy_router.py b/openbb_platform/extensions/economy/openbb_economy/economy_router.py index 308994c2a93..f89fb8fdeaf 100644 --- a/openbb_platform/extensions/economy/openbb_economy/economy_router.py +++ b/openbb_platform/extensions/economy/openbb_economy/economy_router.py @@ -539,3 +539,27 @@ async def primary_dealer_positioning( ) -> OBBject: """Get Primary dealer positioning statistics.""" return await OBBject.from_query(Query(**locals())) + + +@router.command( + model="PersonalConsumptionExpenditures", + examples=[ + APIEx(parameters={"provider": "fred"}), + APIEx( + description="Get reports for multiple dates, entered as a comma-separated string.", + parameters={ + "provider": "fred", + "date": "2024-05-01,2024-04-01,2023-05-01", + "category": "pce_price_index", + }, + ), + ], +) +async def pce( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> OBBject: + """Get Personal Consumption Expenditures (PCE) reports.""" + return await OBBject.from_query(Query(**locals())) diff --git a/openbb_platform/openbb/assets/reference.json b/openbb_platform/openbb/assets/reference.json index f9ff83f1b57..8ac8be1e600 100644 --- a/openbb_platform/openbb/assets/reference.json +++ b/openbb_platform/openbb/assets/reference.json @@ -7955,6 +7955,160 @@ }, "model": "PrimaryDealerPositioning" }, + "/economy/pce": { + "deprecated": { + "flag": null, + "message": null + }, + "description": "Get Personal Consumption Expenditures (PCE) reports.", + "examples": "\nExamples\n--------\n\n```python\nfrom openbb import obb\nobb.economy.pce(provider='fred')\n# Get reports for multiple dates, entered as a comma-separated string.\nobb.economy.pce(provider='fred', date='2024-05-01,2024-04-01,2023-05-01', category=pce_price_index)\n```\n\n", + "parameters": { + "standard": [ + { + "name": "date", + "type": "Union[Union[Union[str, date], str], List[Union[Union[str, date], str]]]", + "description": "A specific date to get data for. Default is the latest report. Multiple items allowed for provider(s): fred.", + "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: fred.", + "default": null, + "optional": true + } + ], + "fred": [ + { + "name": "category", + "type": "Literal['personal_income', 'wages_by_industry', 'real_pce_percent_change', 'real_pce_quantity_index', 'pce_price_index', 'pce_dollars', 'real_pce_chained_dollars', 'pce_price_percent_change']", + "description": "The category to query.", + "default": "personal_income", + "optional": true, + "choices": [ + "personal_income", + "wages_by_industry", + "real_pce_percent_change", + "real_pce_quantity_index", + "pce_price_index", + "pce_dollars", + "real_pce_chained_dollars", + "pce_price_percent_change" + ] + } + ] + }, + "returns": { + "OBBject": [ + { + "name": "results", + "type": "List[PersonalConsumptionExpenditures]", + "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": "", + "optional": false, + "choices": null + }, + { + "name": "value", + "type": "float", + "description": "", + "default": "", + "optional": false, + "choices": null + } + ], + "fred": [ + { + "name": "name", + "type": "str", + "description": "The name of the series.", + "default": "", + "optional": false, + "choices": null + }, + { + "name": "element_id", + "type": "str", + "description": "The element id in the parent/child relationship.", + "default": "", + "optional": false, + "choices": null + }, + { + "name": "parent_id", + "type": "str", + "description": "The parent id in the parent/child relationship.", + "default": "", + "optional": false, + "choices": null + }, + { + "name": "children", + "type": "str", + "description": "The element_id of each child, as a comma-separated string.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "level", + "type": "int", + "description": "The indentation level of the element.", + "default": "", + "optional": false, + "choices": null + }, + { + "name": "line", + "type": "int", + "description": "The line number of the series in the table.", + "default": "", + "optional": false, + "choices": null + } + ] + }, + "model": "PersonalConsumptionExpenditures" + }, "/equity/calendar/ipo": { "deprecated": { "flag": null, diff --git a/openbb_platform/openbb/package/economy.py b/openbb_platform/openbb/package/economy.py index cc40c09eaff..906f88fd4f9 100644 --- a/openbb_platform/openbb/package/economy.py +++ b/openbb_platform/openbb/package/economy.py @@ -29,6 +29,7 @@ class ROUTER_economy(Container): indicators long_term_interest_rate money_measures + pce primary_dealer_positioning retail_prices risk_premium @@ -1806,6 +1807,96 @@ class ROUTER_economy(Container): ) ) + @exception_handler + @validate + def pce( + self, + date: Annotated[ + Union[str, datetime.date, None, List[Union[str, datetime.date, None]]], + OpenBBField( + description="A specific date to get data for. Default is the latest report. Multiple comma separated items allowed for provider(s): fred." + ), + ] = 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: + """Get Personal Consumption Expenditures (PCE) reports. + + Parameters + ---------- + date : Union[str, datetime.date, None, List[Union[str, datetime.d... + A specific date to get data for. Default is the latest report. Multiple comma separated items allowed for provider(s): fred. + 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. + category : Literal['personal_income', 'wages_by_industry', 'real_pce_percent_change', 'real_pce_quantity_index', 'pce_price_index', 'pce_dollars', 'real_pce_chained_dollars', 'pce_price_percent_change'] + The category to query. (provider: fred) + + Returns + ------- + OBBject + results : List[PersonalConsumptionExpenditures] + 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. + + PersonalConsumptionExpenditures + ------------------------------- + date : date + The date of the data. + symbol : str + Symbol representing the entity requested in the data. + value : float + + name : Optional[str] + The name of the series. (provider: fred) + element_id : Optional[str] + The element id in the parent/child relationship. (provider: fred) + parent_id : Optional[str] + The parent id in the parent/child relationship. (provider: fred) + children : Optional[str] + The element_id of each child, as a comma-separated string. (provider: fred) + level : Optional[int] + The indentation level of the element. (provider: fred) + line : Optional[int] + The line number of the series in the table. (provider: fred) + + Examples + -------- + >>> from openbb import obb + >>> obb.economy.pce(provider='fred') + >>> # Get reports for multiple dates, entered as a comma-separated string. + >>> obb.economy.pce(provider='fred', date='2024-05-01,2024-04-01,2023-05-01', category='pce_price_index') + """ # noqa: E501 + + return self._run( + "/economy/pce", + **filter_inputs( + provider_choices={ + "provider": self._get_provider( + provider, + "economy.pce", + ("fred",), + ) + }, + standard_params={ + "date": date, + }, + extra_params=kwargs, + info={"date": {"fred": {"multiple_items_allowed": True}}}, + ) + ) + @exception_handler @validate def primary_dealer_positioning( diff --git a/openbb_platform/providers/fred/openbb_fred/__init__.py b/openbb_platform/providers/fred/openbb_fred/__init__.py index 0ec012b1bb6..b9747d3d68a 100644 --- a/openbb_platform/providers/fred/openbb_fred/__init__.py +++ b/openbb_platform/providers/fred/openbb_fred/__init__.py @@ -28,6 +28,9 @@ from openbb_fred.models.non_farm_payrolls import FredNonFarmPayrollsFetcher from openbb_fred.models.overnight_bank_funding_rate import ( FredOvernightBankFundingRateFetcher, ) +from openbb_fred.models.personal_consumption_expenditures import ( + FredPersonalConsumptionExpendituresFetcher, +) from openbb_fred.models.regional import FredRegionalDataFetcher from openbb_fred.models.retail_prices import FredRetailPricesFetcher from openbb_fred.models.search import ( @@ -78,6 +81,7 @@ Research division of the Federal Reserve Bank of St. Louis that has more than "MortgageIndices": FredMortgageIndicesFetcher, "NonFarmPayrolls": FredNonFarmPayrollsFetcher, "OvernightBankFundingRate": FredOvernightBankFundingRateFetcher, + "PersonalConsumptionExpenditures": FredPersonalConsumptionExpendituresFetcher, "CommercialPaper": FREDCommercialPaperFetcher, "FredSearch": FredSearchFetcher, "FredSeries": FredSeriesFetcher, diff --git a/openbb_platform/providers/fred/openbb_fred/models/personal_consumption_expenditures.py b/openbb_platform/providers/fred/openbb_fred/models/personal_consumption_expenditures.py new file mode 100644 index 00000000000..effad5ff088 --- /dev/null +++ b/openbb_platform/providers/fred/openbb_fred/models/personal_consumption_expenditures.py @@ -0,0 +1,246 @@ +"""FRED Personal Consumption Expenditures Model.""" + +# pylint: disable=unused-argument + +from datetime import date as dateType +from typing import Any, Dict, List, Literal, Optional + +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.personal_consumption_expenditures import ( + PersonalConsumptionExpendituresData, + PersonalConsumptionExpendituresQueryParams, +) +from openbb_core.provider.utils.errors import EmptyDataError +from pydantic import Field, field_validator + +PCE_CATEGORY_TO_EID = { + "personal_income": "155443", + "wages_by_industry": "3151", + "real_pce_percent_change": "3160", + "real_pce_quantity_index": "3196", + "pce_price_index": "3208", + "pce_dollars": "3220", + "real_pce_chained_dollars": "3232", + "pce_price_percent_change": "3172", +} + + +class FredPersonalConsumptionExpendituresQueryParams( + PersonalConsumptionExpendituresQueryParams +): + """FRED Personal Consumption Expenditures Query.""" + + __json_schema_extra__ = {"date": {"multiple_items_allowed": True}} + + category: Literal[ + "personal_income", + "wages_by_industry", + "real_pce_percent_change", + "real_pce_quantity_index", + "pce_price_index", + "pce_dollars", + "real_pce_chained_dollars", + "pce_price_percent_change", + ] = Field( + default="personal_income", + description="The category to query.", + json_schema_extra={"choices": list(PCE_CATEGORY_TO_EID)}, + ) + + @field_validator("date", mode="before", check_fields=False) + @classmethod + def validate_date(cls, v): + """Validate the dates entered.""" + if v is None: + return None + if isinstance(v, (list, dateType)): + return v + new_dates: List = [] + date_param = v + if isinstance(date_param, str): + new_dates = date_param.split(",") + elif isinstance(date_param, dateType): + new_dates.append(date_param.strftime("%Y-%m-%d")) + elif isinstance(date_param, list) and isinstance(date_param[0], dateType): + new_dates = [d.strftime("%Y-%m-%d") for d in new_dates] + else: + new_dates = date_param + return ",".join(new_dates) if len(new_dates) > 1 else new_dates[0] + + +class FredPersonalConsumptionExpendituresData(PersonalConsumptionExpendituresData): + """FRED Personal Consumption Expenditures Data.""" + + __alias_dict__ = { + "date": "observation_date", + "value": "observation_value", + "symbol": "series_id", + } + + name: str = Field( + description="The name of the series.", + ) + element_id: str = Field( + description="The element id in the parent/child relationship.", + ) + parent_id: str = Field( + description="The parent id in the parent/child relationship.", + ) + children: Optional[str] = Field( + default=None, + description="The element_id of each child, as a comma-separated string.", + ) + level: int = Field( + description="The indentation level of the element.", + ) + line: int = Field( + description="The line number of the series in the table.", + ) + + +class FredPersonalConsumptionExpendituresFetcher( + Fetcher[ + FredPersonalConsumptionExpendituresQueryParams, + List[FredPersonalConsumptionExpendituresData], + ] +): + """FRED Personal Consumption Expenditures Fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> FredPersonalConsumptionExpendituresQueryParams: + """Transform query.""" + return FredPersonalConsumptionExpendituresQueryParams(**params) + + @staticmethod + async def aextract_data( + query: FredPersonalConsumptionExpendituresQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict]: + """Extract data.""" + # pylint: disable=import-outside-toplevel + import asyncio # noqa + from openbb_core.provider.utils.helpers import amake_request + from numpy import nan + from pandas import DataFrame, to_datetime + + api_key = credentials.get("fred_api_key") if credentials else "" + element_id = PCE_CATEGORY_TO_EID[query.category] + dates: List = [""] + + if query.date: + if isinstance(query.date, dateType): + query.date = query.date.strftime("%Y-%m-%d") + dates = query.date.split(",") # type: ignore + dates = [d.replace(d[-2:], "01") if len(d) == 10 else d for d in dates] + dates = list(set(dates)) + dates = ( + [f"&observation_date={date}" for date in dates if date] if dates else "" # type: ignore + ) + + URLS = [ + f"https://api.stlouisfed.org/fred/release/tables?release_id=54&element_id={element_id}" + + f"{date}&include_observation_values=true&api_key={api_key}" + + "&file_type=json" + for date in dates + ] + results: List = [] + + async def get_one(URL): + """Get the observations for a single date.""" + response = await amake_request(URL) + data = [ + v + for v in response.get("elements", {}).values() # type: ignore + if v.get("observation_value") != "." and v.get("type") != "header" + ] + if data: + df = ( + DataFrame(data) + .set_index(["line", "element_id", "parent_id"]) + .sort_index()[ + [ + "level", + "series_id", + "name", + "observation_date", + "observation_value", + ] + ] + .reset_index() + ) + df["parent_id"] = df.parent_id.astype(str) + df["element_id"] = df.element_id.astype(str) + df["observation_value"] = df.observation_value.str.replace( + ",", "" + ).astype(float) + df["line"] = df.line.astype(int) + df["observation_date"] = to_datetime( + df["observation_date"], format="%b %Y" + ).dt.date + children = ( + df.groupby("parent_id")["element_id"] + .apply(lambda x: x.sort_values().unique().tolist()) + .to_dict() + ) + children = {k: ",".join(v) for k, v in children.items()} + df["children"] = df.element_id.map(children) + df = ( + df.set_index( + ["line", "element_id", "children", "parent_id", "level"] + ) + .sort_index() + .reset_index() + .replace({nan: None}) + ) + if query.category == "personal_income": + df = df.set_index("line").sort_index() + df["units"] = "Bil. of $" + df.loc[35, "units"] = "%" + df.loc[36:37, "units"] = "Bil. of Chn. 2017 $" + df.loc[38, "units"] = "$" + df.loc[39, "units"] = "Chn. 2017" + df.loc[40, "units"] = "Thous." + df = df.reset_index() + elif query.category in ["wages_by_industry", "pce_dollars"]: + df["units"] = "Bil. of $" + elif query.category in [ + "real_pce_percent_change", + "pce_price_percent_change", + ]: + df["units"] = "%" + elif query.category in ["real_pce_quantity_index", "pce_price_index"]: + df["units"] = "Index 2017=100" + elif query.category == "real_pce_chained_dollars": + df["units"] = "Bil. of Chn. 2017 $" + else: + pass + + results.extend(df.to_dict("records")) + + await asyncio.gather(*[get_one(URL) for URL in URLS]) + + return results + + @staticmethod + def transform_data( + query: FredPersonalConsumptionExpendituresQueryParams, + data: List[Dict], + **kwargs: Any, + ) -> List[FredPersonalConsumptionExpendituresData]: + """Transform data.""" + if not data: + raise EmptyDataError("The request was returned empty.") + + return [ + FredPersonalConsumptionExpendituresData.model_validate(d) + for d in sorted( + data, + key=lambda x: ( + x["observation_date"], + x["line"], + ), + ) + ] diff --git a/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_personal_consumption_expenditures_fetcher_urllib3_v1.yaml b/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_personal_consumption_expenditures_fetcher_urllib3_v1.yaml new file mode 100644 index 00000000000..d1ce3ccbfbb --- /dev/null +++ b/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_personal_consumption_expenditures_fetcher_urllib3_v1.yaml @@ -0,0 +1,63 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://api.stlouisfed.org/fred/release/tables?api_key=MOCK_API_KEY&element_id=3208&file_type=json&include_observation_values=true&observation_date=2024-05-01&release_id=54 + response: + body: + string: !!binary | + H4sIAAAAAAAEA6pWykvMTVWyUgpJTMpJVTDSs9Az0VMIKMpMTlXwzEtJrUgtVkjLL1IISC0qzs9L + zFFwzs8rLs0tKMnMz1NwrShIzUvJLCktSi1WSKpU8E3Myi9SCKksSFXIT1MIKMpPKU0uUdJRSs1J + zU3NK4nPTFGyMjYysNBRKkrNSU0sTgWLKJmaINQUK1lVKxkbWlqCaBR9hpaWqPpMTXSUilOLMlOL + IcYEOLsGeCrpKBUkFqFalpOZB/KhoZKOUkllAYgJ0aakA/M83HfJSL5LRfadRoCzq6aSjlJOallq + jpKVEsiw/KTi1KKyRFBQxJcl5pSCTDY0MtYzsDRT0lFClk1JLAFJ+iZWKhgZGIG8m5yRmZNSlJqn + ZBVdrYTiTyMDM/z+dHF3CQ5yN/Y1sDALdnJ1RPMxOJigPjbC7WP3/PyUYiT/gJQiuxjuH0MTPXMj + dN+S5h9TAv5xCQ3C7R9wcED9Y4zbPy6lRYmgBJyO5i+QFqz+MjDVM7I0ICWeYmt10KLK0NKcgNf8 + XNyJ8xooTeBInH75eSmk+s7IQM/QGGQmst/xxlpsLab/jAwMCfgv2BVP1CEnRVPcURecWlSWmZxK + VGo0MtczMiYpd2HzlTFeX+WV5uSgliJIHoFIQmMqIzUxJbUIUYw4pqSk5qUkomUrfFndAK9LlMBF + mo+bK1oeNzIw1lGC5glQYECdUwwuDBHOCXB2VUitSM4pTcnMS1dIy89PUUjMS1FIzUstSq9EciTO + PGJkpGdgAoo5ElIReh4xMjDC70cXtwhHfHkE4VVz3GnILT8/hSgfmemZmIPMochHBHN9EN5cj/CR + BW4fuYJjSQFcnoGjDVTXoGUTnBFnbKlnAa5VKfCmoSVaPY1e37oEOPsSF3GWuL3pm1iUnVqim5RY + nJqiEOAMSumwKhan74wM9UyNQEFHge+MDEwIJMsA5wjifGcIqkVwZEF071GYHw30DMxAgUmCx8Hl + upKxkYEZRosKXLUit8QwYni0pWEFbWIiStXRlkZmfh5Se5e8lgYoRZpiSZHDp60I6sWYY/hweDUZ + dUDRaIjhyWHSbgR5zhiL50abj6PNR6SuP4FuFWaDeLT5mKJkpTTafIQOSw3y5iO4jDfAUgwOw74r + qMQ3wuLVId+FBXkMszFiZDA8iyJQ08sCIxqHWYcWFKUmGJ4cMf1awGprAQvEx5q7GAAA + headers: + Cache-Control: + - max-age=0, no-cache + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '906' + Content-Type: + - application/json; charset=UTF-8 + Date: + - Wed, 10 Jul 2024 19:41:44 GMT + Expires: + - Wed, 10 Jul 2024 19:41:44 GMT + Last-Modified: + - Wed, 10 Jul 2024 19:41:44 GMT + Pragma: + - no-cache + Server: + - Apache + Set-Cookie: + - ak_bmsc=ABFEAA8641E1BF996D74FB74B04DBDC3~000000000000000000000000000000~YAAQxh/XF8UFi1OQAQAAIYMqnhg/yyojpFlH4BQfnilsu/2nL61PsqhE7/HIvFDBnHF6a4CRV147sHTyL8ah1S9e6AWdh5xIA1Hf1BbjuWoT9Bn3Qo3ek8sKArQY57My8I/5md27Z8hGTaD6vJ/KKpSN9/YOO0DA0zuaT0QMxW2B6sdpLhwiMFgo3EghciTj6Gtdg6LJPcd2fPAWGJvDEby4u1rh8NjPnbhCb6aYYNhnqoifkaKOYUS8ip8fPLbTiWf/QSqG1gFH0tkurM6AXl14gTkYf5dLGF9k7rNacFxT3FlDoWeOUm7HggmBGGNsdp0deIryWVeoXW1zjaznR5V7s23DLtcM9746oIn8QB0CNOES1dBZLCm95xihjp61; + Domain=.stlouisfed.org; Path=/; Expires=Wed, 10 Jul 2024 21:41:44 GMT; Max-Age=7200 + Strict-Transport-Security: + - max-age=86400 + Vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_personal_consumption_expenditures_fetcher_urllib3_v2.yaml b/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_personal_consumption_expenditures_fetcher_urllib3_v2.yaml new file mode 100644 index 00000000000..84e7fe04d02 --- /dev/null +++ b/openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_personal_consumption_expenditures_fetcher_urllib3_v2.yaml @@ -0,0 +1,60 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://api.stlouisfed.org/fred/release/tables?api_key=MOCK_API_KEY&element_id=3208&file_type=json&include_observation_values=true&observation_date=2024-05-01&release_id=54 + response: + body: + string: !!binary | + H4sIAAAAAAAEA6pWykvMTVWyUgpJTMpJVTDSs9Az0VMIKMpMTlXwzEtJrUgtVkjLL1IISC0qzs9L + zFFwzs8rLs0tKMnMz1NwrShIzUvJLCktSi1WSKpU8E3Myi9SCKksSFXIT1MIKMpPKU0uUdJRSs1J + zU3NK4nPTFGyMjYysNBRKkrNSU0sTgWLKJmaINQUK1lVKxkbWlqCaBR9hpaWqPpMTXSUilOLMlOL + IcYEOLsGeCrpKBUkFqFalpOZB/KhoZKOUkllAYgJ0aakA/M83HfJSL5LRfadRoCzq6aSjlJOallq + jpKVEsiw/KTi1KKyRFBQxJcl5pSCTDY0MtYzsDRT0lFClk1JLAFJ+iZWKhgZGIG8m5yRmZNSlJqn + ZBVdrYTiTyMDM/z+dHF3CQ5yN/Y1sDALdnJ1RPMxOJigPjbC7WP3/PyUYiT/gJQiuxjuH0MTPXMj + dN+S5h9TAv5xCQ3C7R9wcED9Y4zbPy6lRYmgBJyO5i+QFqz+MjDVM7I0ICWeYmt10KLK0NKcgNf8 + XNyJ8xooTeBInH75eSmk+s7IQM/QGGQmst/xxlpsLab/jAwMCfgv2BVP1CEnRVPcURecWlSWmZxK + VGo0MtczMiYpd2HzlTFeX+WV5uSgliJIHoFIQmMqIzUxJbUIUYw4pqSk5qUkomUrfFndAK9LlMBF + mo+bK1oeNzIw1lGC5glQYECdUwwuDBHOCXB2VUitSM4pTcnMS1dIy89PUUjMS1FIzUstSq9EciTO + PGJkpGdgAoo5ElIReh4xMjDC70cXtwhHfHkE4VVz3GnILT8/hSgfmemZmIPMochHBHN9EN5cj/CR + BW4fuYJjSQFcnoGjDVTXoGUTnBFnbKlnAa5VKfCmoSVaPY1e37oEOPsSF3GWuL3pm1iUnVqim5RY + nJqiEOAMSumwKhan74wM9UyNQEFHge+MDEwIJMsA5wjifGcIqkVwZEF071GYHw30DMxAgUmCx8Hl + upKxkYEZRosKXLUit8QwYni0pWEFbWIiStXRlkZmfh5Se5e8lgYoRZpiSZHDp60I6sWYY/hweDUZ + dUDRaIjhyWHSbgR5zhiL50abj6PNR6SuP4FuFWaDeLT5mKJkpTTafIQOSw3y5iO4jDfAUgwOw74r + qMQ3wuLVId+FBXkMszFiZDA8iyJQ08sCIxqHWYcWFKUmGJ4cMf1awGprAQvEx5q7GAAA + headers: + Cache-Control: + - max-age=0, no-cache + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '906' + Content-Type: + - application/json; charset=UTF-8 + Date: + - Wed, 10 Jul 2024 19:43:08 GMT + Expires: + - Wed, 10 Jul 2024 19:43:08 GMT + Last-Modified: + - Wed, 10 Jul 2024 19:41:44 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 4b7904a344e..fe0049878c9 100644 --- a/openbb_platform/providers/fred/tests/test_fred_fetchers.py +++ b/openbb_platform/providers/fred/tests/test_fred_fetchers.py @@ -31,6 +31,9 @@ from openbb_fred.models.non_farm_payrolls import FredNonFarmPayrollsFetcher from openbb_fred.models.overnight_bank_funding_rate import ( FredOvernightBankFundingRateFetcher, ) +from openbb_fred.models.personal_consumption_expenditures import ( + FredPersonalConsumptionExpendituresFetcher, +) from openbb_fred.models.regional import FredRegionalDataFetcher from openbb_fred.models.retail_prices import FredRetailPricesFetcher from openbb_fred.models.search import ( @@ -487,3 +490,16 @@ def test_fred_non_farm_payrolls_fetcher(credentials=test_credentials): fetcher = FredNonFarmPayrollsFetcher() result = fetcher.test(params, credentials) assert result is None + + +@pytest.mark.record_http +def test_fred_personal_consumption_expenditures_fetcher(credentials=test_credentials): + """Test FRED Personal Consumption Expenditures Fetcher.""" + params = { + "date": "2024-05-01", + "category": "pce_price_index", + } + + fetcher = FredPersonalConsumptionExpendituresFetcher() + result = fetcher.test(params, credentials) + assert result is None