Merge remote-tracking branch 'OpenBB-finance/develop' into feature/argparse_translator

This commit is contained in:
hjoaquim
2024-03-07 07:48:57 +00:00
27 changed files with 284 additions and 110 deletions

View File

@@ -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(

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]:

View File

@@ -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", "")

View File

@@ -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."""

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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",
},

View File

@@ -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
------

View File

@@ -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())

View File

@@ -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

View File

@@ -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"],

View File

@@ -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.

View File

@@ -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]

View File

@@ -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"],
},
},
)
)

View File

@@ -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.

View File

@@ -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 = []

View File

@@ -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

View File

@@ -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"])

View File

@@ -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] = (

View File

@@ -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]

View File

@@ -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] = (

View File

@@ -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):