summaryrefslogtreecommitdiffstats
path: root/openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py
diff options
context:
space:
mode:
Diffstat (limited to 'openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py')
-rw-r--r--openbb_platform/providers/seeking_alpha/openbb_seeking_alpha/models/forward_sales_estimates.py179
1 files changed, 179 insertions, 0 deletions
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