mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-06 01:12:27 +00:00
[Feature] Add PCE (Personal Consumption Expenditures) reports to Economy (#6578)
* add PCE reports to Economy * query.date --------- Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
This commit is contained in:
@@ -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", ""))
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
|
||||
154
openbb_platform/openbb/assets/reference.json
vendored
154
openbb_platform/openbb/assets/reference.json
vendored
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"],
|
||||
),
|
||||
)
|
||||
]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user