mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-08 10:22:09 +00:00
Merge remote-tracking branch 'OpenBB-finance/develop' into feature/argparse_translator
This commit is contained in:
@@ -88,6 +88,7 @@ AppLoader.from_routers(
|
||||
@app.exception_handler(Exception)
|
||||
async def api_exception_handler(_: Request, exc: Exception):
|
||||
"""Exception handler for all other exceptions."""
|
||||
logger.error(exc)
|
||||
return JSONResponse(
|
||||
status_code=404,
|
||||
content={
|
||||
@@ -100,6 +101,7 @@ async def api_exception_handler(_: Request, exc: Exception):
|
||||
@app.exception_handler(OpenBBError)
|
||||
async def openbb_exception_handler(_: Request, exc: OpenBBError):
|
||||
"""Exception handler for OpenBB errors."""
|
||||
logger.error(exc.original)
|
||||
openbb_error = exc.original
|
||||
status_code = 400 if "No results" in str(openbb_error) else 500
|
||||
return JSONResponse(
|
||||
|
||||
@@ -2,6 +2,8 @@ import sys
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Optional
|
||||
|
||||
from typing_extensions import LiteralString
|
||||
|
||||
# `slots` is available on Python >= 3.10
|
||||
if sys.version_info >= (3, 10):
|
||||
slots_true = {"slots": True}
|
||||
@@ -24,3 +26,10 @@ class OpenBBCustomParameter(BaseMetadata):
|
||||
"""Custom parameter for OpenBB."""
|
||||
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True, **slots_true)
|
||||
class OpenBBCustomChoices(BaseMetadata):
|
||||
"""Custom choices for OpenBB."""
|
||||
|
||||
choices: Optional[LiteralString] = None
|
||||
|
||||
@@ -36,7 +36,10 @@ from starlette.routing import BaseRoute
|
||||
from typing_extensions import Annotated, _AnnotatedAlias
|
||||
|
||||
from openbb_core.app.extension_loader import ExtensionLoader, OpenBBGroups
|
||||
from openbb_core.app.model.custom_parameter import OpenBBCustomParameter
|
||||
from openbb_core.app.model.custom_parameter import (
|
||||
OpenBBCustomChoices,
|
||||
OpenBBCustomParameter,
|
||||
)
|
||||
from openbb_core.app.provider_interface import ProviderInterface
|
||||
from openbb_core.app.router import CommandMap, RouterLoader
|
||||
from openbb_core.app.static.utils.console import Console
|
||||
@@ -330,9 +333,7 @@ class ImportDefinition:
|
||||
hint_type_list = cls.get_path_hint_type_list(path=path)
|
||||
code = "from openbb_core.app.static.container import Container"
|
||||
code += "\nfrom openbb_core.app.model.obbject import OBBject"
|
||||
code += (
|
||||
"\nfrom openbb_core.app.model.custom_parameter import OpenBBCustomParameter"
|
||||
)
|
||||
code += "\nfrom openbb_core.app.model.custom_parameter import OpenBBCustomParameter, OpenBBCustomChoices"
|
||||
|
||||
# These imports were not detected before build, so we add them manually and
|
||||
# ruff --fix the resulting code to remove unused imports.
|
||||
@@ -500,7 +501,10 @@ class MethodDefinition:
|
||||
"""Get json schema extra."""
|
||||
field_default = getattr(field, "default", None)
|
||||
if field_default:
|
||||
return getattr(field_default, "json_schema_extra", {})
|
||||
# Getting json_schema_extra without changing the original dict
|
||||
json_schema_extra = getattr(field_default, "json_schema_extra", {}).copy()
|
||||
json_schema_extra.pop("choices", None)
|
||||
return json_schema_extra
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
@@ -571,7 +575,9 @@ class MethodDefinition:
|
||||
type_ = MethodDefinition.get_type(field)
|
||||
default = MethodDefinition.get_default(field)
|
||||
extra = MethodDefinition.get_extra(field)
|
||||
new_type = MethodDefinition.get_expanded_type(field_name, extra)
|
||||
new_type = MethodDefinition.get_expanded_type(
|
||||
field_name, extra, type_
|
||||
)
|
||||
updated_type = type_ if new_type is ... else Union[type_, new_type]
|
||||
|
||||
formatted[field_name] = Parameter(
|
||||
@@ -602,10 +608,10 @@ class MethodDefinition:
|
||||
return MethodDefinition.reorder_params(params=formatted)
|
||||
|
||||
@staticmethod
|
||||
def add_field_descriptions(
|
||||
def add_field_custom_annotations(
|
||||
od: OrderedDict[str, Parameter], model_name: Optional[str] = None
|
||||
):
|
||||
"""Add the field description to the param signature."""
|
||||
"""Add the field custom description and choices to the param signature as annotations."""
|
||||
if model_name:
|
||||
available_fields: Dict[str, Field] = (
|
||||
ProviderInterface().params[model_name]["standard"].__dataclass_fields__
|
||||
@@ -615,16 +621,28 @@ class MethodDefinition:
|
||||
if param not in available_fields:
|
||||
continue
|
||||
|
||||
field = available_fields[param]
|
||||
field_default = available_fields[param].default
|
||||
|
||||
new_value = value.replace(
|
||||
annotation=Annotated[
|
||||
value.annotation,
|
||||
OpenBBCustomParameter(
|
||||
description=getattr(field.default, "description", "")
|
||||
),
|
||||
],
|
||||
choices = getattr(field_default, "json_schema_extra", {}).get(
|
||||
"choices", []
|
||||
)
|
||||
description = getattr(field_default, "description", "")
|
||||
|
||||
if choices:
|
||||
new_value = value.replace(
|
||||
annotation=Annotated[
|
||||
value.annotation,
|
||||
OpenBBCustomParameter(description=description),
|
||||
OpenBBCustomChoices(choices=choices),
|
||||
],
|
||||
)
|
||||
else:
|
||||
new_value = value.replace(
|
||||
annotation=Annotated[
|
||||
value.annotation,
|
||||
OpenBBCustomParameter(description=description),
|
||||
],
|
||||
)
|
||||
|
||||
od[param] = new_value
|
||||
|
||||
@@ -665,7 +683,7 @@ class MethodDefinition:
|
||||
model_name: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Build the command method signature."""
|
||||
MethodDefinition.add_field_descriptions(
|
||||
MethodDefinition.add_field_custom_annotations(
|
||||
od=formatted_params, model_name=model_name
|
||||
) # this modified `od` in place
|
||||
func_params = MethodDefinition.build_func_params(formatted_params)
|
||||
@@ -782,10 +800,19 @@ class MethodDefinition:
|
||||
return code
|
||||
|
||||
@classmethod
|
||||
def get_expanded_type(cls, field_name: str, extra: Optional[dict] = None) -> object:
|
||||
def get_expanded_type(
|
||||
cls,
|
||||
field_name: str,
|
||||
extra: Optional[dict] = None,
|
||||
original_type: Optional[type] = None,
|
||||
) -> object:
|
||||
"""Expand the original field type."""
|
||||
if extra and "multiple_items_allowed" in extra:
|
||||
return List[str]
|
||||
if original_type is None:
|
||||
raise ValueError(
|
||||
"multiple_items_allowed requires the original type to be specified."
|
||||
)
|
||||
return List[original_type]
|
||||
return cls.TYPE_EXPANSION.get(field_name, ...)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -80,8 +80,9 @@ def exception_handler(func: Callable[P, R]) -> Callable[P, R]:
|
||||
).with_traceback(tb) from None
|
||||
|
||||
# If the error is not a ValidationError, then it is a generic exception
|
||||
error_type = getattr(e, "original", e).__class__.__name__
|
||||
raise OpenBBError(
|
||||
f"\nType -> {e.original.__class__.__name__}\n\nDetail -> {str(e)}"
|
||||
f"\nType -> {error_type}\n\nDetail -> {str(e)}"
|
||||
).with_traceback(tb) from None
|
||||
|
||||
return wrapper
|
||||
|
||||
@@ -29,7 +29,9 @@ def filter_inputs(
|
||||
if field in kwargs.get(p, {}):
|
||||
current = kwargs[p][field]
|
||||
new = (
|
||||
",".join(current) if isinstance(current, list) else current
|
||||
",".join(map(str, current))
|
||||
if isinstance(current, list)
|
||||
else current
|
||||
)
|
||||
|
||||
if provider and provider not in props[PROPERTY]:
|
||||
|
||||
@@ -74,7 +74,10 @@ CPI_FREQUENCY = Literal["monthly", "quarter", "annual"]
|
||||
class ConsumerPriceIndexQueryParams(QueryParams):
|
||||
"""CPI Query."""
|
||||
|
||||
country: str = Field(description=QUERY_DESCRIPTIONS.get("country"))
|
||||
country: str = Field(
|
||||
description=QUERY_DESCRIPTIONS.get("country"),
|
||||
choices=CPI_COUNTRIES, # type: ignore
|
||||
)
|
||||
units: CPI_UNITS = Field(
|
||||
default="growth_same",
|
||||
description=QUERY_DESCRIPTIONS.get("units", "")
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
from datetime import (
|
||||
date as dateType,
|
||||
)
|
||||
from typing import List, Literal, Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from pydantic import Field, field_validator
|
||||
from pydantic import Field
|
||||
|
||||
from openbb_core.provider.abstract.data import Data
|
||||
from openbb_core.provider.abstract.query_params import QueryParams
|
||||
@@ -26,25 +26,15 @@ class SpotRateQueryParams(QueryParams):
|
||||
default=None,
|
||||
description=QUERY_DESCRIPTIONS.get("end_date", ""),
|
||||
)
|
||||
maturity: List[float] = Field(
|
||||
default=[10.0], description="The maturities in years."
|
||||
maturity: Union[float, str] = Field(
|
||||
default=10.0, description="Maturities in years."
|
||||
)
|
||||
category: List[Literal["par_yield", "spot_rate"]] = Field(
|
||||
default=["spot_rate"],
|
||||
description="The category.",
|
||||
category: str = Field(
|
||||
default="spot_rate",
|
||||
description="Rate category. Options: spot_rate, par_yield.",
|
||||
choices=["par_yield", "spot_rate"],
|
||||
)
|
||||
|
||||
@field_validator("maturity")
|
||||
@classmethod
|
||||
def maturity_validate(cls, v):
|
||||
"""Validate maturity."""
|
||||
for i in v:
|
||||
if not isinstance(i, float):
|
||||
raise ValueError("`maturity` must be a float")
|
||||
if not 1 <= i <= 100:
|
||||
raise ValueError("`maturity` must be between 1 and 100")
|
||||
return v
|
||||
|
||||
|
||||
class SpotRateData(Data):
|
||||
"""Spot Rate Data."""
|
||||
|
||||
@@ -1675,7 +1675,7 @@ def test_equity_darkpool_otc(params, headers):
|
||||
"params",
|
||||
[
|
||||
({"provider": "fmp", "market": "euronext"}),
|
||||
({"provider": "polygon"}), # premium endpoint
|
||||
# ({"provider": "polygon"}), # premium endpoint
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Python interface integration tests for the equity extension."""
|
||||
|
||||
from datetime import time
|
||||
from datetime import date, time, timedelta
|
||||
|
||||
import pytest
|
||||
from extensions.tests.conftest import parametrize
|
||||
@@ -767,8 +767,6 @@ def test_equity_fundamental_revenue_per_segment(params, obb):
|
||||
"symbol": "IBM:US",
|
||||
"start_date": "2023-09-30",
|
||||
"end_date": "2023-12-31",
|
||||
"limit": None,
|
||||
"form_type": None,
|
||||
}
|
||||
),
|
||||
],
|
||||
@@ -878,8 +876,8 @@ def test_equity_compare_groups(params, obb):
|
||||
{
|
||||
"provider": "cboe",
|
||||
"symbol": "AAPL",
|
||||
"start_date": "2024-02-19",
|
||||
"end_date": "2024-02-20",
|
||||
"start_date": (date.today() - timedelta(days=1)).strftime("%Y-%m-%d"),
|
||||
"end_date": date.today().strftime("%Y-%m-%d"),
|
||||
"interval": "1m",
|
||||
"use_cache": False,
|
||||
}
|
||||
@@ -1572,7 +1570,7 @@ def test_equity_darkpool_otc(params, obb):
|
||||
"params",
|
||||
[
|
||||
({"provider": "fmp", "market": "euronext"}),
|
||||
({"provider": "polygon"}), # premium endpoint
|
||||
# ({"provider": "polygon"}), # premium endpoint
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
|
||||
@@ -341,10 +341,26 @@ def test_fixedincome_corporate_commercial_paper(params, headers):
|
||||
"start_date": "2023-01-01",
|
||||
"end_date": "2023-06-06",
|
||||
"maturity": [10.0],
|
||||
"category": ["spot_rate"],
|
||||
"category": "spot_rate",
|
||||
"provider": "fred",
|
||||
}
|
||||
)
|
||||
),
|
||||
(
|
||||
{
|
||||
"start_date": None,
|
||||
"end_date": None,
|
||||
"maturity": 5.5,
|
||||
"category": ["spot_rate"],
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"start_date": None,
|
||||
"end_date": None,
|
||||
"maturity": "1,5.5,10",
|
||||
"category": "spot_rate,par_yield",
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
|
||||
@@ -312,10 +312,26 @@ def test_fixedincome_corporate_commercial_paper(params, obb):
|
||||
"start_date": "2023-01-01",
|
||||
"end_date": "2023-06-06",
|
||||
"maturity": [10.0],
|
||||
"category": ["spot_rate"],
|
||||
"category": "spot_rate",
|
||||
"provider": "fred",
|
||||
}
|
||||
)
|
||||
),
|
||||
(
|
||||
{
|
||||
"start_date": None,
|
||||
"end_date": None,
|
||||
"maturity": 5.5,
|
||||
"category": ["spot_rate"],
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"start_date": None,
|
||||
"end_date": None,
|
||||
"maturity": "1,5.5,10",
|
||||
"category": "spot_rate,par_yield",
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
|
||||
@@ -26,9 +26,11 @@ def get_headers():
|
||||
return data["headers"]
|
||||
|
||||
|
||||
def request_data(menu: str, symbol: str, provider: str):
|
||||
def request_data(
|
||||
menu: str, symbol: str, provider: str, start_date: str = "", end_date: str = ""
|
||||
):
|
||||
"""Randomly pick a symbol and a provider and get data from the selected menu."""
|
||||
url = f"http://0.0.0.0:8000/api/v1/{menu}/price/historical?symbol={symbol}&provider={provider}"
|
||||
url = f"http://0.0.0.0:8000/api/v1/{menu}/price/historical?symbol={symbol}&provider={provider}&start_date={start_date}&end_date={end_date}"
|
||||
result = requests.get(url, headers=get_headers(), timeout=10)
|
||||
return result.json()["results"]
|
||||
|
||||
@@ -40,7 +42,13 @@ def get_stocks_data():
|
||||
symbol = random.choice(["AAPL", "NVDA", "MSFT", "TSLA", "AMZN", "V"]) # noqa: S311
|
||||
provider = random.choice(["fmp", "polygon", "yfinance"]) # noqa: S311
|
||||
|
||||
data["stocks_data"] = request_data("equity", symbol=symbol, provider=provider)
|
||||
data["stocks_data"] = request_data(
|
||||
menu="equity",
|
||||
symbol=symbol,
|
||||
provider=provider,
|
||||
start_date="2023-01-01",
|
||||
end_date="2023-12-31",
|
||||
)
|
||||
return data["stocks_data"]
|
||||
|
||||
|
||||
@@ -56,6 +64,8 @@ def get_crypto_data():
|
||||
menu="crypto",
|
||||
symbol=symbol,
|
||||
provider=provider,
|
||||
start_date="2023-01-01",
|
||||
end_date="2023-12-31",
|
||||
)
|
||||
return data["crypto_data"]
|
||||
|
||||
@@ -244,7 +254,7 @@ def test_quantitative_performance_sharpe_ratio(params, data_type):
|
||||
"data": "",
|
||||
"target": "close",
|
||||
"target_return": "0.5",
|
||||
"window": "275",
|
||||
"window": "150",
|
||||
"adjusted": "true",
|
||||
"index": "date",
|
||||
},
|
||||
|
||||
@@ -144,7 +144,7 @@ def stdev(data: List[Data], target: str) -> OBBject[List[Data]]:
|
||||
'obb.quantitative.stats.kurtosis(data=returns, target="close")',
|
||||
],
|
||||
)
|
||||
def kurtosis(data: List[Data], target) -> OBBject[List[Data]]:
|
||||
def kurtosis(data: List[Data], target: str) -> OBBject[List[Data]]:
|
||||
"""
|
||||
Calculate the rolling kurtosis of a target column.
|
||||
|
||||
@@ -154,10 +154,11 @@ def kurtosis(data: List[Data], target) -> OBBject[List[Data]]:
|
||||
This function helps in assessing the risk of outliers in financial returns or other time series data.
|
||||
|
||||
Parameters
|
||||
data: List[Data]
|
||||
The time series data as a list of data points.
|
||||
target: str
|
||||
The name of the column for which to calculate kurtosis.
|
||||
----------
|
||||
data: List[Data]
|
||||
The time series data as a list of data points.
|
||||
target: str
|
||||
The name of the column for which to calculate kurtosis.
|
||||
|
||||
Returns
|
||||
------
|
||||
|
||||
@@ -623,6 +623,8 @@ def vwap(
|
||||
The calculated data.
|
||||
"""
|
||||
df = basemodel_to_df(data, index=index)
|
||||
if index == "date":
|
||||
df.index = pd.to_datetime(df.index)
|
||||
df_target = get_target_columns(df, ["high", "low", "close", "volume"])
|
||||
df_vwap = pd.DataFrame(df_target.ta.vwap(anchor=anchor, offset=offset).dropna())
|
||||
|
||||
|
||||
@@ -89,13 +89,13 @@ def list_openbb_extensions() -> Tuple[Set[str], Set[str], Set[str]]:
|
||||
obbject_extensions = set()
|
||||
entry_points_dict = entry_points()
|
||||
|
||||
for entry_point in entry_points_dict["openbb_core_extension"]:
|
||||
for entry_point in entry_points_dict.get("openbb_core_extension", []):
|
||||
core_extensions.add(f"{entry_point.name}")
|
||||
|
||||
for entry_point in entry_points_dict["openbb_provider_extension"]:
|
||||
for entry_point in entry_points_dict.get("openbb_provider_extension", []):
|
||||
provider_extensions.add(f"{entry_point.name}")
|
||||
|
||||
for entry_point in entry_points_dict["openbb_obbject_extension"]:
|
||||
for entry_point in entry_points_dict.get("openbb_obbject_extension", []):
|
||||
obbject_extensions.add(f"{entry_point.name}")
|
||||
|
||||
return core_extensions, provider_extensions, obbject_extensions
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
import datetime
|
||||
from typing import List, Literal, Optional, Union
|
||||
|
||||
from openbb_core.app.model.custom_parameter import OpenBBCustomParameter
|
||||
from openbb_core.app.model.custom_parameter import (
|
||||
OpenBBCustomChoices,
|
||||
OpenBBCustomParameter,
|
||||
)
|
||||
from openbb_core.app.model.obbject import OBBject
|
||||
from openbb_core.app.static.container import Container
|
||||
from openbb_core.app.static.utils.decorators import exception_handler, validate
|
||||
@@ -240,6 +243,59 @@ class ROUTER_economy(Container):
|
||||
OpenBBCustomParameter(
|
||||
description="The country to get data. Multiple items allowed for provider(s): fred."
|
||||
),
|
||||
OpenBBCustomChoices(
|
||||
choices=[
|
||||
"australia",
|
||||
"austria",
|
||||
"belgium",
|
||||
"brazil",
|
||||
"bulgaria",
|
||||
"canada",
|
||||
"chile",
|
||||
"china",
|
||||
"croatia",
|
||||
"cyprus",
|
||||
"czech_republic",
|
||||
"denmark",
|
||||
"estonia",
|
||||
"euro_area",
|
||||
"finland",
|
||||
"france",
|
||||
"germany",
|
||||
"greece",
|
||||
"hungary",
|
||||
"iceland",
|
||||
"india",
|
||||
"indonesia",
|
||||
"ireland",
|
||||
"israel",
|
||||
"italy",
|
||||
"japan",
|
||||
"korea",
|
||||
"latvia",
|
||||
"lithuania",
|
||||
"luxembourg",
|
||||
"malta",
|
||||
"mexico",
|
||||
"netherlands",
|
||||
"new_zealand",
|
||||
"norway",
|
||||
"poland",
|
||||
"portugal",
|
||||
"romania",
|
||||
"russian_federation",
|
||||
"slovak_republic",
|
||||
"slovakia",
|
||||
"slovenia",
|
||||
"south_africa",
|
||||
"spain",
|
||||
"sweden",
|
||||
"switzerland",
|
||||
"turkey",
|
||||
"united_kingdom",
|
||||
"united_states",
|
||||
]
|
||||
),
|
||||
],
|
||||
units: Annotated[
|
||||
Literal["growth_previous", "growth_same", "index_2015"],
|
||||
|
||||
@@ -401,7 +401,7 @@ class ROUTER_equity_estimates(Container):
|
||||
def price_target(
|
||||
self,
|
||||
symbol: Annotated[
|
||||
Union[str, None, List[str]],
|
||||
Union[str, None, List[Optional[str]]],
|
||||
OpenBBCustomParameter(
|
||||
description="Symbol to get data for. Multiple items allowed for provider(s): benzinga."
|
||||
),
|
||||
@@ -417,7 +417,7 @@ class ROUTER_equity_estimates(Container):
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : Union[str, None, List[str]]
|
||||
symbol : Union[str, None, List[Optional[str]]]
|
||||
Symbol to get data for. Multiple items allowed for provider(s): benzinga.
|
||||
limit : int
|
||||
The number of data entries to return.
|
||||
|
||||
@@ -708,6 +708,10 @@ class ROUTER_equity_fundamental(Container):
|
||||
Link to the filing. (provider: fmp)
|
||||
final_link : Optional[str]
|
||||
Link to the filing document. (provider: fmp)
|
||||
net_income_continuing_operations : Optional[float]
|
||||
Net Income (Continuing Operations) (provider: intrinio)
|
||||
net_income_discontinued_operations : Optional[float]
|
||||
Net Income (Discontinued Operations) (provider: intrinio)
|
||||
provision_for_loan_losses : Optional[float]
|
||||
Provision for Loan Losses (provider: intrinio)
|
||||
provision_for_credit_losses : Optional[float]
|
||||
@@ -726,10 +730,6 @@ class ROUTER_equity_fundamental(Container):
|
||||
Net Cash from Continuing Operating Activities (provider: intrinio)
|
||||
net_cash_from_discontinued_operating_activities : Optional[float]
|
||||
Net Cash from Discontinued Operating Activities (provider: intrinio)
|
||||
net_income_continuing_operations : Optional[float]
|
||||
Net Income (Continuing Operations) (provider: intrinio)
|
||||
net_income_discontinued_operations : Optional[float]
|
||||
Net Income (Discontinued Operations) (provider: intrinio)
|
||||
divestitures : Optional[float]
|
||||
Divestitures (provider: intrinio)
|
||||
sale_of_property_plant_and_equipment : Optional[float]
|
||||
|
||||
@@ -419,12 +419,17 @@ class ROUTER_fixedincome_corporate(Container):
|
||||
),
|
||||
] = None,
|
||||
maturity: Annotated[
|
||||
List[float], OpenBBCustomParameter(description="The maturities in years.")
|
||||
] = [10.0],
|
||||
Union[float, str, List[Union[float, str]]],
|
||||
OpenBBCustomParameter(
|
||||
description="Maturities in years. Multiple items allowed for provider(s): fred."
|
||||
),
|
||||
] = 10.0,
|
||||
category: Annotated[
|
||||
List[Literal["par_yield", "spot_rate"]],
|
||||
OpenBBCustomParameter(description="The category."),
|
||||
] = ["spot_rate"],
|
||||
Union[str, List[str]],
|
||||
OpenBBCustomParameter(
|
||||
description="Rate category. Options: spot_rate, par_yield. Multiple items allowed for provider(s): fred."
|
||||
),
|
||||
] = "spot_rate",
|
||||
provider: Optional[Literal["fred"]] = None,
|
||||
**kwargs
|
||||
) -> OBBject:
|
||||
@@ -442,10 +447,10 @@ class ROUTER_fixedincome_corporate(Container):
|
||||
Start date of the data, in YYYY-MM-DD format.
|
||||
end_date : Union[datetime.date, None, str]
|
||||
End date of the data, in YYYY-MM-DD format.
|
||||
maturity : List[float]
|
||||
The maturities in years.
|
||||
category : List[Literal['par_yield', 'spot_rate']]
|
||||
The category.
|
||||
maturity : Union[float, str, List[Union[float, str]]]
|
||||
Maturities in years. Multiple items allowed for provider(s): fred.
|
||||
category : Union[str, List[str]]
|
||||
Rate category. Options: spot_rate, par_yield. Multiple items allowed for provider(s): fred.
|
||||
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
|
||||
@@ -495,5 +500,12 @@ class ROUTER_fixedincome_corporate(Container):
|
||||
"category": category,
|
||||
},
|
||||
extra_params=kwargs,
|
||||
extra_info={
|
||||
"maturity": {"multiple_items_allowed": ["fred"]},
|
||||
"category": {
|
||||
"choices": ["par_yield", "spot_rate"],
|
||||
"multiple_items_allowed": ["fred"],
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ class ROUTER_news(Container):
|
||||
def company(
|
||||
self,
|
||||
symbol: Annotated[
|
||||
Union[str, None, List[str]],
|
||||
Union[str, None, List[Optional[str]]],
|
||||
OpenBBCustomParameter(
|
||||
description="Symbol to get data for. This endpoint will accept multiple symbols separated by commas. Multiple items allowed for provider(s): benzinga, fmp, intrinio, polygon, tiingo, yfinance."
|
||||
),
|
||||
@@ -56,7 +56,7 @@ class ROUTER_news(Container):
|
||||
|
||||
Parameters
|
||||
----------
|
||||
symbol : Union[str, None, List[str]]
|
||||
symbol : Union[str, None, List[Optional[str]]]
|
||||
Symbol to get data for. This endpoint will accept multiple symbols separated by commas. Multiple items allowed for provider(s): benzinga, fmp, intrinio, polygon, tiingo, yfinance.
|
||||
start_date : Union[datetime.date, None, str]
|
||||
Start date of the data, in YYYY-MM-DD format.
|
||||
|
||||
@@ -8,13 +8,18 @@ from openbb_core.provider.standard_models.spot import (
|
||||
SpotRateQueryParams,
|
||||
)
|
||||
from openbb_fred.utils.fred_base import Fred
|
||||
from openbb_fred.utils.fred_helpers import get_spot_series_id
|
||||
from openbb_fred.utils.fred_helpers import comma_to_float_list, get_spot_series_id
|
||||
from pydantic import field_validator
|
||||
|
||||
|
||||
class FREDSpotRateQueryParams(SpotRateQueryParams):
|
||||
"""FRED Spot Rate Query."""
|
||||
|
||||
__json_schema_extra__ = {
|
||||
"maturity": ["multiple_items_allowed"],
|
||||
"category": ["multiple_items_allowed"],
|
||||
}
|
||||
|
||||
|
||||
class FREDSpotRateData(SpotRateData):
|
||||
"""FRED Spot Rate Data."""
|
||||
@@ -56,9 +61,17 @@ class FREDSpotRateFetcher(
|
||||
key = credentials.get("fred_api_key") if credentials else ""
|
||||
fred = Fred(key)
|
||||
|
||||
maturity = (
|
||||
comma_to_float_list(query.maturity)
|
||||
if isinstance(query.maturity, str)
|
||||
else [query.maturity]
|
||||
)
|
||||
if any(1 > m > 100 for m in maturity):
|
||||
raise ValueError("Maturity must be between 1 and 100")
|
||||
|
||||
series = get_spot_series_id(
|
||||
maturity=query.maturity,
|
||||
category=query.category,
|
||||
maturity=maturity,
|
||||
category=query.category.split(","),
|
||||
)
|
||||
|
||||
data = []
|
||||
|
||||
@@ -51,6 +51,16 @@ YIELD_CURVE_SERIES_CORPORATE_PAR = {
|
||||
}
|
||||
|
||||
|
||||
def comma_to_float_list(v: str) -> List[float]:
|
||||
"""Convert comma-separated string to list of floats."""
|
||||
try:
|
||||
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"
|
||||
) from e
|
||||
|
||||
|
||||
def all_cpi_options(harmonized: bool = False) -> List[dict]:
|
||||
"""Get all CPI options."""
|
||||
data = []
|
||||
@@ -136,6 +146,7 @@ def get_ice_bofa_series_id(
|
||||
units = "index" if type_ == "total_return" else "percent"
|
||||
|
||||
for s in series:
|
||||
# pylint: disable=too-many-boolean-expressions
|
||||
if (
|
||||
s["Type"] == type_
|
||||
and s["Units"] == units
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Intrinio Balance Sheet Model."""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
@@ -482,7 +483,7 @@ class IntrinioBalanceSheetFetcher(
|
||||
for sub_item in item["financials"]:
|
||||
field_name = sub_item["data_tag"]["tag"]
|
||||
unit = sub_item["data_tag"].get("unit", "")
|
||||
if unit and "share" not in unit:
|
||||
if unit and len(unit) == 3:
|
||||
units.append(unit)
|
||||
sub_dict[field_name] = (
|
||||
float(sub_item["value"])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Intrinio Cash Flow Statement Model."""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
@@ -89,6 +90,12 @@ class IntrinioCashFlowStatementData(CashFlowStatementData):
|
||||
description="The currency in which the balance sheet is reported.",
|
||||
default=None,
|
||||
)
|
||||
net_income_continuing_operations: Optional[float] = Field(
|
||||
default=None, description="Net Income (Continuing Operations)"
|
||||
)
|
||||
net_income_discontinued_operations: Optional[float] = Field(
|
||||
default=None, description="Net Income (Discontinued Operations)"
|
||||
)
|
||||
net_income: Optional[float] = Field(
|
||||
default=None, description="Consolidated Net Income."
|
||||
)
|
||||
@@ -119,12 +126,6 @@ class IntrinioCashFlowStatementData(CashFlowStatementData):
|
||||
net_cash_from_discontinued_operating_activities: Optional[float] = Field(
|
||||
default=None, description="Net Cash from Discontinued Operating Activities"
|
||||
)
|
||||
net_income_continuing_operations: Optional[float] = Field(
|
||||
default=None, description="Net Income (Continuing Operations)"
|
||||
)
|
||||
net_income_discontinued_operations: Optional[float] = Field(
|
||||
default=None, description="Net Income (Discontinued Operations)"
|
||||
)
|
||||
net_cash_from_operating_activities: Optional[float] = Field(
|
||||
default=None, description="Net Cash from Operating Activities"
|
||||
)
|
||||
@@ -220,7 +221,7 @@ class IntrinioCashFlowStatementData(CashFlowStatementData):
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def replace_zero(cls, values): # pylint: disable=no-self-argument
|
||||
def replace_zero(cls, values):
|
||||
"""Check for zero values and replace with None."""
|
||||
return (
|
||||
{k: None if v == 0 else v for k, v in values.items()}
|
||||
@@ -282,10 +283,10 @@ class IntrinioCashFlowStatementFetcher(
|
||||
"""Return the response."""
|
||||
statement_data = await response.json()
|
||||
return {
|
||||
"period_ending": statement_data["fundamental"]["end_date"],
|
||||
"fiscal_period": statement_data["fundamental"]["fiscal_period"],
|
||||
"fiscal_year": statement_data["fundamental"]["fiscal_year"],
|
||||
"financials": statement_data["standardized_financials"],
|
||||
"period_ending": statement_data["fundamental"]["end_date"], # type: ignore
|
||||
"fiscal_period": statement_data["fundamental"]["fiscal_period"], # type: ignore
|
||||
"fiscal_year": statement_data["fundamental"]["fiscal_year"], # type: ignore
|
||||
"financials": statement_data["standardized_financials"], # type: ignore
|
||||
}
|
||||
|
||||
intrinio_id = f"{query.symbol}-{statement_code}"
|
||||
@@ -308,7 +309,7 @@ class IntrinioCashFlowStatementFetcher(
|
||||
|
||||
for sub_item in item["financials"]:
|
||||
unit = sub_item["data_tag"].get("unit", "")
|
||||
if unit and "share" not in unit:
|
||||
if unit and len(unit) == 3:
|
||||
units.append(unit)
|
||||
field_name = sub_item["data_tag"]["tag"]
|
||||
sub_dict[field_name] = (
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Intrinio Equity Historical Price Model."""
|
||||
|
||||
# pylint: disable = unused-argument
|
||||
|
||||
from datetime import datetime, time
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
@@ -221,7 +223,6 @@ class IntrinioEquityHistoricalFetcher(
|
||||
|
||||
return await amake_requests([url], callback, **kwargs)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
@staticmethod
|
||||
def transform_data(
|
||||
query: IntrinioEquityHistoricalQueryParams,
|
||||
@@ -229,7 +230,10 @@ class IntrinioEquityHistoricalFetcher(
|
||||
**kwargs: Any,
|
||||
) -> List[IntrinioEquityHistoricalData]:
|
||||
"""Return the transformed data."""
|
||||
return [
|
||||
IntrinioEquityHistoricalData.model_validate(d)
|
||||
for d in sorted(data, key=lambda x: x["date"], reverse=False)
|
||||
]
|
||||
try:
|
||||
sorted_data = sorted(
|
||||
data, key=lambda x: x.get("date") or x.get("time"), reverse=False
|
||||
)
|
||||
except Exception:
|
||||
sorted_data = data
|
||||
return [IntrinioEquityHistoricalData.model_validate(d) for d in sorted_data]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Intrinio Income Statement Model."""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
@@ -472,7 +473,7 @@ class IntrinioIncomeStatementFetcher(
|
||||
|
||||
for sub_item in item["financials"]:
|
||||
unit = sub_item["data_tag"].get("unit", "")
|
||||
if unit and "share" not in unit:
|
||||
if unit and len(unit) == 3:
|
||||
units.append(unit)
|
||||
field_name = sub_item["data_tag"]["tag"]
|
||||
sub_dict[field_name] = (
|
||||
|
||||
@@ -4,7 +4,7 @@ import html
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from datetime import datetime, timedelta
|
||||
from itertools import repeat
|
||||
from typing import Any, Dict, List, Optional, Set, Union
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import requests
|
||||
from openbb_core.provider.abstract.fetcher import Fetcher
|
||||
@@ -24,18 +24,16 @@ class NasdaqEconomicCalendarQueryParams(EconomicCalendarQueryParams):
|
||||
|
||||
__json_schema_extra__ = {"country": ["multiple_items_allowed"]}
|
||||
|
||||
country: Optional[Union[str, List[str]]] = Field(
|
||||
country: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Country of the event",
|
||||
)
|
||||
|
||||
@field_validator("country", mode="before", check_fields=False)
|
||||
@classmethod
|
||||
def validate_country(cls, v: Union[str, List[str], Set[str]]):
|
||||
"""Validate the country input."""
|
||||
if isinstance(v, str):
|
||||
return v.lower().replace(" ", "_")
|
||||
return ",".join([country.lower().replace(" ", "_") for country in list(v)])
|
||||
def validate_country(cls, c: str): # pylint: disable=E0213
|
||||
"""Validate country."""
|
||||
return ",".join([v.lower() for v in c.replace(" ", "_").split(",")])
|
||||
|
||||
|
||||
class NasdaqEconomicCalendarData(EconomicCalendarData):
|
||||
|
||||
Reference in New Issue
Block a user