summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-02-15 04:30:16 -0800
committerGitHub <noreply@github.com>2024-02-15 12:30:16 +0000
commit07e98ab58b029b76762d2d5acb3ef537d33c864a (patch)
treeb0248697e6b90c3e8a3e2ea60f47a47ca090394b
parent7deb4e69f646e9212e65eceec9762d9b964516c3 (diff)
[Feature] Add Regional Data Endpoint from FRED (#6071)
* add regional data endpoint from FRED * codespell * ruff * sort imports * add provder='fred' to integration test * thought I already sorted those imports.. * pylint.. * more test params.... * even more test params... * even more test params.... * static assets * reconfigure params * ruff * alias dict * recapture cassette * router doctstring --------- Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
-rw-r--r--openbb_platform/extensions/economy/integration/test_economy_api.py66
-rw-r--r--openbb_platform/extensions/economy/integration/test_economy_python.py65
-rw-r--r--openbb_platform/extensions/economy/openbb_economy/economy_router.py23
-rw-r--r--openbb_platform/openbb/package/economy.py154
-rw-r--r--openbb_platform/openbb/package/module_map.json1
-rw-r--r--openbb_platform/providers/fred/openbb_fred/__init__.py2
-rw-r--r--openbb_platform/providers/fred/openbb_fred/models/regional.py269
-rw-r--r--openbb_platform/providers/fred/openbb_fred/models/search.py60
-rw-r--r--openbb_platform/providers/fred/tests/record/http/test_fred_fetchers/test_fred_regional_fetcher.yaml1375
-rw-r--r--openbb_platform/providers/fred/tests/test_fred_fetchers.py19
10 files changed, 2025 insertions, 9 deletions
diff --git a/openbb_platform/extensions/economy/integration/test_economy_api.py b/openbb_platform/extensions/economy/integration/test_economy_api.py
index dcdcb21ef1b..6a5ac1eb362 100644
--- a/openbb_platform/extensions/economy/integration/test_economy_api.py
+++ b/openbb_platform/extensions/economy/integration/test_economy_api.py
@@ -266,6 +266,7 @@ def test_economy_balance_of_payments(params, headers):
"filter_value": "Monthly",
"tag_names": "nsa",
"exclude_tag_names": None,
+ "series_id": None,
"provider": "fred",
}
),
@@ -280,6 +281,7 @@ def test_economy_balance_of_payments(params, headers):
"filter_value": None,
"tag_names": None,
"exclude_tag_names": None,
+ "series_id": None,
"provider": "fred",
}
),
@@ -294,6 +296,22 @@ def test_economy_balance_of_payments(params, headers):
"filter_value": None,
"tag_names": None,
"exclude_tag_names": None,
+ "series_id": None,
+ "provider": "fred",
+ }
+ ),
+ (
+ {
+ "query": None,
+ "is_release": False,
+ "release_id": None,
+ "offset": None,
+ "limit": None,
+ "filter_variable": None,
+ "filter_value": None,
+ "tag_names": None,
+ "exclude_tag_names": None,
+ "series_id": "NYICLAIMS",
"provider": "fred",
}
),
@@ -478,3 +496,51 @@ def test_economy_long_term_interest_rate(params, headers):
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
+
+
+@parametrize(
+ argnames="params",
+ argvalues=[
+ (
+ {
+ "symbol": "156241",
+ "is_series_group": True,
+ "start_date": "2000-01-01",
+ "end_date": None,
+ "frequency": "w",
+ "units": "Number",
+ "region_type": "state",
+ "season": "NSA",
+ "aggregation_method": "eop",
+ "transform": "ch1",
+ "provider": "fred",
+ "limit": None,
+ }
+ ),
+ (
+ {
+ "symbol": "CAICLAIMS",
+ "is_series_group": False,
+ "start_date": "1990-01-01",
+ "end_date": "2010-01-01",
+ "frequency": None,
+ "units": None,
+ "region_type": None,
+ "season": None,
+ "aggregation_method": None,
+ "transform": None,
+ "provider": "fred",
+ "limit": None,
+ }
+ ),
+ ],
+)
+@pytest.mark.integration
+def test_economy_fred_regional(params, headers):
+ 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/fred_regional?{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 95c73732055..8c4c7ff14c6 100644
--- a/openbb_platform/extensions/economy/integration/test_economy_python.py
+++ b/openbb_platform/extensions/economy/integration/test_economy_python.py
@@ -225,6 +225,7 @@ def test_economy_balance_of_payments(params, obb):
"filter_value": "Monthly",
"tag_names": "nsa",
"exclude_tag_names": None,
+ "series_id": None,
"provider": "fred",
}
),
@@ -239,6 +240,7 @@ def test_economy_balance_of_payments(params, obb):
"filter_value": None,
"tag_names": None,
"exclude_tag_names": None,
+ "series_id": None,
"provider": "fred",
}
),
@@ -253,6 +255,22 @@ def test_economy_balance_of_payments(params, obb):
"filter_value": None,
"tag_names": None,
"exclude_tag_names": None,
+ "series_id": None,
+ "provider": "fred",
+ }
+ ),
+ (
+ {
+ "query": None,
+ "is_release": False,
+ "release_id": None,
+ "offset": None,
+ "limit": None,
+ "filter_variable": None,
+ "filter_value": None,
+ "tag_names": None,
+ "exclude_tag_names": None,
+ "series_id": "NYICLAIMS",
"provider": "fred",
}
),
@@ -430,3 +448,50 @@ def test_economy_long_term_interest_rate(params, obb):
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
+
+
+@parametrize(
+ argnames="params",
+ argvalues=[
+ (
+ {
+ "symbol": "156241",
+ "is_series_group": True,
+ "start_date": "2000-01-01",
+ "end_date": None,
+ "frequency": "w",
+ "units": "Number",
+ "region_type": "state",
+ "season": "NSA",
+ "aggregation_method": "eop",
+ "transform": "ch1",
+ "provider": "fred",
+ "limit": None,
+ }
+ ),
+ (
+ {
+ "symbol": "CAICLAIMS",
+ "is_series_group": False,
+ "start_date": "1990-01-01",
+ "end_date": "2010-01-01",
+ "frequency": None,
+ "units": None,
+ "region_type": None,
+ "season": None,
+ "aggregation_method": None,
+ "transform": None,
+ "provider": "fred",
+ "limit": None,
+ }
+ ),
+ ],
+)
+@pytest.mark.integration
+def test_economy_fred_regional(params, obb):
+ params = {p: v for p, v in params.items() if v}
+
+ result = obb.economy.long_term_interest_rate(**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 4ea2efce047..b6f0bde05d5 100644
--- a/openbb_platform/extensions/economy/openbb_economy/economy_router.py
+++ b/openbb_platform/extensions/economy/openbb_economy/economy_router.py
@@ -231,3 +231,26 @@ async def long_term_interest_rate(
Low long-term interest rates encourage investment in new equipment and high interest rates discourage it.
Investment is, in turn, a major source of economic growth."""
return await OBBject.from_query(Query(**locals()))
+
+
+@router.command(
+ model="FredRegional",
+ exclude_auto_examples=True,
+ examples=[
+ "#### With no date, the most recent report is returned. ####",
+ 'obb.economy.fred_regional("NYICLAIMS")',
+ "#### With a date, time series data is returned. ####",
+ 'obb.economy.fred_regional("NYICLAIMS", start_date="2021-01-01")',
+ ],
+)
+async def fred_regional(
+ cc: CommandContext,
+ provider_choices: ProviderChoices,
+ standard_params: StandardParams,
+ extra_params: ExtraParams,
+) -> OBBject:
+ """
+ Query the Geo Fred API for regional economic data by series group.
+ The series group ID is found by using `fred_search` and the `series_id` parameter.
+ """
+ return await OBBject.from_query(Query(**locals()))
diff --git a/openbb_platform/openbb/package/economy.py b/openbb_platform/openbb/package/economy.py
index 4fab46a8170..bdcde1a270b 100644
--- a/openbb_platform/openbb/package/economy.py
+++ b/openbb_platform/openbb/package/economy.py
@@ -16,6 +16,7 @@ class ROUTER_economy(Container):
calendar
composite_leading_indicator
cpi
+ fred_regional
fred_search
fred_series
/gdp
@@ -349,6 +350,153 @@ class ROUTER_economy(Container):
)
@validate
+ def fred_regional(
+ self,
+ symbol: Annotated[
+ str, OpenBBCustomParameter(description="Symbol to get data for.")
+ ],
+ start_date: Annotated[
+ Union[datetime.date, None, str],
+ OpenBBCustomParameter(
+ description="Start date of the data, in YYYY-MM-DD format."
+ ),
+ ] = None,
+ end_date: Annotated[
+ Union[datetime.date, None, str],
+ OpenBBCustomParameter(
+ description="End date of the data, in YYYY-MM-DD format."
+ ),
+ ] = None,
+ limit: Annotated[
+ Optional[int],
+ OpenBBCustomParameter(description="The number of data entries to return."),
+ ] = 100000,
+ provider: Optional[Literal["fred"]] = None,
+ **kwargs
+ ) -> OBBject:
+ """
+ Query the Geo Fred API for regional economic data by series group.
+ The series group ID is found by using `fred_search` and the `series_id` parameter.
+
+
+ Parameters
+ ----------
+ symbol : str
+ Symbol to get data for.
+ start_date : Optional[datetime.date]
+ Start date of the data, in YYYY-MM-DD format.
+ end_date : Optional[datetime.date]
+ End date of the data, in YYYY-MM-DD format.
+ limit : Optional[int]
+ The number of data entries to return.
+ provider : Optional[Literal['fred']]
+ The provider to use for the query, by default None.
+ If None, the provider specified in defaults is selected or 'fred' if there is
+ no default.
+ is_series_group : bool
+ When True, the symbol provided is for a series_group, else it is for a series ID. (provider: fred)
+ region_type : Optional[Literal['bea', 'msa', 'frb', 'necta', 'state', 'country', 'county', 'censusregion']]
+ The type of regional data. Parameter is only valid when `is_series_group` is True. (provider: fred)
+ season : Optional[Literal['SA', 'NSA', 'SSA']]
+ The seasonal adjustments to the data. Parameter is only valid when `is_series_group` is True. (provider: fred)
+ units : Optional[str]
+ The units of the data. This should match the units returned from searching by series ID. An incorrect field will not necessarily return an error. Parameter is only valid when `is_series_group` is True. (provider: fred)
+ frequency : Optional[Literal['d', 'w', 'bw', 'm', 'q', 'sa', 'a', 'wef', 'weth', 'wew', 'wetu', 'wem', 'wesu', 'wesa', 'bwew', 'bwem']]
+
+ Frequency aggregation to convert high frequency data to lower frequency.
+ Parameter is only valid when `is_series_group` is True.
+ a = Annual
+ sa= Semiannual
+ 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.
+ Only valid when `is_series_group` is True.
+ avg = Average
+ sum = Sum
+ eop = End of Period
+ (provider: fred)
+ transform : Literal['lin', 'chg', 'ch1', 'pch', 'pc1', 'pca', 'cch', 'cca', 'log']
+
+ Transformation type. Only valid when `is_series_group` is True.
+ lin = Levels (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[FredRegional]
+ 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.
+
+ FredRegional
+ ------------
+ date : date
+ The date of the data.
+ region : Optional[str]
+ The name of the region. (provider: fred)
+ code : Optional[Union[str, int]]
+ The code of the region. (provider: fred)
+ value : Optional[Union[float, int]]
+ The obersvation value. The units are defined in the search results by series ID. (provider: fred)
+ series_id : Optional[str]
+ The individual series ID for the region. (provider: fred)
+
+ Example
+ -------
+ >>> from openbb import obb
+ >>> #### With no date, the most recent report is returned. ####
+ >>> obb.economy.fred_regional("NYICLAIMS")
+ >>> #### With a date, time series data is returned. ####
+ >>> obb.economy.fred_regional("NYICLAIMS", start_date="2021-01-01")
+ """ # noqa: E501
+
+ return self._run(
+ "/economy/fred_regional",
+ **filter_inputs(
+ provider_choices={
+ "provider": provider,
+ },
+ standard_params={
+ "symbol": symbol,
+ "start_date": start_date,
+ "end_date": end_date,
+ "limit": limit,
+ },
+ extra_params=kwargs,
+ )
+ )
+
+ @validate
def fred_search(
self,
query: Annotated[
@@ -387,6 +535,8 @@ class ROUTER_economy(Container):
A semicolon delimited list of tag names that series match all of. Example: 'japan;imports' (provider: fred)
exclude_tag_names : Optional[str]
A semicolon delimited list of tag names that series match none of. Example: 'imports;services'. Requires that variable tag_names also be set to limit the number of matching series. (provider: fred)
+ series_id : Optional[str]
+ A FRED Series ID to return series group information for. This returns the required information to query for regional data. Not all series that are in FRED have geographical data. Entering a value for series_id will override all other parameters. Multiple series_ids can be separated by commas. (provider: fred)
Returns
-------
@@ -440,6 +590,10 @@ class ROUTER_economy(Container):
Popularity of the series (provider: fred)
group_popularity : Optional[int]
Group popularity of the release (provider: fred)
+ region_type : Optional[str]
+ The region type of the series. (provider: fred)
+ series_group : Optional[Union[int, str]]
+ The series group ID of the series. This value is used to query for regional data. (provider: fred)
Example
-------
diff --git a/openbb_platform/openbb/package/module_map.json b/openbb_platform/openbb/package/module_map.json
index 2cee252c131..39397c721a3 100644
--- a/openbb_platform/openbb/package/module_map.json
+++ b/openbb_platform/openbb/package/module_map.json
@@ -19,6 +19,7 @@
"economy_calendar": "/economy/calendar",
"economy_composite_leading_indicator": "/economy/composite_leading_indicator",
"economy_cpi": "/economy/cpi",
+ "economy_fred_regional": "/economy/fred_regional",
"economy_fred_search": "/economy/fred_search",
"economy_fred_series": "/economy/fred_series",
"economy_gdp": "/economy/gdp",
diff --git a/openbb_platform/providers/fred/openbb_fred/__init__.py b/openbb_platform/providers/fred/openbb_fred/__init__.py
index c0d250a0d90..b2292c4d946 100644
--- a/openbb_platform/providers/fred/openbb_fred/__init__.py
+++ b/openbb_platform/providers/fred/openbb_fred/__init__.py
@@ -16,6 +16,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.regional import FredRegionalDataFetcher
from openbb_fred.models.search import (
FredSearchFetcher,
)
@@ -51,6 +52,7 @@ fred_provider = Provider(
"CommercialPaper": FREDCommercialPaperFetcher,
"FredSearch": FredSearchFetcher,
"FredSeries": FredSeriesFetcher,
+ "FredRegional": FredRegionalDataFetcher,
"SpotRate": FREDSpotRateFetcher,
"HighQualityMarketCorporateBond": FREDHighQualityMarketCorporateBondFetcher,
"TreasuryConstantMaturity": FREDTreasuryConstantMaturityFetcher,
diff --git a/openbb_platform/providers/fred/openbb_fred/models/regional.py b/openbb_platform/providers/fred/openbb_fred/models/regional.py
new file mode 100644
index 00000000000..64d8b12cc6f
--- /dev/null
+++ b/openbb_platform/providers/fred/openbb_fred/models/regional.py
@@ -0,0 +1,269 @@
+"""FRED Regional Data Model."""
+
+# pylint: disable=unused-argument
+import json
+import warnings
+from datetime import datetime
+from typing import Any, Dict, List, Literal, Optional, Union
+
+from openbb_core.provider.abstract.fetcher import Fetcher
+from openbb_core.provider.standard_models.fred_series import (
+ SeriesData,
+ SeriesQueryParams,
+)
+from openbb_core.provider.utils.errors import EmptyDataError
+from openbb_core.provider.utils.helpers import (
+ amake_request,
+ get_querystring,
+)
+from pydantic import Field, model_validator
+
+_warn = warnings.warn
+
+
+class FredRegionalQueryParams(SeriesQueryParams):
+ """FRED Regional Data Query Params."""
+
+ __alias_dict__ = {
+ "symbol": "series_group",
+ "transform": "transformation",
+ }
+ symbol: str = Field(
+ description="For this function, it is the series_group ID or series ID."
+ + " If the symbol provided is for a series_group, set the `is_series_group` parameter to True."
+ + " Not all series that are in FRED have geographical data."
+ )
+ is_series_group: bool = Field(
+ default=False,
+ description="When True, the symbol provided is for a series_group, else it is for a series ID.",
+ )
+ region_type: Union[
+ None,
+ Literal[
+ "bea",
+ "msa",
+ "frb",
+ "necta",
+ "state",
+ "country",
+ "county",
+ "censusregion",
+ ],
+ ] = Field(
+ default=None,
+ description="The type of regional data."
+ + " Parameter is only valid when `is_series_group` is True.",
+ )
+ season: Union[
+ None,
+ Literal[
+ "SA",
+ "NSA",
+ "SSA",
+ ],
+ ] = Field(
+ default="NSA",
+ description="The seasonal adjustments to the data."
+ + " Parameter is only valid when `is_series_group` is True.",
+ )
+ units: Optional[str] = Field(
+ default=None,
+ description="The units of the data."
+ + " This should match the units returned from searching by series ID."
+ + " An incorrect field will not necessarily return an error."
+ + " Parameter is only valid when `is_series_group` is True.",
+ )
+ frequency: Union[
+ None,
+ Literal[
+ "d",
+ "w",
+ "bw",
+ "m",
+ "q",
+ "sa",
+ "a",
+ "wef",
+ "weth",
+ "wew",
+ "wetu",
+ "wem",
+ "wesu",
+ "wesa",
+ "bwew",
+ "bwem",
+ ],
+ ] = Field(
+ default=None,
+ description="""
+ Frequency aggregation to convert high frequency data to lower frequency.
+ Parameter is only valid when `is_series_group` is True.
+ a = Annual
+ sa= Semiannual
+ 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
+ """,
+ )
+ 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.
+ Only valid when `is_series_group` is True.
+ avg = Average
+ sum = Sum
+ eop = End of Period
+ """,
+ )
+ transform: Literal[
+ "lin", "chg", "ch1", "pch", "pc1", "pca", "cch", "cca", "log"
+ ] = Field(
+ default="lin",
+ description="""
+ Transformation type. Only valid when `is_series_group` is True.
+ lin = Levels (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
+ """,
+ )
+
+ @model_validator(mode="before")
+ @classmethod
+ def transform_validate(cls, values):
+ """Add default start date."""
+ if values.get("is_series_group") is True:
+ required = ["frequency", "region_type", "units"]
+ for key in required:
+ if values.get(key) is None:
+ raise ValueError(
+ f"{key} is a required field missing for series_group."
+ )
+
+ values["start_date"] = (
+ "1900-01-01"
+ if values.get("start_date") is None
+ else values.get("start_date")
+ )
+ if values.get("is_series_group") is False:
+ values["start_date"] = (
+ None if values.get("start_date") is None else values.get("start_date")
+ )
+ return values
+
+
+class FredRegionalData(SeriesData):
+ """FRED Regional Data."""
+
+ __alias_dict__ = {
+ "date": "observation_date",
+ }
+ region: str = Field(
+ description="The name of the region.",
+ )
+ code: Union[str, int] = Field(
+ description="The code of the region.",
+ )
+ value: Optional[Union[int, float]] = Field(
+ default=None,
+ description="The obersvation value. The units are defined in the search results by series ID.",
+ )
+ series_id: str = Field(
+ description="The individual series ID for the region.",
+ )
+
+
+class FredRegionalDataFetcher(
+ Fetcher[
+ FredRegionalQueryParams,
+ List[FredRegionalData],
+ ]
+):
+ """FRED Regional Data Fetcher."""
+
+ @staticmethod
+ def transform_query(params: Dict[str, Any]) -> FredRegionalQueryParams:
+ """Transform query."""
+ return FredRegionalQueryParams(**params)
+
+ @staticmethod
+ async def aextract_data(
+ query: FredRegionalQueryParams,
+ credentials: Optional[Dict[str, str]],
+ **kwargs: Any,
+ ) -> Dict:
+ """Extract the raw data."""
+ api_key = credentials.get("fred_api_key") if credentials else ""
+ if query.is_series_group is True:
+ base_url = "https://api.stlouisfed.org/geofred/regional/data?"
+ url = (
+ base_url
+ + get_querystring(
+ query.model_dump(), ["limit", "end_date", "is_series_group"]
+ )
+ + f"&file_type=json&api_key={api_key}"
+ )
+ if query.is_series_group is False:
+ base_url = "https://api.stlouisfed.org/geofred/series/data?"
+ url = (
+ base_url
+ + f"series_id={query.symbol}&"
+ + get_querystring(
+ query.model_dump(),
+ [
+ "limit",
+ "end_date",
+ "region_type",
+ "season",
+ "units",
+ "is_series_group",