From 5ff4d8907ce2fd0c2bd59fd1c6a0daae4656c43b Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Sat, 25 May 2024 04:28:45 -0700 Subject: [PATCH] [BugFix] Fix Seeking Alpha (#6461) * fix seeking alpha * try again sequence * prevent key error * move value check up the sequence * filter test cassette more --------- Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com> --- .../standard_models/upcoming_release_days.py | 23 - .../equity/integration/test_equity_api.py | 42 +- .../equity/integration/test_equity_python.py | 39 +- .../discovery/discovery_router.py | 14 - .../openbb_seeking_alpha/__init__.py | 12 +- .../models/calendar_earnings.py | 144 +++++++ .../models/forward_eps_estimates.py | 207 +++++++++ .../models/forward_sales_estimates.py | 179 ++++++++ .../models/upcoming_release_days.py | 108 ----- .../openbb_seeking_alpha/utils/helpers.py | 40 ++ .../test_sa_calendar_earnings_fetcher.yaml | 400 ++++++++++++++++++ .../test_sa_forward_eps_estimates.yaml | 223 ++++++++++ .../test_sa_forward_sales_estimates.yaml | 208 +++++++++ ...test_sa_upcoming_release_days_fetcher.yaml | 249 ----------- .../tests/test_seeking_alpha_fetchers.py | 40 +- 15 files changed, 1490 insertions(+), 438 deletions(-) delete mode 100644 openbb_platform/core/openbb_core/provider/standard_models/upcoming_release_days.py create mode 100644 openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/calendar_earnings.py create mode 100644 openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_eps_estimates.py create mode 100644 openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py delete mode 100644 openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/upcoming_release_days.py create mode 100644 openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/utils/helpers.py create mode 100644 openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_calendar_earnings_fetcher.yaml create mode 100644 openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_eps_estimates.yaml create mode 100644 openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_sales_estimates.yaml delete mode 100644 openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_upcoming_release_days_fetcher.yaml diff --git a/openbb_platform/core/openbb_core/provider/standard_models/upcoming_release_days.py b/openbb_platform/core/openbb_core/provider/standard_models/upcoming_release_days.py deleted file mode 100644 index 34560c90e5d..00000000000 --- a/openbb_platform/core/openbb_core/provider/standard_models/upcoming_release_days.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Upcoming Release Days Standard Model.""" - -from datetime import date as dateType - -from pydantic import Field - -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 - - -class UpcomingReleaseDaysQueryParams(QueryParams): - """Upcoming Release Days Query.""" - - -class UpcomingReleaseDaysData(Data): - """Upcoming Release Days Data.""" - - symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", "")) - name: str = Field(description="The full name of the asset.") - exchange: str = Field(description="The exchange the asset is traded on.") - release_time_type: str = Field(description="The type of release time.") - release_date: dateType = Field(description="The date of the release.") diff --git a/openbb_platform/extensions/equity/integration/test_equity_api.py b/openbb_platform/extensions/equity/integration/test_equity_api.py index f47350f300c..ffe57cda534 100644 --- a/openbb_platform/extensions/equity/integration/test_equity_api.py +++ b/openbb_platform/extensions/equity/integration/test_equity_api.py @@ -144,6 +144,14 @@ def test_equity_calendar_splits(params, headers): ({"start_date": "2023-11-09", "end_date": "2023-11-10", "provider": "fmp"}), ({"start_date": "2023-11-09", "end_date": "2023-11-10", "provider": "nasdaq"}), ({"start_date": "2023-11-09", "end_date": "2023-11-10", "provider": "tmx"}), + ( + { + "start_date": None, + "end_date": None, + "provider": "seeking_alpha", + "country": "us", + } + ), ], ) @pytest.mark.integration @@ -397,7 +405,14 @@ def test_equity_estimates_historical(params, headers): "calendar_period": None, "provider": "intrinio", } - ) + ), + ( + { + "symbol": "AAPL,BAM:CA", + "period": "annual", + "provider": "seeking_alpha", + } + ), ], ) @pytest.mark.integration @@ -434,6 +449,13 @@ def test_equity_estimates_forward_sales(params, headers): "provider": "fmp", } ), + ( + { + "symbol": "AAPL,BAM:CA", + "period": "annual", + "provider": "seeking_alpha", + } + ), ], ) @pytest.mark.integration @@ -1652,24 +1674,6 @@ def test_equity_discovery_top_retail(params, headers): assert result.status_code == 200 -@parametrize( - "params", - [({"provider": "seeking_alpha"})], -) -@pytest.mark.integration -def test_equity_discovery_upcoming_release_days(params, headers): - """Test the equity discovery upcoming release days endpoint.""" - params = {p: v for p, v in params.items() if v} - - query_str = get_querystring(params, []) - url = ( - f"http://0.0.0.0:8000/api/v1/equity/discovery/upcoming_release_days?{query_str}" - ) - result = requests.get(url, headers=headers, timeout=30) - assert isinstance(result, requests.Response) - assert result.status_code == 200 - - @parametrize( "params", [ diff --git a/openbb_platform/extensions/equity/integration/test_equity_python.py b/openbb_platform/extensions/equity/integration/test_equity_python.py index 66ed58e6fde..0e167cae8df 100644 --- a/openbb_platform/extensions/equity/integration/test_equity_python.py +++ b/openbb_platform/extensions/equity/integration/test_equity_python.py @@ -132,6 +132,14 @@ def test_equity_calendar_splits(params, obb): ({"start_date": "2023-11-09", "end_date": "2023-11-10", "provider": "fmp"}), ({"start_date": "2023-11-09", "end_date": "2023-11-10", "provider": "nasdaq"}), ({"start_date": "2023-11-09", "end_date": "2023-11-10", "provider": "tmx"}), + ( + { + "start_date": None, + "end_date": None, + "provider": "seeking_alpha", + "country": "us", + } + ), ], ) @pytest.mark.integration @@ -705,7 +713,14 @@ def test_equity_estimates_consensus(params, obb): "calendar_period": None, "provider": "intrinio", } - ) + ), + ( + { + "symbol": "AAPL,BAM:CA", + "period": "annual", + "provider": "seeking_alpha", + } + ), ], ) @pytest.mark.integration @@ -739,6 +754,13 @@ def test_equity_estimates_forward_sales(params, obb): "provider": "fmp", } ), + ( + { + "symbol": "AAPL,BAM:CA", + "period": "annual", + "provider": "seeking_alpha", + } + ), ], ) @pytest.mark.integration @@ -1570,21 +1592,6 @@ def test_equity_discovery_top_retail(params, obb): assert len(result.results) > 0 -@parametrize( - "params", - [({"provider": "seeking_alpha"})], -) -@pytest.mark.integration -def test_equity_discovery_upcoming_release_days(params, obb): - """Test the equity discovery upcoming release days endpoint.""" - params = {p: v for p, v in params.items() if v} - - result = obb.equity.discovery.upcoming_release_days(**params) - assert result - assert isinstance(result, OBBject) - assert len(result.results) > 0 - - @parametrize( "params", [ diff --git a/openbb_platform/extensions/equity/openbb_equity/discovery/discovery_router.py b/openbb_platform/extensions/equity/openbb_equity/discovery/discovery_router.py index b8fbbc1a7be..652227975a6 100644 --- a/openbb_platform/extensions/equity/openbb_equity/discovery/discovery_router.py +++ b/openbb_platform/extensions/equity/openbb_equity/discovery/discovery_router.py @@ -152,20 +152,6 @@ async def top_retail( return await OBBject.from_query(Query(**locals())) -@router.command( - model="UpcomingReleaseDays", - examples=[APIEx(parameters={"provider": "seeking_alpha"})], -) -async def upcoming_release_days( - cc: CommandContext, - provider_choices: ProviderChoices, - standard_params: StandardParams, - extra_params: ExtraParams, -) -> OBBject: - """Get upcoming earnings release dates.""" - return await OBBject.from_query(Query(**locals())) - - @router.command( model="DiscoveryFilings", examples=[ diff --git a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/__init__.py b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/__init__.py index e4022980de8..38b5757c7b1 100644 --- a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/__init__.py +++ b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/__init__.py @@ -1,8 +1,12 @@ """Seeking Alpha Provider module.""" from openbb_core.provider.abstract.provider import Provider -from openbb_seeking_alpha.models.upcoming_release_days import ( - SAUpcomingReleaseDaysFetcher, +from openbb_seeking_alpha.models.calendar_earnings import SACalendarEarningsFetcher +from openbb_seeking_alpha.models.forward_eps_estimates import ( + SAForwardEpsEstimatesFetcher, +) +from openbb_seeking_alpha.models.forward_sales_estimates import ( + SAForwardSalesEstimatesFetcher, ) seeking_alpha_provider = Provider( @@ -11,7 +15,9 @@ seeking_alpha_provider = Provider( description="""Seeking Alpha is a data provider with access to news, analysis, and real-time alerts on stocks.""", fetcher_dict={ - "UpcomingReleaseDays": SAUpcomingReleaseDaysFetcher, + "CalendarEarnings": SACalendarEarningsFetcher, + "ForwardEpsEstimates": SAForwardEpsEstimatesFetcher, + "ForwardSalesEstimates": SAForwardSalesEstimatesFetcher, }, repr_name="Seeking Alpha", ) diff --git a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/calendar_earnings.py b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/calendar_earnings.py new file mode 100644 index 00000000000..67b6ee13339 --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/calendar_earnings.py @@ -0,0 +1,144 @@ +"""Seeking Alpha Calendar Earnings Model.""" + +# pylint: disable=unused-argument + +import asyncio +import json +from datetime import datetime, timedelta +from typing import Any, Dict, List, Literal, Optional +from warnings import warn + +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.calendar_earnings import ( + CalendarEarningsData, + CalendarEarningsQueryParams, +) +from openbb_core.provider.utils.helpers import amake_request +from openbb_seeking_alpha.utils.helpers import HEADERS, date_range +from pydantic import Field, field_validator + + +class SACalendarEarningsQueryParams(CalendarEarningsQueryParams): + """Seeking Alpha Calendar Earnings Query. + + Source: https://seekingalpha.com/earnings/earnings-calendar + """ + + country: Literal["us", "ca"] = Field( + default="us", + description="The country to get calendar data for.", + json_schema_extra={"choices": ["us", "ca"]}, + ) + + +class SACalendarEarningsData(CalendarEarningsData): + """Seeking Alpha Calendar Earnings Data.""" + + market_cap: Optional[float] = Field( + default=None, + description="Market cap of the entity.", + ) + reporting_time: Optional[str] = Field( + default=None, + description="The reporting time - e.g. after market close.", + ) + exchange: Optional[str] = Field( + default=None, + description="The primary trading exchange.", + ) + sector_id: Optional[int] = Field( + default=None, + description="The Seeking Alpha Sector ID.", + ) + + @field_validator("report_date", mode="before", check_fields=False) + @classmethod + def validate_release_date(cls, v): + """Validate the release date.""" + v = v.split("T")[0] + return datetime.strptime(v, "%Y-%m-%d").date() + + +class SACalendarEarningsFetcher( + Fetcher[ + SACalendarEarningsQueryParams, + List[SACalendarEarningsData], + ] +): + """Seeking Alpha Calendar Earnings Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> SACalendarEarningsQueryParams: + """Transform the query.""" + now = datetime.today().date() + transformed_params = params + if not params.get("start_date"): + transformed_params["start_date"] = now + if not params.get("end_date"): + transformed_params["end_date"] = now + timedelta(days=3) + return SACalendarEarningsQueryParams(**transformed_params) + + @staticmethod + async def aextract_data( + query: SACalendarEarningsQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict]: + """Return the raw data from the Seeking Alpha endpoint.""" + results: List[Dict] = [] + dates = [ + date.strftime("%Y-%m-%d") + for date in date_range(query.start_date, query.end_date) + ] + currency = "USD" if query.country == "us" else "CAD" + messages: List = [] + + async def get_date(date, currency): + """Get date for one date.""" + url = ( + f"https://seekingalpha.com/api/v3/earnings_calendar/tickers?" + f"filter%5Bselected_date%5D={date}" + f"&filter%5Bwith_rating%5D=false&filter%5Bcurrency%5D={currency}" + ) + response = await amake_request(url=url, headers=HEADERS) + # Try again if the response is blocked. + if "blockScript" in response: + response = await amake_request(url=url, headers=HEADERS) + if "blockScript" in response: + message = json.dumps(response) + messages.append(message) + warn(message) + if "data" in response: + results.extend(response.get("data")) + + await asyncio.gather(*[get_date(date, currency) for date in dates]) + + if not results: + raise RuntimeError(f"Error with the Seeking Alpha request -> {messages}") + + return results + + @staticmethod + def transform_data( + query: SACalendarEarningsQueryParams, + data: List[Dict], + **kwargs: Any, + ) -> List[SACalendarEarningsData]: + """Transform the data to the standard format.""" + transformed_data: List[SACalendarEarningsData] = [] + for row in sorted(data, key=lambda x: x["attributes"]["release_date"]): + attributes = row.get("attributes", {}) + transformed_data.append( + SACalendarEarningsData.model_validate( + { + "report_date": attributes.get("release_date"), + "reporting_time": attributes.get("release_time"), + "symbol": attributes.get("slug"), + "name": attributes.get("name"), + "market_cap": attributes.get("marketcap"), + "exchange": attributes.get("exchange"), + "sector_id": attributes.get("sector_id"), + } + ) + ) + return transformed_data diff --git a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_eps_estimates.py b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_eps_estimates.py new file mode 100644 index 00000000000..fb0c4458eca --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_eps_estimates.py @@ -0,0 +1,207 @@ +"""Seeking Alpha Forward EPS Estimates Model.""" + +# pylint: disable=unused-argument + +from typing import Any, Dict, List, Literal, Optional +from warnings import warn + +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.forward_eps_estimates import ( + ForwardEpsEstimatesData, + ForwardEpsEstimatesQueryParams, +) +from openbb_core.provider.utils.helpers import amake_request +from openbb_seeking_alpha.utils.helpers import HEADERS, get_seekingalpha_id +from pydantic import Field, field_validator + + +class SAForwardEpsEstimatesQueryParams(ForwardEpsEstimatesQueryParams): + """Seeking Alpha Forward EPS Estimates Query. + + Source: https://seekingalpha.com/earnings/earnings-calendar + """ + + __json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}} + + period: Literal["annual", "quarter"] = Field( + default="quarter", + description="The reporting period.", + json_schema_extra={"choices": ["annual", "quarter"]}, + ) + + @field_validator("symbol", mode="before", check_fields=False) + @classmethod + def check_symbol(cls, value): + """Check the symbol.""" + if not value: + raise RuntimeError("Error: Symbol is a required field for Seeking Alpha.") + return value + + +class SAForwardEpsEstimatesData(ForwardEpsEstimatesData): + """Seeking Alpha Forward EPS Estimates Data.""" + + normalized_actual: Optional[float] = Field( + default=None, + description="Actual normalized EPS.", + ) + period_growth: Optional[float] = Field( + default=None, + description="Estimated (or actual if reported) EPS growth for the period.", + json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100}, + ) + low_estimate_gaap: Optional[float] = Field( + default=None, + description="Estimated GAAP EPS low for the period.", + ) + high_estimate_gaap: Optional[float] = Field( + default=None, + description="Estimated GAAP EPS high for the period.", + ) + mean_gaap: Optional[float] = Field( + default=None, + description="Estimated GAAP EPS mean for the period.", + ) + gaap_actual: Optional[float] = Field( + default=None, + description="Actual GAAP EPS.", + ) + + +class SAForwardEpsEstimatesFetcher( + Fetcher[ + SAForwardEpsEstimatesQueryParams, + List[SAForwardEpsEstimatesData], + ] +): + """Seeking Alpha Forward EPS Estimates Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> SAForwardEpsEstimatesQueryParams: + """Transform the query.""" + return SAForwardEpsEstimatesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: SAForwardEpsEstimatesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict: + """Return the raw data from the Seeking Alpha endpoint.""" + tickers = query.symbol.split(",") + fp = query.period if query.period == "annual" else "quarterly" + url = "https://seekingalpha.com/api/v3/symbol_data/estimates" + querystring: Dict = { + "estimates_data_items": "eps_normalized_actual,eps_normalized_consensus_low,eps_normalized_consensus_mean," + "eps_normalized_consensus_high,eps_normalized_num_of_estimates," + "eps_gaap_actual,eps_gaap_consensus_low,eps_gaap_consensus_mean,eps_gaap_consensus_high,", + "period_type": fp, + "relative_periods": "-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12", + } + ids: Dict = {ticker: await get_seekingalpha_id(ticker) for ticker in tickers} + querystring["ticker_ids"] = (",").join(list(ids.values())) + payload: str = "" + response = await amake_request( + url, data=payload, headers=HEADERS, params=querystring + ) + estimates: Dict = response.get("estimates", {}) # type: ignore + if not estimates: + raise RuntimeError(f"No estimates data was returned for: {query.symbol}") + + output: Dict = {"ids": ids, "estimates": estimates} + + return output + + @staticmethod + def transform_data( + query: SAForwardEpsEstimatesQueryParams, + data: Dict, + **kwargs: Any, + ) -> List[SAForwardEpsEstimatesData]: + """Transform the data to the standard format.""" + tickers = query.symbol.split(",") + ids = data.get("ids") + estimates = data.get("estimates") + results: List[SAForwardEpsEstimatesData] = [] + for ticker in tickers: + sa_id = str(ids.get(ticker, "")) + if sa_id == "" or sa_id not in estimates: + warn(f"Symbol Error: No data found for, {ticker}") + seek_object = estimates.get(sa_id) + items = len(seek_object["eps_normalized_num_of_estimates"]) + for i in range(0, items - 4): + eps_estimates: Dict = {} + eps_estimates["symbol"] = ticker + num_estimates = seek_object["eps_normalized_num_of_estimates"].get(str(i)) + if not num_estimates: + continue + period = num_estimates[0].get("period", {}) + if period: + period_type = period.get("periodtypeid") + eps_estimates["calendar_year"] = period.get("calendaryear") + eps_estimates["calendar_period"] = ( + "Q" + str(period.get("calendarquarter", "")) + if period_type == "quarterly" + else "FY" + ) + eps_estimates["date"] = period.get("periodenddate").split("T")[0] + eps_estimates["fiscal_year"] = period.get("fiscalyear") + eps_estimates["fiscal_period"] = ( + "Q" + str(period.get("fiscalquarter", "")) + if period_type == "quarterly" + else "FY" + ) + eps_estimates["number_of_analysts"] = num_estimates[0].get( + "dataitemvalue" + ) + actual = seek_object["eps_normalized_actual"].get(str(i)) + if actual: + eps_estimates["normalized_actual"] = actual[0].get("dataitemvalue") + gaap_actual = seek_object["eps_gaap_actual"].get(str(i)) + if gaap_actual: + eps_estimates["gaap_actual"] = gaap_actual[0].get("dataitemvalue") + low = seek_object["eps_normalized_consensus_low"].get(str(i)) + if low: + eps_estimates["low_estimate"] = low[0].get("dataitemvalue") + gaap_low = seek_object["eps_gaap_consensus_low"].get(str(i)) + if gaap_low: + eps_estimates["low_estimate_gaap"] = gaap_low[0].get( + "dataitemvalue" + ) + high = seek_object["eps_normalized_consensus_high"].get(str(i)) + if high: + eps_estimates["high_estimate"] = high[0].get("dataitemvalue") + gaap_high = seek_object["eps_gaap_consensus_high"].get(str(i)) + if gaap_high: + eps_estimates["high_estimate_gaap"] = gaap_high[0].get( + "dataitemvalue" + ) + mean = seek_object["eps_normalized_consensus_mean"].get(str(i)) + if mean: + mean = mean[0].get("dataitemvalue") + eps_estimates["mean"] = mean + gaap_mean = seek_object["eps_gaap_consensus_mean"].get(str(i)) + if gaap_mean: + eps_estimates["mean_gaap"] = gaap_mean[0].get("dataitemvalue") + # Calculate the estimated growth percent. + this = float(mean) if mean else None + prev = None + percent = None + try: + prev = float( + seek_object["eps_normalized_actual"][str(i - 1)][0].get( + "dataitemvalue" + ) + ) + except KeyError: + prev = float( + seek_object["eps_normalized_consensus_mean"][str(i - 1)][0].get( + "dataitemvalue" + ) + ) + if this and prev: + percent = (this - prev) / prev + eps_estimates["period_growth"] = percent + results.append(SAForwardEpsEstimatesData.model_validate(eps_estimates)) + + return results diff --git a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py new file mode 100644 index 00000000000..c20e371bac0 --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py @@ -0,0 +1,179 @@ +"""Seeking Alpha Forward Sales Estimates Model.""" + +# pylint: disable=unused-argument + +from typing import Any, Dict, List, Literal, Optional +from warnings import warn + +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.forward_sales_estimates import ( + ForwardSalesEstimatesData, + ForwardSalesEstimatesQueryParams, +) +from openbb_core.provider.utils.helpers import amake_request +from openbb_seeking_alpha.utils.helpers import HEADERS, get_seekingalpha_id +from pydantic import Field, field_validator + + +class SAForwardSalesEstimatesQueryParams(ForwardSalesEstimatesQueryParams): + """Seeking Alpha Forward Sales Estimates Query. + + Source: https://seekingalpha.com/earnings/earnings-calendar + """ + + __json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}} + + period: Literal["annual", "quarter"] = Field( + default="quarter", + description="The reporting period.", + json_schema_extra={"choices": ["annual", "quarter"]}, + ) + + @field_validator("symbol", mode="before", check_fields=False) + @classmethod + def check_symbol(cls, value): + """Check the symbol.""" + if not value: + raise RuntimeError("Error: Symbol is a required field for Seeking Alpha.") + return value + + +class SAForwardSalesEstimatesData(ForwardSalesEstimatesData): + """Seeking Alpha Forward Sales Estimates Data.""" + + actual: Optional[int] = Field( + default=None, + description="Actual sales (revenue) for the period.", + ) + period_growth: Optional[float] = Field( + default=None, + description="Estimated (or actual if reported) EPS growth for the period.", + json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100}, + ) + + +class SAForwardSalesEstimatesFetcher( + Fetcher[ + SAForwardSalesEstimatesQueryParams, + List[SAForwardSalesEstimatesData], + ] +): + """Seeking Alpha Forward Sales Estimates Fetcher.""" + + @staticmethod + def transform_query(params: Dict[str, Any]) -> SAForwardSalesEstimatesQueryParams: + """Transform the query.""" + return SAForwardSalesEstimatesQueryParams(**params) + + @staticmethod + async def aextract_data( + query: SAForwardSalesEstimatesQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> Dict: + """Return the raw data from the Seeking Alpha endpoint.""" + tickers = query.symbol.split(",") + fp = query.period if query.period == "annual" else "quarterly" + url = "https://seekingalpha.com/api/v3/symbol_data/estimates" + payload = "" + querystring = { + "estimates_data_items": "revenue_actual,revenue_consensus_low,revenue_consensus_mean," + "revenue_consensus_high,revenue_num_of_estimates", + "period_type": fp, + "relative_periods": "-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12", + } + ids = {ticker: await get_seekingalpha_id(ticker) for ticker in tickers} + querystring["ticker_ids"] = (",").join(list(ids.values())) + response = await amake_request( + url, data=payload, headers=HEADERS, params=querystring + ) + estimates = response.get("estimates", {}) # type: ignore + if not estimates: + raise RuntimeError(f"No estimates data was returned for: {query.symbol}") + output: Dict = {"ids": ids, "estimates": estimates} + + return output + + @staticmethod + def transform_data( + query: SAForwardSalesEstimatesQueryParams, + data: Dict, + **kwargs: Any, + ) -> List[SAForwardSalesEstimatesData]: + """Transform the data to the standard format.""" + tickers = query.symbol.split(",") + ids = data.get("ids") + estimates = data.get("estimates") + results: List[SAForwardSalesEstimatesData] = [] + for ticker in tickers: + sa_id = str(ids.get(ticker, "")) + if sa_id == "" or sa_id not in estimates: + warn(f"Symbol Error: No data found for, {ticker}") + seek_object = estimates.get(sa_id) + items = len(seek_object["revenue_num_of_estimates"]) + for i in range(0, items - 4): + rev_estimates: Dict = {} + rev_estimates["symbol"] = ticker + num_estimates = seek_object["revenue_num_of_estimates"].get(str(i)) + if not num_estimates: + continue + period = num_estimates[0].get("period", {}) + if period: + period_type = period.get("periodtypeid") + rev_estimates["calendar_year"] = period.get("calendaryear") + rev_estimates["calendar_period"] = ( + "Q" + str(period.get("calendarquarter", "")) + if period_type == "quarterly" + else "FY" + ) + rev_estimates["date"] = period.get("periodenddate").split("T")[0] + rev_estimates["fiscal_year"] = period.get("fiscalyear") + rev_estimates["fiscal_period"] = ( + "Q" + str(period.get("fiscalquarter", "")) + if period_type == "quarterly" + else "FY" + ) + rev_estimates["number_of_analysts"] = num_estimates[0].get( + "dataitemvalue" + ) + mean = seek_object["revenue_consensus_mean"].get(str(i)) + if mean: + mean = mean[0].get("dataitemvalue") + rev_estimates["mean"] = int(float(mean)) + actual = ( + seek_object["revenue_actual"][str(i)][0].get("dataitemvalue") + if i < 1 + else None + ) + if actual: + rev_estimates["actual"] = int(float(actual)) + low = seek_object["revenue_consensus_low"].get(str(i)) + if low: + low = low[0].get("dataitemvalue") + rev_estimates["low_estimate"] = int(float(low)) + high = seek_object["revenue_consensus_high"].get(str(i)) + if high: + high = high[0].get("dataitemvalue") + rev_estimates["high_estimate"] = int(float(high)) + # Calculate the estimated growth percent. + this = float(mean) if mean else None + prev = None + percent = None + try: + prev = float( + seek_object["revenue"][str(i - 1)][0].get("dataitemvalue") + ) + except KeyError: + prev = float( + seek_object["revenue_consensus_mean"][str(i - 1)][0].get( + "dataitemvalue" + ) + ) + if this and prev: + percent = (this - prev) / prev + rev_estimates["period_growth"] = percent + results.append( + SAForwardSalesEstimatesData.model_validate(rev_estimates) + ) + + return results diff --git a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/upcoming_release_days.py b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/upcoming_release_days.py deleted file mode 100644 index 3dda9734307..00000000000 --- a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/upcoming_release_days.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Seeking Alpha Upcoming Release Days Model.""" - -from datetime import datetime -from typing import Any, Dict, List, Optional - -import requests -from openbb_core.provider.abstract.fetcher import Fetcher -from openbb_core.provider.standard_models.upcoming_release_days import ( - UpcomingReleaseDaysData, - UpcomingReleaseDaysQueryParams, -) -from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS -from pydantic import Field, field_validator - - -class SAUpcomingReleaseDaysQueryParams(UpcomingReleaseDaysQueryParams): - """Seeking Alpha Upcoming Release Days Query. - - Source: https://seekingalpha.com/api/v3/earnings_calendar/tickers - """ - - limit: int = Field( - description=QUERY_DESCRIPTIONS.get("limit", "") - + "In this case, the number of lookahead days.", - default=10, - ) - - -class SAUpcomingReleaseDaysData(UpcomingReleaseDaysData): - """Seeking Alpha Upcoming Release Days Data.""" - - __alias_dict__ = {"symbol": "slug", "release_time_type": "release_time"} - - sector_id: Optional[int] = Field( - description="The sector ID of the asset.", - ) - - @field_validator("release_date", mode="before", check_fields=False) - def validate_release_date(cls, v: Any) -> Any: # pylint: disable=E0213 - """Validate the release date.""" - v = v.split("T")[0] - return datetime.strptime(v, "%Y-%m-%d").date() - - -class SAUpcomingReleaseDaysFetcher( - Fetcher[ - SAUpcomingReleaseDaysQueryParams, - List[SAUpcomingReleaseDaysData], - ] -): - """Transform the query, extract and transform the data from the Seeking Alpha endpoints.""" - - @staticmethod - def transform_query(params: Dict[str, Any]) -> SAUpcomingReleaseDaysQueryParams: - """Transform the query.""" - return SAUpcomingReleaseDaysQueryParams(**params) - - @staticmethod - def extract_data( - query: SAUpcomingReleaseDaysQueryParams, # pylint: disable=W0613 - credentials: Optional[Dict[str, str]], - **kwargs: Any, - ) -> Dict: - """Return the raw data from the Seeking Alpha endpoint.""" - url = ( - f"https://seekingalpha.com/api/v3/earnings_calendar/tickers?" - f"filter%5Bselected_date%5D={str(datetime.now().date())}" # cspell:disable-line - f"&filter%5Bwith_rating%5D=false&filter%5Bcurrency%5D=USD" # cspell:disable-line - ) - response = requests.get( - url=url, - timeout=5, - ) - if response.status_code != 200: - raise ValueError( - f"Seeking Alpha Upcoming Release Days Fetcher failed with status code " - f"{response.status_code}" - ) - - if not response.json()["data"]: - raise ValueError( - "Seeking Alpha Upcoming Release Days Fetcher failed with empty response." - ) - - return response.json() - - @staticmethod - def transform_data( - query: SAUpcomingReleaseDaysQueryParams, data: Dict, **kwargs: Any - ) -> List[SAUpcomingReleaseDaysData]: - """Transform the data to the standard format.""" - transformed_data: List[Dict[str, Any]] = [] - data = data["data"] - for row in data: - transformed_data.append( - { - "symbol": row["attributes"]["slug"], - "name": row["attributes"]["name"], - "exchange": row["attributes"]["exchange"], - "release_time_type": row["attributes"]["release_time"], - "release_date": row["attributes"]["release_date"], - "sector_id": row["attributes"]["sector_id"], - } - ) - - return [ - SAUpcomingReleaseDaysData(**row) for row in transformed_data[: query.limit] - ] diff --git a/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/utils/helpers.py b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/utils/helpers.py new file mode 100644 index 00000000000..656b4d0c4fe --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/utils/helpers.py @@ -0,0 +1,40 @@ +"""Seeking Alpha Utilities.""" + +from datetime import timedelta + +from openbb_core.provider.utils.helpers import amake_request + +HEADERS = { + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0", + "Accept": "*/*", + "Accept-Language": "de,en-US;q=0.7,en;q=0.3", + "Accept-Encoding": "gzip, deflate, br", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", + "Connection": "keep-alive", +} + + +def date_range(start_date, end_date): + """Yield dates between start_date and end_date.""" + for n in range(int((end_date - start_date).days) + 1): + yield start_date + timedelta(n) + + +async def get_seekingalpha_id(symbol: str) -> str: + """Map a ticker symbol to its Seeking Alpha ID.""" + url = "https://seekingalpha.com/api/v3/searches" + querystring = { + "filter[type]": "symbols", + "filter[list]": "all", + "page[size]": "100", + } + querystring["filter[query]"] = symbol + payload = "" + response = await amake_request( + url, data=payload, headers=HEADERS, params=querystring + ) + ids = response.get("symbols") # type: ignore + + return str(ids[0].get("id", "")) diff --git a/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_calendar_earnings_fetcher.yaml b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_calendar_earnings_fetcher.yaml new file mode 100644 index 00000000000..cc799034988 --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_calendar_earnings_fetcher.yaml @@ -0,0 +1,400 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://seekingalpha.com/api/v3/earnings_calendar/tickers?filter%5Bcurrency%5D=USD&filter%5Bselected_date%5D=MOCK_DATE&filter%5Bwith_rating%5D=false + response: + body: + string: !!binary | + H4sIAAAAAAAAA81ba2/byhH9K4S/tAWSgPsiufmml2VdU4+Kiu20KIK1xFhEKK4uH+71DfLfO1Sd + aEkuEnk3agokHyzJj4OZOXPmzPDzxUaU4uLtPz9fJJuLtxcYBd7Fq4vyaR/DV7HIsyR7+LAWaZxt + RP6hTNaf4hw+IMoyT+6rMi4u3n6+KNLqAT4+ma3ewXuZ2NXfPMnKKimdSbZ+Ay/Gf6y3Inuo35j1 + omHv7/BaEa9LmX+ofzFlry7yOI1FEcMvOXz/Qhbl66nIP8UlfPbrm/Dn1m9iF9PXLnuNycqlb10X + /r1xXfcf8Mnd4VvWYn/xFgW+5yGGfHjrjfvly6uvKDFHxiinw9UR5DTelLnMkrWzT9ctmO+jURMk + 0YDMY1uMiAQ+dRlvYaScuOYgb4e990eUtzL/tBFPr35pML2AU4CKWzgZ58ahnK1G0RHlLC5HkH+n + omRuN2Wto+kRwjF1SQuk59qgHI8VkKJMZCZSZ5wnm1OSlnWTdlRAiUIdbuDHGtUlI4QFGLEWSoQZ + M47lch4pdbmUReFEwC5xcWo8cRfoT6AgSl3uYdINKA0Cc7D90XXNLc9Mez1yrmS6AaYudBXa4SHv + HJmLMWfcIx4EVaVa5DNsHNPhaHB9hDmM6+ZTOPOq3EiZOwOZ72V+yGf4kNpgOojPE1uAjF3kc2gv + KmRI7cDzjUH3fztC7v/2l8K53co0LqAFO4O0uv8Wam1ed5CTc8QauT4mPPBbsfZ9nxNzNo7m4c0R + eiTTxxgkxO4lcdZ0WGuyQq4XBJwjaDyNxGbUPMbL8Ah0KdL91glFlcfZS7Dqctq6/yCXco/57Spm + PmbYHO5VOFPo6krskrSUGWDO4pqwnuv40E9+pBPPkc6eT5FHGGqVsc9tZGJ/Ek6UQk7S5B7+6/hZ + I4fPoy2ojxinHWnBCA6M2SpajRZK0ZbxHrouhHWcy2rvhPV7BiG1l/4UAxt5vhc0qRn6EzIfcAbR + rRJT+ArCuakKGIVEerrOOEcKk4BS6LQwBKgcRYCnjSM7uBnMj5EdiMe1/Ab3V6sq0BoYQhngBlyv + 7kvmgHurnqIge6WscidMPsZF+QS995vCCpNd8l/t+4PEPgtBY6AqBCh5E7nLoD+ZhzrsK8gHcZrG + 9zmAdIYTJyw3hvO7dTPCJIAZlgRNrBaTUDi+fNM75nQIyrFwxjDJOKOsjPNSJNkONMehC7dBd5SV + jqPteQsT5gYeamU2ojAuEeP43vaPoG/j5F5+R2f8r7oRjH8AEiPUSmTOAvNutIhmim2xqHVzKXKn + V5VyJ8vk8VsZO++unUU4+HF3OksRI05918PYb80NBISmcZCHvRslt0fg1z2KUwZ8nfVmXbpguSFo + R15LUnGIrnkaX4Yzxcm4DEd3DryiY6hOsaJzNF3EPOJj6rXCyLnPzVO4P18uj9Xal3nuDPMkTWG6 + h26kaz2ngB1WOfwAW6sRZgOfBKipqTC1sIx709vhEW1vF+fJWmTOrZSb2sd9GU9hTZDtGRlRH8oV + 5oOmtELIPMarsSIjV3lS7WACPCjm04Z6DVD7MbeuVw503IBJqYWsiAY3ypwbbSU4GPVG4VGkWpya + 1nMW+uWEQtW22NdzacDNxePgpld3k2dHbgDD/I1YO7M3N20doQGpcS1+gvMI9ozPmd/ur9i8w0xn + S2UimMosl780jjDgYQYdpaUgYDCwiGMvVKWwAOLdJKUonNU2zsU+rmAFVji9vvPXfXWf/u3HAkIX + XOv2yjxOEYjjBvKAmjNStLhVuk5UZQv57/h7FqsmjTWstLBHSmEfAtNtAymnFkPAarKaHSt1BcHN + nKlYb5Mszp9O9GfOAxW7vksJww0z2UPmyTwcKs2VDJ3oqSjjXfGdrtrREhqk1q0GnAovIB4NVKAg + FQk2X1gOewo1vZfVRui5qQtQs/axzlpKSMCI7zWyFqgYe+YIr6IrxWm7gg1cKp6EE22T/f4gDruD + ehdsVyFZRxNgQjMFf7yRtsDNFuvn8O5YoWH8R5JdJjCgr7eKC9NFq6Ek2sX7Ezor2GqUYdjrNRAz + 5lpYi8P5VEngIYyrp7ZW3fxmr3oxiF4Car+BEXLaPH9H0UidT6tcFnDuoUtbTSA1NGTfWzDzAwoO + cUs/sMBifJsqiw5MRLaZHj2HgfxlYrBe3wD9+I0i5X7gm8fzejZX+Ai+Wjnzjx+LLezcnYXIS2in + EN0FNJvdDpY977KkLFpyqcNP5xnTA048DEu85pKW2sjE98oAhxA6tVbPtI2FCwPqkUZskcuo+aID + VrFHAh7BVA6ryeeNTtne0nWCqKtVe0JCLneD+v6nIRoQhgqGP9XwYO3ucIf29WDtzumtf6+SIqlP + CrQmsIaZsipNu4dA9ng5iCTKKG3ABUfNwgYeh+qJzDiV93AHFB0M0iPuU1P5XLhhCCc+SMFWmLHN + YmfZV85IevmDdPqpXH+Ci8Qk01ikmijrmqy1Sgzq8dVrZjQBd818kRPNVwrSSGby9Sp+oYt2Fqg+ + g6MCzhtRhWMR89IdjSeKXhptHhIbwWQtguHKlMPZT1NJgKNm4XbD8ZOil4YxHO0l4J/tYWBN/5+I + Co6AmOejhlD0uE+YudE0uOupe/a73n5/4mCuS157Lqbcx3BO2xAQCDxEC/U0Gi+VGWckHmDFvNiK + fCfWB4/pBQcFGofJOp8pp4FLmvMrHKH6PjVutuOlutgYy3qpIZwVTHWZTOXD07PAML4Xt8cMcywB + u6XlSiDPHPPVu0l9kv4sMK6q5M/jNKBf5uh6z1mGWAoLWOCsBmMhBG3W3G4a3YwVZ230KB9iuATS + +BIakOdIYphfORBTQ0JRSpH5zDMIJ4pNOoDnFfIoeciOWZzE33PXNLh1YtlaWBDqeZw0j2PgTj7w + zXevv10qj3bwyxPpWHMjYV2mhDI4vWwLROISc3DTSO030yotkyjOCrgx7k2+f2+riaimBdljdjml + nUWHx13zE/LpcnpkpuloOFnOp85VLNJyuxYwwn8j5jqlTyNlzbLOGjn4UHDGR5qLDhSYU/JqOVGu + YWD5Cr7Ek9NP5MFf7D7Fo4mwhqvszShYM3senJM35x6CmTnU6fuZ0n2mMOz8WQPdxRvzxaR1QGFi + B2FBm94pXDwx81lgMhwpWmqyiUUmd7CmO3WU1XDwT4BZPw3Ruj4FU5ybs9RgHiklO5DFThbPFXti + gWoS1x4oPACBcdOnYAGDpZ2xUgyv1cvTsJKfKthvKEpRd5+nqdNzdB+E4GE7eKinKRIDuOUyhtvv + zZWn0PpCFtsY8MKjhYl4FsUvPz49B3aG4fKUsSZ0j1qtnZfK1US0TqK9yNe/UjLCgwCYHwa9f335 + D5Tg08YdOwAA + headers: + Accept-Ranges: + - bytes + Age: + - '21758' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=600, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '2517' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 16:42:04 GMT + Etag: + - W/"2a09f706fdc5455baebaa05403eea943" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=3502582417922; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + - machine_cookie_ts=1716482524; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - jv3Z3eZW3zjx9Eig + X-Runtime: + - '3.360883' + X-Served-By: + - cache-bfi-krnt7300052-BFI + X-Timer: + - S1716482525.972443,VS0,VE1 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://seekingalpha.com/api/v3/earnings_calendar/tickers?filter%5Bcurrency%5D=USD&filter%5Bselected_date%5D=MOCK_DATE&filter%5Bwith_rating%5D=false + response: + body: + string: !!binary | + H4sIAAAAAAAAA6tWSkksSVSyio6tBQCUZLJeCwAAAA== + headers: + Accept-Ranges: + - bytes + Age: + - '21736' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=600, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '31' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 16:42:04 GMT + Etag: + - W/"8fe32e407a1038ee38753b70e5374b3a" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=3093286235387; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + - machine_cookie_ts=1716482524; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - xndndKwIIPRTNj0D + X-Runtime: + - '3.198017' + X-Served-By: + - cache-bfi-krnt7300039-BFI + X-Timer: + - S1716482525.972185,VS0,VE2 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://seekingalpha.com/api/v3/earnings_calendar/tickers?filter%5Bcurrency%5D=USD&filter%5Bselected_date%5D=MOCK_DATE&filter%5Bwith_rating%5D=false + response: + body: + string: !!binary | + H4sIAAAAAAAAA6tWSkksSVSyio6tBQCUZLJeCwAAAA== + headers: + Accept-Ranges: + - bytes + Age: + - '21742' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=600, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '31' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 16:42:04 GMT + Etag: + - W/"8fe32e407a1038ee38753b70e5374b3a" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=0300067817068; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + - machine_cookie_ts=1716482524; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - zbTuU4fDBYmMmSFT + X-Runtime: + - '3.296164' + X-Served-By: + - cache-bfi-krnt7300087-BFI + X-Timer: + - S1716482525.972668,VS0,VE1 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + method: GET + uri: https://seekingalpha.com/api/v3/earnings_calendar/tickers?filter%5Bcurrency%5D=USD&filter%5Bselected_date%5D=MOCK_DATE&filter%5Bwith_rating%5D=false + response: + body: + string: !!binary | + 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 + zdTsZgW2Vx7+/e2/icJqGdpKAAA= + headers: + Accept-Ranges: + - bytes + Age: + - '21750' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=600, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '3098' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 16:42:04 GMT + Etag: + - W/"c0d703afc9ed2f546a04aaee2925a9bf" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=0933180971065; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + - machine_cookie_ts=1716482524; expires=Wed, 23 May 2029 16:42:04 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - zjGiaWaQMoFNHTkQ + X-Runtime: + - '3.387168' + X-Served-By: + - cache-bfi-krnt7300112-BFI + X-Timer: + - S1716482525.972384,VS0,VE1 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +version: 1 diff --git a/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_eps_estimates.yaml b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_eps_estimates.yaml new file mode 100644 index 00000000000..8f408139aab --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_eps_estimates.yaml @@ -0,0 +1,223 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Accept-Language: + - de,en-US;q=0.7,en;q=0.3 + Connection: + - keep-alive + Sec-Fetch-Dest: + - empty + Sec-Fetch-Mode: + - cors + Sec-Fetch-Site: + - same-origin + method: GET + uri: https://seekingalpha.com/api/v3/searches?filter%5Blist%5D=all&filter%5Bquery%5D=NVDA&filter%5Btype%5D=symbols&page%5Bsize%5D=100 + response: + body: + string: !!binary | + H4sIAAAAAAAAA5WUUW/aMBSF/4qVp02CQOzYSVBVKSUwIXVsErQCTXsIxKWWTMLspCKq+O+zCyVx + k1Tb47124k/nnnteLVnuNxmX1ujXq8USa+Q4eNiz8vJArdHl0OpZcpsJ1fACglFgo55VCK7OB+ev + B/PHKFS30nivv7rZ3OrGzWBzq5rbLM1pmqv+/HEWzUIwzsQhE3HOslT/mRc7dZa+JLF16p0ZPBgE + BHZSOJ7vERs6uAVjXWEoBl1V768Z5cn3+AhqgODHQYOAWbrN9hQscgVGdyWYLKcGXFnBERejT+CI + EslWOrXAhaNxUybV+2+lRttKLEI84vndYhHoD22C2njuDbF0VYn1TcQpy+niORZUAngE91m6M5SL + YsabQvGrUAQSiJRVOrzkIOz7to9Ji1ALA0xXFdhSxIkAjg3xysC5o7EA7UzyyoQdV73XSTR0sA2R + 00JkALUJJa9CLZ4zkf+DUjVHIU/7pQsLugjZrqtm3Ni6B4NLV5VQERP0qL19FkWbXi2Z9hq4KzgH + cAUu0/1o9sJEczvRAk0W4KCFLDLIdPUp2SUvwNsUnU6ypEaGHdRtex/5apSB95FsrVLB2MKq0Qiq + L7Wc+Are0uNdsZ+FyjBJzZgoVYjVNxPBIfS756oiDHpDX99ozDVc9aPJ9GE+XvbPf6nUvECtGqER + ci4PgqktjdgLFZI9MZqAyZ+C5SWYFmkC+mDMYylBCHQM1ZL32E/oU5Fu8/fXTr9PfwHF9HT0GwYA + AA== + headers: + Accept-Ranges: + - bytes + Age: + - '0' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=60, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '571' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 18:10:32 GMT + Etag: + - W/"1a4846bf634ee5326277e66d6bb54460" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=1388282386286; expires=Wed, 23 May 2029 18:10:32 GMT; path=/; + - machine_cookie_ts=1716487832; expires=Wed, 23 May 2029 18:10:32 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - Gkj-RwREjk1ZTUrZ + X-Runtime: + - '0.062366' + X-Served-By: + - cache-bfi-krnt7300078-BFI + X-Timer: + - S1716487832.960725,VS0,VE132 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Accept-Language: + - de,en-US;q=0.7,en;q=0.3 + Connection: + - keep-alive + Sec-Fetch-Dest: + - empty + Sec-Fetch-Mode: + - cors + Sec-Fetch-Site: + - same-origin + method: GET + uri: https://seekingalpha.com/api/v3/symbol_data/estimates?estimates_data_items=MOCK_ITEMS&period_type=MOCK_PERIOD&relative_periods=MOCK_PERIODS&ticker_ids=MOCK_TICKER_IDS + response: + body: + string: !!binary | + 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 + 9z+Ftwv5io9Je/vl5eVfb7A0d7OJAAA= + headers: + Accept-Ranges: + - bytes + Age: + - '0' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=60, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '2588' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 18:10:32 GMT + Etag: + - W/"c526b90b1ad2bad27d7a89410724e0b4" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=1430579407975; expires=Wed, 23 May 2029 18:10:32 GMT; path=/; + - machine_cookie_ts=1716487832; expires=Wed, 23 May 2029 18:10:32 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - EKKFASmucRVgAHBy + X-Runtime: + - '0.414440' + X-Served-By: + - cache-bfi-krnt7300098-BFI + X-Timer: + - S1716487832.180053,VS0,VE473 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +version: 1 diff --git a/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_sales_estimates.yaml b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_sales_estimates.yaml new file mode 100644 index 00000000000..24aebfd9640 --- /dev/null +++ b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_forward_sales_estimates.yaml @@ -0,0 +1,208 @@ +interactions: +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Accept-Language: + - de,en-US;q=0.7,en;q=0.3 + Connection: + - keep-alive + Sec-Fetch-Dest: + - empty + Sec-Fetch-Mode: + - cors + Sec-Fetch-Site: + - same-origin + method: GET + uri: https://seekingalpha.com/api/v3/searches?filter%5Blist%5D=all&filter%5Bquery%5D=NVDA&filter%5Btype%5D=symbols&page%5Bsize%5D=100 + response: + body: + string: !!binary | + H4sIAAAAAAAAA5WUUW/aMBSF/4qVp02CQOzYSVBVKSUwIXVsErQCTXsIxKWWTMLspCKq+O+zCyVx + k1Tb47124k/nnnteLVnuNxmX1ujXq8USa+Q4eNiz8vJArdHl0OpZcpsJ1fACglFgo55VCK7OB+ev + B/PHKFS30nivv7rZ3OrGzWBzq5rbLM1pmqv+/HEWzUIwzsQhE3HOslT/mRc7dZa+JLF16p0ZPBgE + BHZSOJ7vERs6uAVjXWEoBl1V768Z5cn3+AhqgODHQYOAWbrN9hQscgVGdyWYLKcGXFnBERejT+CI + EslWOrXAhaNxUybV+2+lRttKLEI84vndYhHoD22C2njuDbF0VYn1TcQpy+niORZUAngE91m6M5SL + YsabQvGrUAQSiJRVOrzkIOz7to9Ji1ALA0xXFdhSxIkAjg3xysC5o7EA7UzyyoQdV73XSTR0sA2R + 00JkALUJJa9CLZ4zkf+DUjVHIU/7pQsLugjZrqtm3Ni6B4NLV5VQERP0qL19FkWbXi2Z9hq4KzgH + cAUu0/1o9sJEczvRAk0W4KCFLDLIdPUp2SUvwNsUnU6ypEaGHdRtex/5apSB95FsrVLB2MKq0Qiq + L7Wc+Are0uNdsZ+FyjBJzZgoVYjVNxPBIfS756oiDHpDX99ozDVc9aPJ9GE+XvbPf6nUvECtGqER + ci4PgqktjdgLFZI9MZqAyZ+C5SWYFmkC+mDMYylBCHQM1ZL32E/oU5Fu8/fXTr9PfwHF9HT0GwYA + AA== + headers: + Accept-Ranges: + - bytes + Age: + - '1' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=60, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '571' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 18:10:32 GMT + Etag: + - W/"1a4846bf634ee5326277e66d6bb54460" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=5349009622943; expires=Wed, 23 May 2029 18:10:32 GMT; path=/; + - machine_cookie_ts=1716487832; expires=Wed, 23 May 2029 18:10:32 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - Gkj-RwREjk1ZTUrZ + X-Runtime: + - '0.062366' + X-Served-By: + - cache-bfi-krnt7300038-BFI + X-Timer: + - S1716487833.753393,VS0,VE2 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +- request: + body: '' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Accept-Language: + - de,en-US;q=0.7,en;q=0.3 + Connection: + - keep-alive + Sec-Fetch-Dest: + - empty + Sec-Fetch-Mode: + - cors + Sec-Fetch-Site: + - same-origin + method: GET + uri: https://seekingalpha.com/api/v3/symbol_data/estimates?estimates_data_items=MOCK_ITEMS&period_type=MOCK_PERIOD&relative_periods=MOCK_PERIODS&ticker_ids=MOCK_TICKER_IDS + response: + body: + string: !!binary | + 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 + headers: + Accept-Ranges: + - bytes + Age: + - '0' + Allow: + - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS + Cache-Control: + - max-age=60, public + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1760' + Content-Security-Policy: + - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri + https://seekingalpha.com/report/csp' + Content-Type: + - application/json; charset=utf-8 + Date: + - Thu, 23 May 2024 18:10:33 GMT + Etag: + - W/"12cf4f5afb8ec98c9f2d871548d040a7" + Referrer-Policy: + - strict-origin-when-cross-origin + Set-Cookie: + - machine_cookie=7439933647486; expires=Wed, 23 May 2029 18:10:33 GMT; path=/; + - machine_cookie_ts=1716487833; expires=Wed, 23 May 2029 18:10:33 GMT; path=/; + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Vary: + - User-Agent, Accept-Encoding + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Request-Id: + - qTivZXS0q5ixQj7N + X-Runtime: + - '0.390214' + X-Served-By: + - cache-bfi-krnt7300067-BFI + X-Timer: + - S1716487833.838951,VS0,VE510 + X-XSS-Protection: + - '0' + alt-svc: + - h3=":443";ma=86400,h3-29=":443";ma=86400,h3-27=":443";ma=86400 + status: + code: 200 + message: OK +version: 1 diff --git a/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_upcoming_release_days_fetcher.yaml b/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_upcoming_release_days_fetcher.yaml deleted file mode 100644 index 510af9913f6..00000000000 --- a/openbb_platform/providers/seeking_alpha/tests/record/http/test_seeking_alpha_fetchers/test_sa_upcoming_release_days_fetcher.yaml +++ /dev/null @@ -1,249 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - method: GET - uri: https://seekingalpha.com/api/v3/earnings_calendar/tickers?filter%5Bcurrency%5D=USD&filter%5Bselected_date%5D=MOCK_DATE&filter%5Bwith_rating%5D=false - response: - body: - string: !!binary | - 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 - headers: - Accept-Ranges: - - bytes - Age: - - '21880' - Allow: - - GET, POST, HEAD, PUT, PATCH, DELETE, OPTIONS - Cache-Control: - - max-age=600, public - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '9949' - Content-Security-Policy: - - 'default-src https: wss: data: blob: ''unsafe-inline'' ''unsafe-eval''; report-uri - https://seekingalpha.com/report/csp' - Content-Type: - - application/json; charset=utf-8 - Date: - - Thu, 09 Nov 2023 17:43:56 GMT - ETag: - - W/"737a1bbf695efa544b45db3f96c8c113" - Referrer-Policy: - - strict-origin-when-cross-origin - Strict-Transport-Security: - - max-age=63072000; includeSubDomains; preload - Vary: - - User-Agent, Accept-Encoding - X-Cache: - - HIT, HIT - X-Cache-Hits: - - 4, 1 - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Frame-Options: - - SAMEORIGIN - X-Permitted-Cross-Domain-Policies: - - none - X-Request-Id: - - 6KuqekWy2HeAyTrW - X-Runtime: - - '0.141511' - X-Served-By: - - cache-bfi-krnt7300051-BFI, cache-vie6380-VIE - X-Timer: - - S1699551837.656634,VS0,VE1 - X-XSS-Protection: - - 1; mode=block - set-cookie: - - machine_cookie=5308334431016; expires=Wed, 08 Nov 2028 17:43:56 GMT; path=/; - - machine_cookie_ts=1699551836; expires=Wed, 08 Nov 2028 17:43:56 GMT; path=/; - status: - code: 200 - message: OK -version: 1 diff --git a/openbb_platform/providers/seeking_alpha/tests/test_seeking_alpha_fetchers.py b/openbb_platform/providers/seeking_alpha/tests/test_seeking_alpha_fetchers.py index e279c044e00..a049b3b7c7c 100644 --- a/openbb_platform/providers/seeking_alpha/tests/test_seeking_alpha_fetchers.py +++ b/openbb_platform/providers/seeking_alpha/tests/test_seeking_alpha_fetchers.py @@ -2,8 +2,12 @@ import pytest from openbb_core.app.service.user_service import UserService -from openbb_seeking_alpha.models.upcoming_release_days import ( - SAUpcomingReleaseDaysFetcher, +from openbb_seeking_alpha.models.calendar_earnings import SACalendarEarningsFetcher +from openbb_seeking_alpha.models.forward_eps_estimates import ( + SAForwardEpsEstimatesFetcher, +) +from openbb_seeking_alpha.models.forward_sales_estimates import ( + SAForwardSalesEstimatesFetcher, ) test_credentials = UserService().default_user_settings.credentials.model_dump( @@ -18,15 +22,39 @@ def vcr_config(): "filter_headers": [("User-Agent", None)], "filter_query_parameters": [ ("filter[selected_date]", "MOCK_DATE"), + ("relative_periods", "MOCK_PERIODS"), + ("estimates_data_items", "MOCK_ITEMS"), + ("period_type", "MOCK_PERIOD"), + ("ticker_ids", "MOCK_TICKER_IDS"), ], } @pytest.mark.record_http -def test_sa_upcoming_release_days_fetcher(credentials=test_credentials): - """Test the Seeking Alpha Upcoming Release Days fetcher.""" - params = {"limit": 5} +def test_sa_calendar_earnings_fetcher(credentials=test_credentials): + """Test the Seeking Alpha Calendar Earnings fetcher.""" + params = {} - fetcher = SAUpcomingReleaseDaysFetcher() + fetcher = SACalendarEarningsFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_sa_forward_eps_estimates(credentials=test_credentials): + """Test the Seeking Alpha Forward EPS Estimates fetcher.""" + params = {"symbol": "NVDA"} + + fetcher = SAForwardEpsEstimatesFetcher() + result = fetcher.test(params, credentials) + assert result is None + + +@pytest.mark.record_http +def test_sa_forward_sales_estimates(credentials=test_credentials): + """Test the Seeking Alpha Forward Sales Estimates fetcher.""" + params = {"symbol": "NVDA"} + + fetcher = SAForwardSalesEstimatesFetcher() result = fetcher.test(params, credentials) assert result is None