mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-03 16:02:04 +00:00
Merge branch 'develop' of https://github.com/OpenBB-finance/OpenBB into feature/websockets
This commit is contained in:
@@ -3,20 +3,19 @@
|
||||
from datetime import date as dateType
|
||||
from typing import Optional, Union
|
||||
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
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,
|
||||
)
|
||||
from pydantic import Field, computed_field, field_validator
|
||||
|
||||
|
||||
class YieldCurveQueryParams(QueryParams):
|
||||
"""Yield Curve Query."""
|
||||
|
||||
date: Union[None, dateType, str] = Field(
|
||||
date: Optional[Union[dateType, str]] = Field(
|
||||
default=None,
|
||||
description=QUERY_DESCRIPTIONS.get("date", "")
|
||||
+ " By default is the current data.",
|
||||
@@ -34,9 +33,10 @@ class YieldCurveQueryParams(QueryParams):
|
||||
if isinstance(v, dateType):
|
||||
return v.strftime("%Y-%m-%d")
|
||||
new_dates: list = []
|
||||
dates: list = []
|
||||
if isinstance(v, str):
|
||||
dates = v.split(",")
|
||||
if isinstance(v, list):
|
||||
elif isinstance(v, list):
|
||||
dates = v
|
||||
for date in dates:
|
||||
new_dates.append(to_datetime(date).date().strftime("%Y-%m-%d"))
|
||||
@@ -52,7 +52,21 @@ class YieldCurveData(Data):
|
||||
description=DATA_DESCRIPTIONS.get("date", ""),
|
||||
)
|
||||
maturity: str = Field(description="Maturity length of the security.")
|
||||
rate: float = Field(
|
||||
description="The yield as a normalized percent (0.05 is 5%)",
|
||||
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
||||
|
||||
@computed_field( # type: ignore
|
||||
description="Maturity length, in years, as a decimal.",
|
||||
return_type=Optional[float],
|
||||
)
|
||||
@property
|
||||
def maturity_years(self) -> Optional[float]:
|
||||
"""Get the maturity in years as a decimal."""
|
||||
if "_" not in self.maturity: # pylint: disable=E1135
|
||||
return None
|
||||
|
||||
parts = self.maturity.split("_") # pylint: disable=E1101
|
||||
months = sum(
|
||||
int(parts[i + 1]) * (12 if parts[i] == "year" else 1)
|
||||
for i in range(0, len(parts), 2)
|
||||
)
|
||||
|
||||
return months / 12
|
||||
|
||||
@@ -28,7 +28,7 @@ class FixedIncomeViews:
|
||||
from openbb_core.app.utils import basemodel_to_df
|
||||
from pandas import DataFrame
|
||||
|
||||
data = kwargs.get("data", None)
|
||||
data = kwargs.get("data")
|
||||
df: DataFrame = DataFrame()
|
||||
if data:
|
||||
if isinstance(data, DataFrame) and not data.empty: # noqa: SIM108
|
||||
@@ -55,6 +55,9 @@ class FixedIncomeViews:
|
||||
provider = kwargs.get("provider")
|
||||
df["date"] = df["date"].astype(str)
|
||||
maturities = duration_sorter(df["maturity"].unique().tolist())
|
||||
countries: list = (
|
||||
df["country"].unique().tolist() if "country" in df.columns else []
|
||||
)
|
||||
|
||||
# Use the supplied colors, if any.
|
||||
colors = kwargs.get("colors", [])
|
||||
@@ -66,11 +69,13 @@ class FixedIncomeViews:
|
||||
figure.update_layout(ChartStyle().plotly_template.get("layout", {}))
|
||||
text_color = "white" if ChartStyle().plt_style == "dark" else "black"
|
||||
|
||||
def create_fig(figure, df, dates, color_count, country: Optional[str] = None):
|
||||
def create_fig(
|
||||
figure, dataframe, dates, color_count, country: Optional[str] = None
|
||||
):
|
||||
"""Create a scatter for each date in the data."""
|
||||
for date in dates:
|
||||
color = colors[color_count % len(colors)]
|
||||
plot_df = df[df["date"] == date].copy()
|
||||
plot_df = dataframe[dataframe["date"] == date].copy()
|
||||
plot_df["rate"] = plot_df["rate"].apply(lambda x: x * 100)
|
||||
plot_df = plot_df.rename(columns={"rate": "Yield"})
|
||||
plot_df = (
|
||||
@@ -81,31 +86,41 @@ class FixedIncomeViews:
|
||||
)
|
||||
plot_df = plot_df.rename(columns={"index": "Maturity"})
|
||||
plot_df["Maturity"] = [
|
||||
(
|
||||
d.split("_")[1] + " " + d.split("_")[0].title()
|
||||
if d != "long_term"
|
||||
else "Long Term"
|
||||
)
|
||||
(d.split("_")[1] + " " + d.split("_")[0].title())
|
||||
for d in plot_df["Maturity"]
|
||||
]
|
||||
figure.add_scatter(
|
||||
x=plot_df["Maturity"],
|
||||
y=plot_df["Yield"],
|
||||
mode="lines+markers",
|
||||
name=f"{country} - {date}" if country else date,
|
||||
name=(
|
||||
f"{country.replace('_', ' ').title().replace('Ecb', 'ECB')} {date}"
|
||||
if country
|
||||
else date
|
||||
),
|
||||
line=dict(width=3, color=color),
|
||||
marker=dict(size=10, color=color),
|
||||
hovertemplate=(
|
||||
"Maturity: %{x}<br>Yield: %{y}%<extra></extra>"
|
||||
if len(dates) == 1
|
||||
if len(dates) == 1 and not countries
|
||||
else "%{fullData.name}<br>Maturity: %{x}<br>Yield: %{y}%<extra></extra>"
|
||||
),
|
||||
)
|
||||
color_count += 1
|
||||
return figure, color_count
|
||||
|
||||
dates = df.date.unique().tolist()
|
||||
figure, color_count = create_fig(figure, df, dates, color_count)
|
||||
if countries:
|
||||
for _country in countries:
|
||||
_df = df[df["country"] == _country]
|
||||
dates = _df.date.unique().tolist()
|
||||
figure, color_count = create_fig(
|
||||
figure, _df, dates, color_count, _country
|
||||
)
|
||||
|
||||
else:
|
||||
dates = df.date.unique().tolist()
|
||||
figure, color_count = create_fig(figure, df, dates, color_count)
|
||||
|
||||
extra_params = kwargs.get("extra_params", {})
|
||||
extra_params = (
|
||||
extra_params if isinstance(extra_params, dict) else extra_params.__dict__
|
||||
@@ -130,14 +145,26 @@ class FixedIncomeViews:
|
||||
)
|
||||
country = f"United States {curve_type}"
|
||||
elif provider == "econdb":
|
||||
country = extra_params.get("country", "")
|
||||
country = country.replace("_", " ").title() if country else "United States"
|
||||
country = (
|
||||
""
|
||||
if countries
|
||||
else (
|
||||
extra_params.get("country", "")
|
||||
.replace("_", " ")
|
||||
.title()
|
||||
.replace("Ecb", "ECB")
|
||||
or "United States"
|
||||
)
|
||||
)
|
||||
|
||||
country = country + " " if country else ""
|
||||
title = kwargs.get("title", "")
|
||||
if not title:
|
||||
title = f"{country}Yield Curve"
|
||||
if len(dates) == 1:
|
||||
if len(dates) == 1 and len(countries) == 1:
|
||||
title = f"{country} Yield Curve - {dates[0]}"
|
||||
elif countries:
|
||||
title = f"Yield Curve - {', '.join(countries).replace('_', ' ').title().replace('Ecb', 'ECB')}"
|
||||
|
||||
# Update the layout of the figure.
|
||||
figure.update_layout(
|
||||
@@ -156,11 +183,7 @@ class FixedIncomeViews:
|
||||
categoryorder="array",
|
||||
categoryarray=(
|
||||
[
|
||||
(
|
||||
d.split("_")[1] + " " + d.split("_")[0].title()
|
||||
if d != "long_term"
|
||||
else "Long Term"
|
||||
)
|
||||
(d.split("_")[1] + " " + d.split("_")[0].title())
|
||||
for d in maturities
|
||||
]
|
||||
),
|
||||
@@ -186,6 +209,7 @@ class FixedIncomeViews:
|
||||
margin=dict(
|
||||
b=25,
|
||||
t=10,
|
||||
l=20,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
33
openbb_platform/openbb/assets/reference.json
vendored
33
openbb_platform/openbb/assets/reference.json
vendored
@@ -3020,7 +3020,7 @@
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[str, date], List[Union[str, date]]]",
|
||||
"type": "Union[Union[date, str], List[Union[date, str]]]",
|
||||
"description": "A specific date to get data for. Multiple items allowed for provider(s): yfinance.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
@@ -4671,7 +4671,7 @@
|
||||
"standard": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[str, date], List[Union[str, date]]]",
|
||||
"type": "Union[Union[date, str], List[Union[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,
|
||||
@@ -9657,7 +9657,7 @@
|
||||
"standard": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[str, date], List[Union[str, date]]]",
|
||||
"type": "Union[Union[date, str], List[Union[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,
|
||||
@@ -33795,7 +33795,7 @@
|
||||
"fmp": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[str, date], str]",
|
||||
"type": "Union[Union[date, str], str]",
|
||||
"description": "A specific date to get data for. Entering a date will attempt to return the NPORT-P filing for the entered date. This needs to be _exactly_ the date of the filing. Use the holdings_date command/endpoint to find available filing dates for the ETF.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
@@ -33823,7 +33823,7 @@
|
||||
"sec": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[str, date], str]",
|
||||
"type": "Union[Union[date, str], str]",
|
||||
"description": "A specific date to get data for. The date represents the period ending. The date entered will return the closest filing.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
@@ -36674,7 +36674,7 @@
|
||||
"standard": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[None, date, str], List[Union[None, date, str]]]",
|
||||
"type": "Union[Union[date, str], List[Union[date, str]]]",
|
||||
"description": "A specific date to get data for. By default is the current data. Multiple items allowed for provider(s): econdb, federal_reserve, fmp, fred.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
@@ -36684,14 +36684,17 @@
|
||||
"econdb": [
|
||||
{
|
||||
"name": "country",
|
||||
"type": "Literal['australia', 'canada', 'china', 'hong_kong', 'india', 'japan', 'mexico', 'new_zealand', 'russia', 'saudi_arabia', 'singapore', 'south_africa', 'south_korea', 'taiwan', 'thailand', 'united_kingdom', 'united_states']",
|
||||
"description": "The country to get data. New Zealand, Mexico, Singapore, and Thailand have only monthly data. The nearest date to the requested one will be used.",
|
||||
"type": "Union[str, List[str]]",
|
||||
"description": "The country to get data. New Zealand, Mexico, Singapore, and Thailand have only monthly data. The nearest date to the requested one will be used. Multiple items allowed for provider(s): econdb.",
|
||||
"default": "united_states",
|
||||
"optional": true,
|
||||
"choices": [
|
||||
"australia",
|
||||
"canada",
|
||||
"china",
|
||||
"ecb_instantaneous_forward",
|
||||
"ecb_par_yield",
|
||||
"ecb_spot_rate",
|
||||
"hong_kong",
|
||||
"india",
|
||||
"japan",
|
||||
@@ -36783,14 +36786,6 @@
|
||||
"default": "",
|
||||
"optional": false,
|
||||
"choices": null
|
||||
},
|
||||
{
|
||||
"name": "rate",
|
||||
"type": "float",
|
||||
"description": "The yield as a normalized percent (0.05 is 5%)",
|
||||
"default": "",
|
||||
"optional": false,
|
||||
"choices": null
|
||||
}
|
||||
],
|
||||
"econdb": [],
|
||||
@@ -37456,7 +37451,7 @@
|
||||
"standard": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "Union[Union[str, date], List[Union[str, date]]]",
|
||||
"type": "Union[Union[date, str], List[Union[date, str]]]",
|
||||
"description": "A specific date to get data for. Multiple items allowed for provider(s): fred.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
@@ -38767,7 +38762,7 @@
|
||||
},
|
||||
{
|
||||
"name": "date_first_added",
|
||||
"type": "Union[str, date]",
|
||||
"type": "Union[date, str]",
|
||||
"description": "Date the constituent company was added to the index.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
@@ -38783,7 +38778,7 @@
|
||||
},
|
||||
{
|
||||
"name": "founded",
|
||||
"type": "Union[str, date]",
|
||||
"type": "Union[date, str]",
|
||||
"description": "Founding year of the constituent company in the index.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
|
||||
@@ -338,7 +338,7 @@ class ROUTER_fixedincome_government(Container):
|
||||
def yield_curve(
|
||||
self,
|
||||
date: Annotated[
|
||||
Union[str, datetime.date, None, List[Union[str, datetime.date, None]]],
|
||||
Union[datetime.date, str, None, List[Union[datetime.date, str, None]]],
|
||||
OpenBBField(
|
||||
description="A specific date to get data for. By default is the current data. Multiple comma separated items allowed for provider(s): econdb, federal_reserve, fmp, fred."
|
||||
),
|
||||
@@ -355,12 +355,12 @@ class ROUTER_fixedincome_government(Container):
|
||||
|
||||
Parameters
|
||||
----------
|
||||
date : Union[str, date, None, List[Union[str, date, None]]]
|
||||
date : Union[date, str, None, List[Union[date, str, None]]]
|
||||
A specific date to get data for. By default is the current data. Multiple comma separated items allowed for provider(s): econdb, federal_reserve, fmp, fred.
|
||||
provider : Optional[Literal['econdb', 'federal_reserve', 'fmp', 'fred']]
|
||||
The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: econdb, federal_reserve, fmp, fred.
|
||||
country : Literal['australia', 'canada', 'china', 'hong_kong', 'india', 'japan', 'mexico', 'new_zealand', 'russia', 'saudi_arabia', 'singapore', 'south_africa', 'south_korea', 'taiwan', 'thailand', 'united_kingdom', 'united_states']
|
||||
The country to get data. New Zealand, Mexico, Singapore, and Thailand have only monthly data. The nearest date to the requested one will be used. (provider: econdb)
|
||||
country : str
|
||||
The country to get data. New Zealand, Mexico, Singapore, and Thailand have only monthly data. The nearest date to the requested one will be used. Multiple comma separated items allowed. (provider: econdb)
|
||||
use_cache : bool
|
||||
If true, cache the request for four hours. (provider: econdb)
|
||||
yield_curve_type : Literal['nominal', 'real', 'breakeven', 'treasury_minus_fed_funds', 'corporate_spot', 'corporate_par']
|
||||
@@ -386,8 +386,6 @@ class ROUTER_fixedincome_government(Container):
|
||||
The date of the data.
|
||||
maturity : str
|
||||
Maturity length of the security.
|
||||
rate : float
|
||||
The yield as a normalized percent (0.05 is 5%)
|
||||
|
||||
Examples
|
||||
--------
|
||||
@@ -421,7 +419,34 @@ class ROUTER_fixedincome_government(Container):
|
||||
},
|
||||
"fmp": {"multiple_items_allowed": True, "choices": None},
|
||||
"fred": {"multiple_items_allowed": True, "choices": None},
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"econdb": {
|
||||
"multiple_items_allowed": True,
|
||||
"choices": [
|
||||
"australia",
|
||||
"canada",
|
||||
"china",
|
||||
"ecb_instantaneous_forward",
|
||||
"ecb_par_yield",
|
||||
"ecb_spot_rate",
|
||||
"hong_kong",
|
||||
"india",
|
||||
"japan",
|
||||
"mexico",
|
||||
"new_zealand",
|
||||
"russia",
|
||||
"saudi_arabia",
|
||||
"singapore",
|
||||
"south_africa",
|
||||
"south_korea",
|
||||
"taiwan",
|
||||
"thailand",
|
||||
"united_kingdom",
|
||||
"united_states",
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ from openbb_core.provider.standard_models.yield_curve import (
|
||||
)
|
||||
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
||||
from openbb_core.provider.utils.errors import EmptyDataError
|
||||
from openbb_econdb.utils.yield_curves import COUNTRIES, COUNTRIES_DICT, COUNTRIES_LIST
|
||||
from openbb_econdb.utils.yield_curves import COUNTRIES_DICT, COUNTRIES_LIST
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ class EconDbYieldCurveQueryParams(YieldCurveQueryParams):
|
||||
"""EconDB Economic Indicators Query."""
|
||||
|
||||
__json_schema_extra__ = {
|
||||
"date": ["multiple_items_allowed"],
|
||||
"date": {"multiple_items_allowed": True},
|
||||
"country": {"multiple_items_allowed": True, "choices": COUNTRIES_LIST},
|
||||
}
|
||||
country: COUNTRIES = Field(
|
||||
country: str = Field(
|
||||
default="united_states",
|
||||
description=QUERY_DESCRIPTIONS.get("country", "")
|
||||
+ " New Zealand, Mexico, Singapore, and Thailand have only monthly data."
|
||||
+ " The nearest date to the requested one will be used.",
|
||||
json_schema_extra={"choices": COUNTRIES_LIST},
|
||||
)
|
||||
use_cache: bool = Field(
|
||||
default=True,
|
||||
@@ -42,9 +42,18 @@ class EconDbYieldCurveQueryParams(YieldCurveQueryParams):
|
||||
"""Validate the country."""
|
||||
if v is None:
|
||||
return "united_states"
|
||||
if v not in COUNTRIES_DICT:
|
||||
raise OpenBBError(f"Country must be one of {COUNTRIES_DICT}")
|
||||
return v
|
||||
|
||||
countries = v.split(",")
|
||||
new_countries: list = []
|
||||
|
||||
for country in countries:
|
||||
if country not in COUNTRIES_DICT:
|
||||
raise ValueError(
|
||||
f"Invalid country, {country}, must be one of {list(COUNTRIES_DICT)}"
|
||||
)
|
||||
new_countries.append(country)
|
||||
|
||||
return ",".join(new_countries)
|
||||
|
||||
|
||||
class EconDbYieldCurveData(YieldCurveData):
|
||||
@@ -59,7 +68,7 @@ class EconDbYieldCurveFetcher(
|
||||
require_credentials = False
|
||||
|
||||
@staticmethod
|
||||
def transform_query(params: Dict[str, Any]) -> EconDbYieldCurveQueryParams:
|
||||
def transform_query(params: dict[str, Any]) -> EconDbYieldCurveQueryParams:
|
||||
"""Transform the query parameters."""
|
||||
if not params.get("date"):
|
||||
params["date"] = datetime.now().strftime("%Y-%m-%d")
|
||||
@@ -68,133 +77,194 @@ class EconDbYieldCurveFetcher(
|
||||
@staticmethod
|
||||
async def aextract_data( # pylint: disable=R0914.R0912,R0915
|
||||
query: EconDbYieldCurveQueryParams,
|
||||
credentials: Optional[Dict[str, str]],
|
||||
credentials: Optional[dict[str, str]],
|
||||
**kwargs: Any,
|
||||
) -> List[Dict]:
|
||||
) -> dict:
|
||||
"""Extract the data."""
|
||||
# pylint: disable=import-outside-toplevel
|
||||
import asyncio # noqa
|
||||
from openbb_econdb.utils import helpers
|
||||
from warnings import warn
|
||||
|
||||
token = credentials.get("econdb_api_key") if credentials else "" # type: ignore
|
||||
# Attempt to create a temporary token if one is not supplied.
|
||||
if not token:
|
||||
token = await helpers.create_token(use_cache=query.use_cache)
|
||||
credentials.update({"econdb_api_key": token}) # type: ignore
|
||||
base_url = "https://www.econdb.com/api/series/?ticker="
|
||||
symbols = list(COUNTRIES_DICT[query.country].keys())
|
||||
url = (
|
||||
base_url
|
||||
+ f"%5B{','.join(symbols)}%5D&page_size=50&format=json&token={token}"
|
||||
)
|
||||
data: List[Dict] = []
|
||||
response: Union[Dict, List[Dict]] = {}
|
||||
if query.use_cache is True:
|
||||
cache_dir = f"{helpers.get_user_cache_directory()}/http/econdb_yield_curve"
|
||||
async with helpers.CachedSession(
|
||||
cache=helpers.SQLiteBackend(
|
||||
cache_dir, expire_after=3600 * 4, ignored_params=["token"]
|
||||
)
|
||||
) as session:
|
||||
try:
|
||||
response = await helpers.amake_request( # type: ignore
|
||||
url,
|
||||
session=session,
|
||||
timeout=20,
|
||||
**kwargs,
|
||||
)
|
||||
finally:
|
||||
await session.close()
|
||||
else:
|
||||
response = await helpers.amake_request( # type: ignore
|
||||
url, timeout=20, **kwargs
|
||||
|
||||
results: dict = {}
|
||||
messages: list = []
|
||||
|
||||
async def get_one_country(country):
|
||||
"""Get the data for one country."""
|
||||
base_url = "https://www.econdb.com/api/series/?ticker="
|
||||
symbols = list(COUNTRIES_DICT[country].keys())
|
||||
url = (
|
||||
base_url
|
||||
+ f"%5B{','.join(symbols)}%5D&page_size=50&format=json&token={token}"
|
||||
)
|
||||
if not response:
|
||||
raise OpenBBError("No data was returned.")
|
||||
data = response.get("results") # type: ignore
|
||||
if not data:
|
||||
raise EmptyDataError("The response was returned empty.")
|
||||
return data
|
||||
data: list = []
|
||||
response: Union[dict, list[dict]] = {}
|
||||
if query.use_cache is True:
|
||||
cache_dir = (
|
||||
f"{helpers.get_user_cache_directory()}/http/econdb_yield_curve"
|
||||
)
|
||||
async with helpers.CachedSession(
|
||||
cache=helpers.SQLiteBackend(
|
||||
cache_dir, expire_after=3600 * 4, ignored_params=["token"]
|
||||
)
|
||||
) as session:
|
||||
await session.delete_expired_responses()
|
||||
try:
|
||||
response = await helpers.amake_request( # type: ignore
|
||||
url,
|
||||
session=session,
|
||||
timeout=20,
|
||||
**kwargs,
|
||||
)
|
||||
finally:
|
||||
await session.close()
|
||||
else:
|
||||
response = await helpers.amake_request( # type: ignore
|
||||
url, timeout=20, **kwargs
|
||||
)
|
||||
if not response:
|
||||
messages.append(f"No data was returned for, {country}")
|
||||
return
|
||||
data = response.get("results") # type: ignore
|
||||
if not data:
|
||||
messages.append(f"The response for, {country}, was returned empty.")
|
||||
return
|
||||
results[country] = data
|
||||
|
||||
return
|
||||
|
||||
_countries = query.country.split(",")
|
||||
|
||||
tasks = [asyncio.create_task(get_one_country(c)) for c in _countries]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
if not results and messages:
|
||||
msg_str = "\n".join(messages)
|
||||
raise OpenBBError(msg_str)
|
||||
|
||||
if not results and not messages:
|
||||
raise OpenBBError("Unexpected outcome -> All requests were returned empty.")
|
||||
|
||||
if results and messages:
|
||||
for message in messages:
|
||||
warn(message)
|
||||
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def transform_data(
|
||||
query: EconDbYieldCurveQueryParams,
|
||||
data: List[Dict],
|
||||
data: dict,
|
||||
**kwargs: Any,
|
||||
) -> AnnotatedResult[List[EconDbYieldCurveData]]:
|
||||
) -> AnnotatedResult[list[EconDbYieldCurveData]]:
|
||||
"""Transform the data."""
|
||||
# pylint: disable=import-outside-toplevel
|
||||
from numpy import nan
|
||||
from pandas import Categorical, DataFrame, DatetimeIndex
|
||||
|
||||
maturity_order = list(COUNTRIES_DICT[query.country].values())
|
||||
dates = query.date.split(",") # type: ignore
|
||||
dates_list = DatetimeIndex(dates)
|
||||
new_data: Dict = {}
|
||||
metadata: Dict = {}
|
||||
# Unpack the data for each maturity.
|
||||
for item in data:
|
||||
ticker = item.get("ticker")
|
||||
maturity = COUNTRIES_DICT[query.country].get(ticker)
|
||||
dataset = item.get("dataset")
|
||||
additional_metadata = item.get("additional_metadata")
|
||||
units = "percent"
|
||||
description = item.get("description")
|
||||
dates = item["data"]["dates"]
|
||||
rates = item["data"]["values"]
|
||||
meta = {
|
||||
"symbol": ticker,
|
||||
"description": description,
|
||||
"dataset": dataset,
|
||||
"units": units,
|
||||
"additional_metadata": additional_metadata,
|
||||
}
|
||||
metadata[maturity] = meta
|
||||
for d, r in zip(dates, rates):
|
||||
if maturity not in new_data:
|
||||
new_data[maturity] = {}
|
||||
new_data[maturity][d] = r
|
||||
results_metadata: dict = {}
|
||||
results_data: list = []
|
||||
|
||||
# Create a DataFrame from the data
|
||||
df = DataFrame(new_data)
|
||||
def process_country_data(country, country_data):
|
||||
"""Process the data for one country."""
|
||||
maturity_order = list(COUNTRIES_DICT[country].values())
|
||||
dates = query.date.split(",") # type: ignore
|
||||
dates_list = DatetimeIndex(dates)
|
||||
new_data: Dict = {}
|
||||
metadata: Dict = {}
|
||||
# Unpack the data for each maturity.
|
||||
for item in country_data:
|
||||
ticker = item.get("ticker")
|
||||
maturity = COUNTRIES_DICT[country].get(ticker)
|
||||
dataset = item.get("dataset")
|
||||
additional_metadata = item.get("additional_metadata")
|
||||
units = "percent"
|
||||
description = item.get("description")
|
||||
dates = item["data"]["dates"]
|
||||
rates = item["data"]["values"]
|
||||
meta = {
|
||||
"symbol": ticker,
|
||||
"description": description,
|
||||
"dataset": dataset,
|
||||
"units": units,
|
||||
"additional_metadata": additional_metadata,
|
||||
}
|
||||
metadata[maturity] = meta
|
||||
for d, r in zip(dates, rates):
|
||||
if maturity not in new_data:
|
||||
new_data[maturity] = {}
|
||||
new_data[maturity][d] = r
|
||||
|
||||
# Convert the index to a DatetimeIndex
|
||||
df.index = DatetimeIndex(df.index)
|
||||
# Create a DataFrame from the data
|
||||
df = DataFrame(new_data)
|
||||
|
||||
# Find the nearest date in the DataFrame to each date in dates_list
|
||||
def get_nearest_date(df, target_date):
|
||||
differences = (df.index - target_date).to_series().abs()
|
||||
nearest_date_index = differences.argmin()
|
||||
nearest_date = df.index[nearest_date_index]
|
||||
return nearest_date
|
||||
# Convert the index to a DatetimeIndex
|
||||
df.index = DatetimeIndex(df.index)
|
||||
|
||||
nearest_dates = [get_nearest_date(df, date) for date in dates_list]
|
||||
# Find the nearest date in the DataFrame to each date in dates_list
|
||||
def get_nearest_date(df, target_date):
|
||||
differences = (df.index - target_date).to_series().abs()
|
||||
nearest_date_index = differences.argmin()
|
||||
nearest_date = df.index[nearest_date_index]
|
||||
return nearest_date
|
||||
|
||||
# Filter for only the nearest dates
|
||||
df = df[df.index.isin(nearest_dates)]
|
||||
nearest_dates = [get_nearest_date(df, date) for date in dates_list]
|
||||
|
||||
# Flatten the DataFrame
|
||||
flattened_data = df.reset_index().melt(
|
||||
id_vars="index", var_name="maturity", value_name="rate"
|
||||
)
|
||||
flattened_data = flattened_data.rename(columns={"index": "date"}).sort_values(
|
||||
"date"
|
||||
)
|
||||
flattened_data["maturity"] = Categorical(
|
||||
flattened_data["maturity"], categories=maturity_order, ordered=True
|
||||
)
|
||||
flattened_data["rate"] = flattened_data["rate"].astype(float) / 100
|
||||
flattened_data = flattened_data.sort_values(
|
||||
by=["date", "maturity"]
|
||||
).reset_index(drop=True)
|
||||
flattened_data.loc[:, "date"] = flattened_data["date"].dt.strftime("%Y-%m-%d")
|
||||
records = flattened_data.copy()
|
||||
records["rate"] = records["rate"].fillna("N/A").replace("N/A", None)
|
||||
records = records.to_dict("records")
|
||||
results: List[EconDbYieldCurveData] = [
|
||||
EconDbYieldCurveData.model_validate(r) for r in records if r.get("rate")
|
||||
]
|
||||
if not results:
|
||||
# Filter for only the nearest dates
|
||||
df = df[df.index.isin(nearest_dates)]
|
||||
|
||||
# Flatten the DataFrame
|
||||
flattened_data = df.reset_index().melt(
|
||||
id_vars="index", var_name="maturity", value_name="rate"
|
||||
)
|
||||
flattened_data = flattened_data.rename(
|
||||
columns={"index": "date"}
|
||||
).sort_values("date")
|
||||
flattened_data["maturity"] = Categorical(
|
||||
flattened_data["maturity"], categories=maturity_order, ordered=True
|
||||
)
|
||||
flattened_data["rate"] = flattened_data["rate"].astype(float) / 100
|
||||
flattened_data = flattened_data.sort_values(
|
||||
by=["date", "maturity"]
|
||||
).reset_index(drop=True)
|
||||
flattened_data.loc[:, "date"] = flattened_data["date"].dt.strftime(
|
||||
"%Y-%m-%d"
|
||||
)
|
||||
new_df = flattened_data.copy()
|
||||
new_df.loc[:, "country"] = country
|
||||
|
||||
def convert_duration(x):
|
||||
"""Convert the duration to a decimal representation of years."""
|
||||
period, unit = x.split("_")
|
||||
if period == "long" and unit == "term":
|
||||
return 30
|
||||
if period == "year":
|
||||
return int(unit)
|
||||
return int(unit) / 12
|
||||
|
||||
new_df.loc[:, "maturity_years"] = new_df.maturity.apply(convert_duration)
|
||||
|
||||
new_df = new_df.replace({nan: None})
|
||||
records = [
|
||||
EconDbYieldCurveData.model_validate(r)
|
||||
for r in new_df.to_dict("records")
|
||||
if r.get("rate")
|
||||
]
|
||||
results_data.extend(records)
|
||||
results_metadata[country] = metadata
|
||||
|
||||
for country, country_data in data.items():
|
||||
process_country_data(country, country_data)
|
||||
|
||||
if not results_data:
|
||||
raise EmptyDataError(
|
||||
f"No data was found for the country, {query.country},"
|
||||
f" and dates, {query.date}"
|
||||
)
|
||||
return AnnotatedResult(result=results, metadata=metadata)
|
||||
return AnnotatedResult(result=results_data, metadata=results_metadata)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""EconDB Yield Curve Utilities"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
DAILY = {
|
||||
"australia": {
|
||||
"RBA_F02D.FCMYGBAG2D.D.AU": "year_2",
|
||||
@@ -20,7 +18,7 @@ DAILY = {
|
||||
"BOC_BOND_YIELDS_ALL.BD_CDN_5YR_DQ_YLD.D.CA": "year_5",
|
||||
"BOC_BOND_YIELDS_ALL.BD_CDN_7YR_DQ_YLD.D.CA": "year_7",
|
||||
"BOC_BOND_YIELDS_ALL.BD_CDN_10YR_DQ_YLD.D.CA": "year_10",
|
||||
"BOC_BOND_YIELDS_ALL.BD_CDN_LONG_DQ_YLD.D.CA": "long_term",
|
||||
"BOC_BOND_YIELDS_ALL.BD_CDN_LONG_DQ_YLD.D.CA": "year_30",
|
||||
},
|
||||
"china": {
|
||||
"CCDC_CGB.CHB_3M.D.CN": "month_3",
|
||||
@@ -134,7 +132,56 @@ DAILY = {
|
||||
"FRB_H15.C279E.D.US": "year_20",
|
||||
"FRB_H15.F5280.D.US": "year_30",
|
||||
},
|
||||
"ecb_spot_rate": {
|
||||
"ECBYC.035E9053E2.B.EA": "month_3",
|
||||
"ECBYC.03610053E2.B.EA": "month_6",
|
||||
"ECBYC.0354C053E2.B.EA": "year_1",
|
||||
"ECBYC.035D1053E2.B.EA": "year_2",
|
||||
"ECBYC.035EA053E2.B.EA": "year_3",
|
||||
"ECBYC.035F7053E2.B.EA": "year_4",
|
||||
"ECBYC.03604053E2.B.EA": "year_5",
|
||||
"ECBYC.03611053E2.B.EA": "year_6",
|
||||
"ECBYC.0361E053E2.B.EA": "year_7",
|
||||
"ECBYC.0362B053E2.B.EA": "year_8",
|
||||
"ECBYC.03638053E2.B.EA": "year_9",
|
||||
"ECBYC.03558053E2.B.EA": "year_10",
|
||||
"ECBYC.034D2053E2.B.EA": "year_20",
|
||||
"ECBYC.035DD053E2.B.EA": "year_30",
|
||||
},
|
||||
"ecb_par_yield": {
|
||||
"ECBYC.0317C053E2.B.EA": "month_3",
|
||||
"ECBYC.031A3053E2.B.EA": "month_6",
|
||||
"ECBYC.030DF053E2.B.EA": "year_1",
|
||||
"ECBYC.03164053E2.B.EA": "year_2",
|
||||
"ECBYC.0317D053E2.B.EA": "year_3",
|
||||
"ECBYC.0318A053E2.B.EA": "year_4",
|
||||
"ECBYC.03197053E2.B.EA": "year_5",
|
||||
"ECBYC.031A4053E2.B.EA": "year_6",
|
||||
"ECBYC.031B1053E2.B.EA": "year_7",
|
||||
"ECBYC.031BE053E2.B.EA": "year_8",
|
||||
"ECBYC.031CB053E2.B.EA": "year_9",
|
||||
"ECBYC.03065053E2.B.EA": "year_10",
|
||||
"ECBYC.030EB053E2.B.EA": "year_20",
|
||||
"ECBYC.03170053E2.B.EA": "year_30",
|
||||
},
|
||||
"ecb_instantaneous_forward": {
|
||||
"ECBYC.02C71053E2.B.EA": "month_3",
|
||||
"ECBYC.02C98053E2.B.EA": "month_6",
|
||||
"ECBYC.02BD4053E2.B.EA": "year_1",
|
||||
"ECBYC.02C59053E2.B.EA": "year_2",
|
||||
"ECBYC.02C72053E2.B.EA": "year_3",
|
||||
"ECBYC.02C7F053E2.B.EA": "year_4",
|
||||
"ECBYC.02C8C053E2.B.EA": "year_5",
|
||||
"ECBYC.02C99053E2.B.EA": "year_6",
|
||||
"ECBYC.02CA6053E2.B.EA": "year_7",
|
||||
"ECBYC.02CB3053E2.B.EA": "year_8",
|
||||
"ECBYC.02CC0053E2.B.EA": "year_9",
|
||||
"ECBYC.02B5A053E2.B.EA": "year_10",
|
||||
"ECBYC.02BE0053E2.B.EA": "year_20",
|
||||
"ECBYC.02C65053E2.B.EA": "year_30",
|
||||
},
|
||||
}
|
||||
|
||||
MONTHLY = {
|
||||
"thailand": {
|
||||
"BOTFM_RT_001_S2.36.M.TH": "month_1",
|
||||
@@ -198,22 +245,3 @@ COUNTRIES_DICT = {
|
||||
**DAILY,
|
||||
**MONTHLY,
|
||||
}
|
||||
COUNTRIES = Literal[
|
||||
"australia",
|
||||
"canada",
|
||||
"china",
|
||||
"hong_kong",
|
||||
"india",
|
||||
"japan",
|
||||
"mexico",
|
||||
"new_zealand",
|
||||
"russia",
|
||||
"saudi_arabia",
|
||||
"singapore",
|
||||
"south_africa",
|
||||
"south_korea",
|
||||
"taiwan",
|
||||
"thailand",
|
||||
"united_kingdom",
|
||||
"united_states",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user