diff options
author | Danglewood <85772166+deeleeramone@users.noreply.github.com> | 2024-05-17 13:10:33 -0700 |
---|---|---|
committer | Danglewood <85772166+deeleeramone@users.noreply.github.com> | 2024-05-17 13:10:33 -0700 |
commit | fbaf10f879bc2e30bd3ae6ec7507e79c193b3abc (patch) | |
tree | d195e3e222e038008da4693fbdc974cf7f3f88b2 | |
parent | b33e563dc19dcce2aaf382202c557348a98b7c3b (diff) |
add balance of payments to FRED provider
9 files changed, 2457 insertions, 1 deletions
diff --git a/openbb_platform/extensions/economy/integration/test_economy_api.py b/openbb_platform/extensions/economy/integration/test_economy_api.py index f8de0ee5ca1..3f7998c14de 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_api.py +++ b/openbb_platform/extensions/economy/integration/test_economy_api.py @@ -251,6 +251,14 @@ def test_economy_gdp_real(params, headers): "provider": "ecb", } ), + ( + { + "country": "united_states", + "start_date": None, + "end_date": None, + "provider": "fred", + } + ), ], ) @pytest.mark.integration diff --git a/openbb_platform/extensions/economy/integration/test_economy_python.py b/openbb_platform/extensions/economy/integration/test_economy_python.py index dec33dacb6e..75a01123c7b 100644 --- a/openbb_platform/extensions/economy/integration/test_economy_python.py +++ b/openbb_platform/extensions/economy/integration/test_economy_python.py @@ -212,6 +212,14 @@ def test_economy_gdp_real(params, obb): "provider": "ecb", } ), + ( + { + "country": "united_states", + "start_date": None, + "end_date": None, + "provider": "fred", + } + ), ], ) @pytest.mark.integration diff --git a/openbb_platform/extensions/economy/openbb_economy/economy_router.py b/openbb_platform/extensions/economy/openbb_economy/economy_router.py index 19eb403cea1..6adeaeea1ea 100644 --- a/openbb_platform/extensions/economy/openbb_economy/economy_router.py +++ b/openbb_platform/extensions/economy/openbb_economy/economy_router.py @@ -93,6 +93,8 @@ async def risk_premium( @router.command( model="BalanceOfPayments", examples=[ + APIEx(parameters={"provider": "fred"}), + APIEx(parameters={"provider": "fred", "country": "brazil"}), APIEx(parameters={"provider": "ecb"}), APIEx(parameters={"report_type": "summary", "provider": "ecb"}), APIEx( diff --git a/openbb_platform/openbb/assets/reference.json b/openbb_platform/openbb/assets/reference.json index 0c7ed54c228..bbc14f5b00d 100644 --- a/openbb_platform/openbb/assets/reference.json +++ b/openbb_platform/openbb/assets/reference.json @@ -3153,6 +3153,254 @@ }, "model": "RiskPremium" }, + "/economy/balance_of_payments": { + "deprecated": { + "flag": null, + "message": null + }, + "description": "Balance of Payments Reports.", + "examples": "\nExamples\n--------\n\n```python\nfrom openbb import obb\nobb.economy.balance_of_payments(provider='fred')\nobb.economy.balance_of_payments(provider='fred', country=brazil)\n```\n\n", + "parameters": { + "standard": [ + { + "name": "provider", + "type": "Literal['fred']", + "description": "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.", + "default": "fred", + "optional": true + } + ], + "fred": [ + { + "name": "country", + "type": "Literal['argentina', 'australia', 'austria', 'belgium', 'brazil', 'canada', 'chile', 'china', 'colombia', 'costa_rica', 'czechia', 'denmark', 'estonia', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'india', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'mexico', 'netherlands', 'new_zealand', 'norway', 'poland', 'portugal', 'russia', 'saudi_arabia', 'slovak_republic', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states', 'g7', 'g20']", + "description": "The country to get data. Enter as a 3-letter ISO country code, default is USA.", + "default": "united_states", + "optional": true, + "choices": "argentina,australia,austria,belgium,brazil,canada,chile,china,colombia,costa_rica,czechia,denmark,estonia,finland,france,germany,greece,hungary,iceland,india,indonesia,ireland,israel,italy,japan,korea,latvia,lithuania,luxembourg,mexico,netherlands,new_zealand,norway,poland,portugal,russia,saudi_arabia,slovak_republic,slovenia,south_africa,spain,sweden,switzerland,turkey,united_kingdom,united_states,g7,g20" + }, + { + "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 + } + ] + }, + "returns": { + "OBBject": [ + { + "name": "results", + "type": "List[BalanceOfPayments]", + "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": "period", + "type": "date", + "description": "The date representing the beginning of the reporting period.", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "balance_percent_of_gdp", + "type": "float", + "description": "Current Account Balance as Percent of GDP", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "balance_total", + "type": "float", + "description": "Current Account Total Balance (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "balance_total_services", + "type": "float", + "description": "Current Account Total Services Balance (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "balance_total_secondary_income", + "type": "float", + "description": "Current Account Total Secondary Income Balance (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "balance_total_goods", + "type": "float", + "description": "Current Account Total Goods Balance (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "balance_total_primary_income", + "type": "float", + "description": "Current Account Total Primary Income Balance (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_services_percent_of_goods_and_services", + "type": "float", + "description": "Current Account Credits Services as Percent of Goods and Services", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_services_percent_of_current_account", + "type": "float", + "description": "Current Account Credits Services as Percent of Current Account", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_total_services", + "type": "float", + "description": "Current Account Credits Total Services (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_total_goods", + "type": "float", + "description": "Current Account Credits Total Goods (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_total_primary_income", + "type": "float", + "description": "Current Account Credits Total Primary Income (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_total_secondary_income", + "type": "float", + "description": "Current Account Credits Total Secondary Income (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "credits_total", + "type": "float", + "description": "Current Account Credits Total (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_services_percent_of_goods_and_services", + "type": "float", + "description": "Current Account Debits Services as Percent of Goods and Services", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_services_percent_of_current_account", + "type": "float", + "description": "Current Account Debits Services as Percent of Current Account", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_total_services", + "type": "float", + "description": "Current Account Debits Total Services (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_total_goods", + "type": "float", + "description": "Current Account Debits Total Goods (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_total_primary_income", + "type": "float", + "description": "Current Account Debits Total Primary Income (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_total", + "type": "float", + "description": "Current Account Debits Total (USD)", + "default": null, + "optional": true, + "choices": null + }, + { + "name": "debits_total_secondary_income", + "type": "float", + "description": "Current Account Debits Total Secondary Income (USD)", + "default": null, + "optional": true, + "choices": null + } + ], + "fred": [] + }, + "model": "BalanceOfPayments" + }, "/economy/fred_search": { "deprecated": { "flag": null, diff --git a/openbb_platform/openbb/package/economy.py b/openbb_platform/openbb/package/economy.py index fcf6f22d4c8..0a589831355 100644 --- a/openbb_platform/openbb/package/economy.py +++ b/openbb_platform/openbb/package/economy.py @@ -14,6 +14,7 @@ from typing_extensions import Annotated class ROUTER_economy(Container): """/economy available_indicators + balance_of_payments calendar composite_leading_indicator country_profile @@ -124,6 +125,114 @@ class ROUTER_economy(Container): @exception_handler @validate + def balance_of_payments( + self, + provider: Annotated[ + Optional[Literal["fred"]], + OpenBBField( + description="The provider to use for the query, by default None.\n If None, the provider specified in defaults is selected or 'fred' if there is\n no default." + ), + ] = None, + **kwargs + ) -> OBBject: + """Balance of Payments Reports. + + Parameters + ---------- + 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. + country : Literal['argentina', 'australia', 'austria', 'belgium', 'brazil', 'canada', 'chile', 'china', 'colombia', 'costa_rica', 'czechia', 'denmark', 'estonia', 'finland', 'france', 'germany', 'greece', 'hungary', 'iceland', 'india', 'indonesia', 'ireland', 'israel', 'italy', 'japan', 'korea', 'latvia', 'lithuania', 'luxembourg', 'mexico', 'netherlands', 'new_zealand', 'norway', 'poland', 'portugal', 'russia', 'saudi_arabia', 'slovak_republic', 'slovenia', 'south_africa', 'spain', 'sweden', 'switzerland', 'turkey', 'united_kingdom', 'united_states', 'g7', 'g20'] + The country to get data. Enter as a 3-letter ISO country code, default is USA. (provider: fred) + start_date : Optional[datetime.date] + Start date of the data, in YYYY-MM-DD format. (provider: fred) + end_date : Optional[datetime.date] + End date of the data, in YYYY-MM-DD format. (provider: fred) + + Returns + ------- + OBBject + results : List[BalanceOfPayments] + 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. + + BalanceOfPayments + ----------------- + period : Optional[date] + The date representing the beginning of the reporting period. + balance_percent_of_gdp : Optional[float] + Current Account Balance as Percent of GDP + balance_total : Optional[float] + Current Account Total Balance (USD) + balance_total_services : Optional[float] + Current Account Total Services Balance (USD) + balance_total_secondary_income : Optional[float] + Current Account Total Secondary Income Balance (USD) + balance_total_goods : Optional[float] + Current Account Total Goods Balance (USD) + balance_total_primary_income : Optional[float] + Current Account Total Primary Income Balance (USD) + credits_services_percent_of_goods_and_services : Optional[float] + Current Account Credits Services as Percent of Goods and Services + credits_services_percent_of_current_account : Optional[float] + Current Account Credits Services as Percent of Current Account + credits_total_services : Optional[float] + Current Account Credits Total Services (USD) + credits_total_goods : Optional[float] + Current Account Credits Total Goods (USD) + credits_total_primary_income : Optional[float] + Current Account Credits Total Primary Income (USD) + credits_total_secondary_income : Optional[float] + Current Account Credits Total Secondary Income (USD) + credits_total : Optional[float] + Current Account Credits Total (USD) + debits_services_percent_of_goods_and_services : Optional[float] + Current Account Debits Services as Percent of Goods and Services + debits_services_percent_of_current_account : Optional[float] + Current Account Debits Services as Percent of Current Account + debits_total_services : Optional[float] + Current Account Debits Total Services (USD) + debits_total_goods : Optional[float] + Current Account Debits Total Goods (USD) + debits_total_primary_income : Optional[float] + Current Account Debits Total Primary Income (USD) + debits_total : Optional[float] + Current Account Debits Total (USD) + debits_total_secondary_income : Optional[float] + Current Account Debits Total Secondary Income (USD) + + Examples + -------- + >>> from openbb import obb + >>> obb.economy.balance_of_payments(provider='fred') + >>> obb.economy.balance_of_payments(provider='fred', country='brazil') + """ # noqa: E501 + + return self._run( + "/economy/balance_of_payments", + **filter_inputs( + provider_choices={ + "provider": self._get_provider( + provider, + "/economy/balance_of_payments", + ("fred",), + ) + }, + standard_params={}, + extra_params=kwargs, + ) + ) + + @exception_handler + @validate def calendar( self, start_date: Annotated[ diff --git a/openbb_platform/providers/fred/openbb_fred/models/balance_of_payments.py b/openbb_platform/providers/fred/openbb_fred/models/balance_of_payments.py new file mode 100644 index 00000000000..bdfb141e2cf --- /dev/null +++ b/openbb_platform/providers/fred/openbb_fred/models/balance_of_payments.py @@ -0,0 +1,124 @@ +"""FRED Balance Of Payments Model.""" + +# pylint: disable=unused-argument + +from datetime import date as dateType +from typing import Any, Dict, List, Optional + +from openbb_core.provider.abstract.annotated_result import AnnotatedResult +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.balance_of_payments import ( + B6BopUsdData, + BalanceOfPaymentsQueryParams, +) +from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS +from openbb_core.provider.utils.errors import EmptyDataError +from openbb_fred.models.series import ( + FredSeriesData, + FredSeriesFetcher, + FredSeriesQueryParams, +) +from openbb_fred.utils.fred_helpers import ( + BOP_COUNTRIES, + BOP_COUNTRY_CHOICES, + get_bop_series, +) +from pandas import DataFrame +from pydantic import Field, field_validator + + +class FredBalanceOfPaymentsQueryParams(BalanceOfPaymentsQueryParams): + """FRED Balance Of Payments Query Parameters.""" + + country: BOP_COUNTRY_CHOICES = Field( + default="united_states", + description=QUERY_DESCRIPTIONS.get("country", "") + + " Enter as a 3-letter ISO country code, default is USA.", + json_schema_extra={"choices": ",".join(list(BOP_COUNTRIES))}, + ) + 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 FredBalanceOfPaymentsData(B6BopUsdData): + """FRED Balance Of Payments Data.""" + + __alias_dict__ = {"period": "date"} + + @field_validator( + "balance_percent_of_gdp", + "credits_services_percent_of_goods_and_services", + "credits_services_percent_of_current_account", + "debits_services_percent_of_goods_and_services", + "debits_services_percent_of_current_account", + mode="before", + check_fields=False, + ) + @classmethod + def normalize_percent(cls, v): + """Normalize the percent value.""" + return float(v) / 100 if v else None + + +class FredBalanceOfPaymentsFetcher( + Fetcher[FredBalanceOfPaymentsQueryParams, List[FredBalanceOfPaymentsData]] +): + """FRED Balance Of Payments Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> FredBalanceOfPaymentsQueryParams: + """Transform query.""" + return FredBalanceOfPaymentsQueryParams(**params) + + @staticmethod + async def aextract_data( + query: FredBalanceOfPaymentsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict: + """Extract data.""" + fred_fetcher = FredSeriesFetcher() + country = BOP_COUNTRIES.get(query.country) if query.country else "USA" + query_dict = query.model_dump(exclude_none=True) + query_dict["symbol"] = ",".join(list(get_bop_series(country).values())) + fred_query = FredSeriesQueryParams(**query_dict) + data = await fred_fetcher.aextract_data(fred_query, credentials) + return data + + @staticmethod + def transform_data( + query: FredBalanceOfPaymentsQueryParams, + data: Dict, + **kwargs: Any, + ) -> AnnotatedResult[List[FredBalanceOfPaymentsData]]: + """Transform data.""" + if not data: + raise EmptyDataError(f"No data was found for, {query.country}.") + fred_fetcher = FredSeriesFetcher() + country = BOP_COUNTRIES.get(query.country) if query.country else "USA" + query_dict = query.model_dump(exclude_none=True) + query_dict["symbol"] = ",".join(list(get_bop_series(country).values())) + fred_query = FredSeriesQueryParams(**query_dict) + data = fred_fetcher.transform_data(fred_query, data) + + series_ids = get_bop_series(query.country) + col_map = {v: k for k, v in series_ids.items()} + result = data.result + df = ( + DataFrame([d.model_dump() for d in result]) + .set_index("date") + .sort_index(ascending=False) + ) + df = df.rename(columns=col_map) + records = df.reset_index().fillna("N/A").replace("N/A", None).to_dict("records") + + return AnnotatedResult( + result=[FredBalanceOfPaymentsData.model_validate(r) for r in records], + metadata=data.metadata, + ) diff --git a/openbb_platform/providers/fred/openbb_fred/utils/fred_helpers.py b/openbb_platform/providers/fred/openbb_fred/utils/fred_helpers.py index b61cc26625e..9d2ce67d8c4 100644 --- a/openbb_platform/providers/fred/openbb_fred/utils/fred_helpers.py +++ b/openbb_platform/providers/fred/openbb_fred/utils/fred_helpers.py @@ -57,7 +57,7 @@ def comma_to_float_list(v: str) -> List[float]: return [float(m) for m in v.split(",")] except ValueError as e: raise ValueError( - "'maturity' must be a float or a comma-separated string of floats" + "maturity must be a float or a comma-separated string of floats" ) from e @@ -216,3 +216,135 @@ def get_spot_series_id(maturity: List[float], category: List[str]) -> List[dict] filtered_series.append(s) return filtered_series + + +BOP_COUNTRIES = { + "argentina": "ARG", + "australia": "AUS", + "austria": "AUT", + "belgium": "BEL", + "brazil": "BRA", + "canada": "CAN", + "chile": "CHL", + "china": "CHN", + "colombia": "COL", + "costa_rica": "CRI", + "czechia": "CZE", + "denmark": "DNK", + "estonia": "EST", + "finland": "FIN", + "france": "FRA", + "germany": "DEU", + "greece": "GRC", + "hungary": "HUN", + "iceland": "ISL", + "india": "IND", + "indonesia": "IDN", + "ireland": "IRL", + "israel": "ISR", + "italy": "ITA", + "japan": "JAP", + "korea": "KOR", + "latvia": "LVA", + "lithuania": "LTU", + "luxembourg": "LUX", + "mexico": "MEX", + "netherlands": "NLD", + "new_zealand": "NZL", + "norway": "NOR", + "poland": "POL", + "portugal": "PRT", + "russia": "RUS", + "saudi_arabia": "SAU", + "slovak_republic": "SVK", + "slovenia": "SVN", + "south_africa": "ZAF", + "spain": "ESP", + "sweden": "SWE", + "switzerland": "CHE", + "turkey": "TUR", + "united_kingdom": "GBR", + "united_states": "USA", + "g7": "G7", + "g20": "G20", +} + +BOP_COUNTRY_CHOICES = Literal[ + "argentina", + "australia", + "austria", + "belgium", + "brazil", + "canada", + "chile", + "china", + "colombia", + "costa_rica", + |