diff --git a/openbb_platform/core/openbb_core/provider/standard_models/crypto_historical.py b/openbb_platform/core/openbb_core/provider/standard_models/crypto_historical.py index 0df4ea0f91c..ae2e991825d 100644 --- a/openbb_platform/core/openbb_core/provider/standard_models/crypto_historical.py +++ b/openbb_platform/core/openbb_core/provider/standard_models/crypto_historical.py @@ -13,7 +13,7 @@ from openbb_core.provider.utils.descriptions import ( DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS, ) -from pydantic import Field, PositiveFloat, field_validator +from pydantic import Field, field_validator class CryptoHistoricalQueryParams(QueryParams): @@ -47,12 +47,14 @@ class CryptoHistoricalData(Data): date: Union[dateType, datetime] = Field( description=DATA_DESCRIPTIONS.get("date", "") ) - open: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("open", "")) - high: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("high", "")) - low: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("low", "")) - close: PositiveFloat = Field(description=DATA_DESCRIPTIONS.get("close", "")) - volume: float = Field(description=DATA_DESCRIPTIONS.get("volume", "")) - vwap: Optional[PositiveFloat] = Field( + open: float = Field(description=DATA_DESCRIPTIONS.get("open", "")) + high: float = Field(description=DATA_DESCRIPTIONS.get("high", "")) + low: float = Field(description=DATA_DESCRIPTIONS.get("low", "")) + close: float = Field(description=DATA_DESCRIPTIONS.get("close", "")) + volume: Optional[float] = Field( + default=None, description=DATA_DESCRIPTIONS.get("volume", "") + ) + vwap: Optional[float] = Field( default=None, description=DATA_DESCRIPTIONS.get("vwap", "") ) diff --git a/openbb_platform/extensions/econometrics/openbb_econometrics/utils.py b/openbb_platform/extensions/econometrics/openbb_econometrics/utils.py index 5ea40f6d737..ff2211a3dbe 100644 --- a/openbb_platform/extensions/econometrics/openbb_econometrics/utils.py +++ b/openbb_platform/extensions/econometrics/openbb_econometrics/utils.py @@ -79,7 +79,7 @@ def get_engle_granger_two_step_cointegration_test( short_run_ols = sm.OLS(dependent_series.diff().iloc[1:], (z.shift().iloc[1:])) short_run_ols_fit = short_run_ols.fit() - alpha = short_run_ols_fit.params[0] + alpha = short_run_ols_fit.params.iloc[0] # NOTE: The p-value returned by the adfuller function assumes we do not estimate z # first, but test stationarity of an unestimated series directly. This assumption diff --git a/openbb_platform/extensions/equity/integration/test_equity_python.py b/openbb_platform/extensions/equity/integration/test_equity_python.py index 84312ce6f90..d0476beb1f6 100644 --- a/openbb_platform/extensions/equity/integration/test_equity_python.py +++ b/openbb_platform/extensions/equity/integration/test_equity_python.py @@ -2108,7 +2108,7 @@ def test_equity_fundamental_management_discussion_analysis(params, obb): @pytest.mark.integration def test_equity_calendar_events(params, obb): """Test the equity calendar events endpoint.""" - result = obb.equity.calendar_events(**params) + result = obb.equity.calendar.events(**params) assert result assert isinstance(result, OBBject) assert len(result.results) > 0 diff --git a/openbb_platform/providers/fmp/openbb_fmp/models/crypto_historical.py b/openbb_platform/providers/fmp/openbb_fmp/models/crypto_historical.py index 328c5e17731..dbae7859a42 100644 --- a/openbb_platform/providers/fmp/openbb_fmp/models/crypto_historical.py +++ b/openbb_platform/providers/fmp/openbb_fmp/models/crypto_historical.py @@ -2,10 +2,8 @@ # pylint: disable=unused-argument -import asyncio from datetime import datetime -from typing import Any, Dict, List, Literal, Optional -from warnings import warn +from typing import Any, Literal, Optional from dateutil.relativedelta import relativedelta from openbb_core.provider.abstract.fetcher import Fetcher @@ -17,12 +15,6 @@ from openbb_core.provider.utils.descriptions import ( DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS, ) -from openbb_core.provider.utils.errors import EmptyDataError -from openbb_core.provider.utils.helpers import ( - amake_request, - get_querystring, -) -from openbb_fmp.utils.helpers import get_interval from pydantic import Field @@ -69,13 +61,13 @@ class FMPCryptoHistoricalData(CryptoHistoricalData): class FMPCryptoHistoricalFetcher( Fetcher[ FMPCryptoHistoricalQueryParams, - List[FMPCryptoHistoricalData], + list[FMPCryptoHistoricalData], ] ): """Transform the query, extract and transform the data from the FMP endpoints.""" @staticmethod - def transform_query(params: Dict[str, Any]) -> FMPCryptoHistoricalQueryParams: + def transform_query(params: dict[str, Any]) -> FMPCryptoHistoricalQueryParams: """Transform the query params. Start and end dates are set to 1 year interval.""" transformed_params = params @@ -91,10 +83,20 @@ class FMPCryptoHistoricalFetcher( @staticmethod async def aextract_data( query: FMPCryptoHistoricalQueryParams, - credentials: Optional[Dict[str, str]], + credentials: Optional[dict[str, str]], **kwargs: Any, - ) -> List[Dict]: + ) -> list[dict]: """Return the raw data from the FMP endpoint.""" + # pylint: disable=import-outside-toplevel + import asyncio # noqa + from openbb_core.provider.utils.errors import EmptyDataError + from openbb_core.provider.utils.helpers import ( + amake_request, + get_querystring, + ) + from openbb_fmp.utils.helpers import get_interval, response_callback + from warnings import warn + api_key = credentials.get("fmp_api_key") if credentials else "" interval = get_interval(query.interval) @@ -111,17 +113,16 @@ class FMPCryptoHistoricalFetcher( symbols = query.symbol.split(",") - results = [] - messages = [] + results: list = [] + messages: list = [] async def get_one(symbol): """Get data for one symbol.""" url = get_url_params(symbol) + data: list = [] - data = [] - - response = await amake_request(url, **kwargs) + response = await amake_request(url, response_callback=response_callback) if isinstance(response, dict) and response.get("Error Message"): message = f"Error fetching data for {symbol}: {response.get('Error Message', '')}" @@ -161,13 +162,13 @@ class FMPCryptoHistoricalFetcher( @staticmethod def transform_data( - query: FMPCryptoHistoricalQueryParams, data: List[Dict], **kwargs: Any - ) -> List[FMPCryptoHistoricalData]: + query: FMPCryptoHistoricalQueryParams, data: list[dict], **kwargs: Any + ) -> list[FMPCryptoHistoricalData]: """Return the transformed data.""" # Get rid of duplicate fields. to_pop = ["label", "changePercent", "unadjustedVolume"] - results: List[FMPCryptoHistoricalData] = [] + results: list[FMPCryptoHistoricalData] = [] for d in sorted( data, @@ -179,8 +180,8 @@ class FMPCryptoHistoricalFetcher( reverse=False, ): _ = [d.pop(pop) for pop in to_pop if pop in d] - if d.get("unadjusted_volume") == d.get("volume"): - _ = d.pop("unadjusted_volume") + if d.get("unadjusted_volume") and d["adjusted_volume"] == d.get("volume"): + _ = d.pop("unadjusted_volume", None) results.append(FMPCryptoHistoricalData.model_validate(d)) return results diff --git a/openbb_platform/providers/intrinio/openbb_intrinio/models/forward_pe_estimates.py b/openbb_platform/providers/intrinio/openbb_intrinio/models/forward_pe_estimates.py index d56e5c45def..646c6a308f7 100644 --- a/openbb_platform/providers/intrinio/openbb_intrinio/models/forward_pe_estimates.py +++ b/openbb_platform/providers/intrinio/openbb_intrinio/models/forward_pe_estimates.py @@ -2,10 +2,8 @@ # pylint: disable=unused-argument -import asyncio from datetime import date as dateType -from typing import Any, Dict, List, Optional -from warnings import warn +from typing import Any, Optional from openbb_core.app.model.abstract.error import OpenBBError from openbb_core.provider.abstract.fetcher import Fetcher @@ -13,9 +11,6 @@ from openbb_core.provider.standard_models.forward_pe_estimates import ( ForwardPeEstimatesData, ForwardPeEstimatesQueryParams, ) -from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError -from openbb_core.provider.utils.helpers import amake_request -from openbb_intrinio.utils.helpers import response_callback from pydantic import Field @@ -59,13 +54,13 @@ class IntrinioForwardPeEstimatesData(ForwardPeEstimatesData): class IntrinioForwardPeEstimatesFetcher( - Fetcher[IntrinioForwardPeEstimatesQueryParams, List[IntrinioForwardPeEstimatesData]] + Fetcher[IntrinioForwardPeEstimatesQueryParams, list[IntrinioForwardPeEstimatesData]] ): """Intrinio Forward PE Estimates Fetcher.""" @staticmethod def transform_query( - params: Dict[str, Any] + params: dict[str, Any], ) -> IntrinioForwardPeEstimatesQueryParams: """Transform the query params.""" return IntrinioForwardPeEstimatesQueryParams(**params) @@ -73,14 +68,20 @@ class IntrinioForwardPeEstimatesFetcher( @staticmethod async def aextract_data( query: IntrinioForwardPeEstimatesQueryParams, - credentials: Optional[Dict[str, str]], + credentials: Optional[dict[str, str]], **kwargs: Any, - ) -> List[Dict]: + ) -> list[dict]: """Return the raw data from the Intrinio endpoint.""" + # pylint: disable=import-outside-toplevel + import asyncio # noqa + from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError + from openbb_core.provider.utils.helpers import amake_request + from openbb_intrinio.utils.helpers import response_callback + api_key = credentials.get("intrinio_api_key") if credentials else "" BASE_URL = "https://api-v2.intrinio.com/zacks/forward_pe" symbols = query.symbol.split(",") if query.symbol else None - results: List[Dict] = [] + results: list[dict] = [] async def get_one(symbol): """Get the data for one symbol.""" @@ -90,33 +91,51 @@ class IntrinioForwardPeEstimatesFetcher( url, response_callback=response_callback, **kwargs ) except Exception as e: - warn(f"Symbol Error: {symbol} --> {e}") - else: - if data: - results.append(data) # type: ignore + raise OpenBBError(e) from e + + if data: + results.append(data) # type: ignore if symbols: - await asyncio.gather(*[get_one(symbol) for symbol in symbols]) - if not results: - raise EmptyDataError( - f"There were no results found for any of the given symbols. -> {symbols}" + try: + gather_results = await asyncio.gather( + *[get_one(symbol) for symbol in symbols], return_exceptions=True ) - return results + + for result in gather_results: + if isinstance(result, UnauthorizedError): + raise result + if isinstance(result, OpenBBError): + raise result + + if not results: + raise EmptyDataError( + f"There were no results found for any of the given symbols. -> {symbols}" + ) + return results + except Exception as e: + raise OpenBBError( + f"Error in Intrinio request -> {e} -> {symbols}" + ) from e async def fetch_callback(response, session): """Use callback for pagination.""" data = await response.json() error = data.get("error", None) + if error: message = data.get("message", "") - if "api key" in message.lower(): + if "api key" in message.lower() or "view this data" in error.lower(): raise UnauthorizedError( - f"Unauthorized Intrinio request -> {message}" + f"Unauthorized Intrinio request -> {message} -> {error}" ) raise OpenBBError(f"Error: {error} -> {message}") + forward_pe = data.get("forward_pe") + if forward_pe and len(forward_pe) > 0: # type: ignore results.extend(forward_pe) # type: ignore + return results url = f"{BASE_URL}?page_size=10000&api_key={api_key}" @@ -130,9 +149,9 @@ class IntrinioForwardPeEstimatesFetcher( @staticmethod def transform_data( query: IntrinioForwardPeEstimatesQueryParams, - data: List[Dict], + data: list[dict], **kwargs: Any, - ) -> List[IntrinioForwardPeEstimatesData]: + ) -> list[IntrinioForwardPeEstimatesData]: """Transform the raw data into the standard format.""" symbols = query.symbol.split(",") if query.symbol else [] if symbols: diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py b/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py index 4f315297893..97cc9d18bc0 100644 --- a/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py +++ b/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py @@ -3,8 +3,7 @@ # pylint: disable=unused-argument from datetime import datetime -from typing import Any, Dict, List, Literal, Optional -from warnings import warn +from typing import Any, Literal, Optional from openbb_core.provider.abstract.fetcher import Fetcher from openbb_core.provider.standard_models.index_historical import ( @@ -95,13 +94,13 @@ class PolygonIndexHistoricalData(IndexHistoricalData): class PolygonIndexHistoricalFetcher( Fetcher[ PolygonIndexHistoricalQueryParams, - List[PolygonIndexHistoricalData], + list[PolygonIndexHistoricalData], ] ): """Polygon Index Historical Fetcher.""" @staticmethod - def transform_query(params: Dict[str, Any]) -> PolygonIndexHistoricalQueryParams: + def transform_query(params: dict[str, Any]) -> PolygonIndexHistoricalQueryParams: """Transform the query params.""" # pylint: disable=import-outside-toplevel from dateutil.relativedelta import relativedelta @@ -119,18 +118,21 @@ class PolygonIndexHistoricalFetcher( @staticmethod async def aextract_data( query: PolygonIndexHistoricalQueryParams, - credentials: Optional[Dict[str, str]], + credentials: Optional[dict[str, str]], **kwargs: Any, - ) -> List[Dict]: + ) -> list[dict]: """Extract raw data from the Polygon endpoint.""" # pylint: disable=import-outside-toplevel - from openbb_core.provider.utils.helpers import ( # noqa + from aiohttp import ClientResponseError # noqa + from openbb_core.provider.utils.errors import OpenBBError, UnauthorizedError + from openbb_core.provider.utils.helpers import ( ClientResponse, ClientSession, amake_requests, safe_fromtimestamp, ) - from pytz import timezone # noqa + from pytz import timezone + from warnings import warn api_key = credentials.get("polygon_api_key") if credentials else "" @@ -146,9 +148,12 @@ class PolygonIndexHistoricalFetcher( async def callback( response: ClientResponse, session: ClientSession - ) -> List[Dict]: + ) -> list[dict]: data = await response.json() + if isinstance(data, dict) and data.get("status") == "NOT_AUTHORIZED": + raise UnauthorizedError(response.get("message", str(response))) + symbol = response.url.parts[4] next_url = data.get("next_url", None) # type: ignore[union-attr] results: list = data.get("results", []) # type: ignore[union-attr] @@ -178,14 +183,24 @@ class PolygonIndexHistoricalFetcher( return results - return await amake_requests(urls, callback, **kwargs) # type: ignore + try: + return await amake_requests(urls, callback, raise_for_status=True) # type: ignore + + except ClientResponseError as e: + if e.status == 403: + raise UnauthorizedError( + f"Unauthorized Polygon request -> {e.message} -> {e.request_info.url}" + ) from e + raise OpenBBError( + f"Error in Polygon request -> {e} -> {e.request_info.url}" + ) from e @staticmethod def transform_data( query: PolygonIndexHistoricalQueryParams, - data: List[Dict], + data: list[dict], **kwargs: Any, - ) -> List[PolygonIndexHistoricalData]: + ) -> list[PolygonIndexHistoricalData]: """Transform the data.""" if not data: raise EmptyDataError() diff --git a/openbb_platform/providers/polygon/openbb_polygon/utils/helpers.py b/openbb_platform/providers/polygon/openbb_polygon/utils/helpers.py index fac1276e977..82320d637d3 100644 --- a/openbb_platform/providers/polygon/openbb_polygon/utils/helpers.py +++ b/openbb_platform/providers/polygon/openbb_polygon/utils/helpers.py @@ -1,72 +1,31 @@ """Polygon Helpers Module.""" -import json -from io import StringIO -from typing import Any, Dict, List, Optional, Tuple, TypeVar, Union +from typing import Any, Optional, Tuple, Union from openbb_core.app.model.abstract.error import OpenBBError -from openbb_core.provider.utils.errors import EmptyDataError +from openbb_core.provider.utils.errors import EmptyDataError, UnauthorizedError from openbb_core.provider.utils.helpers import ( ClientResponse, ClientSession, amake_request, ) -from pydantic import BaseModel - -T = TypeVar("T", bound=BaseModel) - - -class BasicResponse: - """Basic Response class.""" - - def __init__(self, response: StringIO): - """Initialize the BasicResponse class.""" - # Find a way to get the status code - self.status_code = 200 - response.seek(0) - self.text = response.read() - - def json(self) -> dict: - """Return the response as a dictionary.""" - return json.loads(self.text) - - -def request(url: str) -> BasicResponse: - """Request function for PyScript. - - Pass in Method and make sure to await. - - Parameters - ---------- - url: str - URL to make request to - - Return - ------ - response: BasicRequest - BasicRequest object with status_code and text attributes - """ - # pylint: disable=import-outside-toplevel - from pyodide.http import open_url # type: ignore - - response = open_url(url) - return BasicResponse(response) async def response_callback( response: ClientResponse, _: ClientSession -) -> Union[dict, List[dict]]: +) -> Union[dict, list]: """Use callback for make_request.""" - data: Dict = await response.json() # type: ignore + data = await response.json() # type: ignore if response.status != 200: message = data.get("error") or data.get("message") raise OpenBBError(f"Error in Polygon request -> {message}") - keys_in_data = "results" in data or "tickers" in data + if isinstance(data, dict) and data.get("status") == "NOT_AUTHORIZED": + raise UnauthorizedError(response.get("message", str(response))) - if not keys_in_data or len(data) == 0: - raise EmptyDataError() + if not data: + raise EmptyDataError("No data was returned from the Polygon endpoint") return data @@ -78,7 +37,7 @@ async def get_data(url: str, **kwargs: Any) -> Union[list, dict]: async def get_data_many( url: str, sub_dict: Optional[str] = None, **kwargs: Any -) -> List[dict]: +) -> list[dict]: """Get data from Polygon endpoint and convert to list of schemas. Parameters