summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-04-04 09:09:01 -0700
committerGitHub <noreply@github.com>2024-04-04 16:09:01 +0000
commit3f08e0f3420c07cfe98a2b0e81d629c1d2ca7978 (patch)
tree5e7b35ad5f7908809aad34961d809fa7c3625630
parent45998938eddd6fc29e5a4016e06fc1856cd35252 (diff)
[Feature] Add Forward Sales and EPS Estimates (#6269)
* add intrinio forward estimates * decorators * mypy * better sentence for descriptions * black * mypy * review things * update field --------- Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
-rw-r--r--openbb_platform/core/openbb_core/provider/standard_models/forward_eps_estimates.py67
-rw-r--r--openbb_platform/core/openbb_core/provider/standard_models/forward_sales_estimates.py68
-rw-r--r--openbb_platform/core/openbb_core/provider/standard_models/price_target_consensus.py9
-rw-r--r--openbb_platform/extensions/equity/integration/test_equity_api.py70
-rw-r--r--openbb_platform/extensions/equity/integration/test_equity_python.py64
-rw-r--r--openbb_platform/extensions/equity/openbb_equity/estimates/estimates_router.py46
-rw-r--r--openbb_platform/openbb/package/equity_estimates.py284
-rw-r--r--openbb_platform/providers/fmp/openbb_fmp/__init__.py2
-rw-r--r--openbb_platform/providers/fmp/openbb_fmp/models/forward_eps_estimates.py153
-rw-r--r--openbb_platform/providers/fmp/openbb_fmp/models/price_target_consensus.py15
-rw-r--r--openbb_platform/providers/fmp/tests/record/http/test_fmp_fetchers/test_fmp_equity_forward_eps_fetcher.yaml269
-rw-r--r--openbb_platform/providers/fmp/tests/test_fmp_fetchers.py15
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/__init__.py12
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/models/forward_eps_estimates.py230
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/models/forward_sales_estimates.py249
-rw-r--r--openbb_platform/providers/intrinio/openbb_intrinio/models/price_target_consensus.py183
-rw-r--r--openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_forward_eps_estimates_fetcher.yaml48
-rw-r--r--openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_forward_sales_estimates_fetcher.yaml52
-rw-r--r--openbb_platform/providers/intrinio/tests/record/http/test_intrinio_fetchers/test_intrinio_price_target_consensus_fetcher.yaml38
-rw-r--r--openbb_platform/providers/intrinio/tests/test_intrinio_fetchers.py36
-rw-r--r--openbb_platform/providers/tmx/openbb_tmx/models/price_target_consensus.py12
-rw-r--r--openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py12
22 files changed, 1913 insertions, 21 deletions
diff --git a/openbb_platform/core/openbb_core/provider/standard_models/forward_eps_estimates.py b/openbb_platform/core/openbb_core/provider/standard_models/forward_eps_estimates.py
new file mode 100644
index 00000000000..e0f95d9c8a1
--- /dev/null
+++ b/openbb_platform/core/openbb_core/provider/standard_models/forward_eps_estimates.py
@@ -0,0 +1,67 @@
+"""Forward EPS Estimates Standard Model."""
+
+from datetime import date as dateType
+from typing import Optional
+
+from pydantic import Field, field_validator
+
+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,
+ QUERY_DESCRIPTIONS,
+)
+
+
+class ForwardEpsEstimatesQueryParams(QueryParams):
+ """Forward EPS Estimates Query Parameters."""
+
+ symbol: Optional[str] = Field(
+ default=None,
+ description=QUERY_DESCRIPTIONS["symbol"],
+ )
+
+ @field_validator("symbol", mode="before", check_fields=False)
+ @classmethod
+ def to_upper(cls, v):
+ """Convert field to uppercase."""
+ return v.upper() if v else None
+
+
+class ForwardEpsEstimatesData(Data):
+ """Forward EPS Estimates Data."""
+
+ symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
+ name: Optional[str] = Field(default=None, description="Name of the entity.")
+ date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
+ fiscal_year: Optional[int] = Field(
+ default=None, description="Fiscal year for the estimate."
+ )
+ fiscal_period: Optional[str] = Field(
+ default=None, description="Fiscal quarter for the estimate."
+ )
+ calendar_year: Optional[int] = Field(
+ default=None, description="Calendar year for the estimate."
+ )
+ calendar_period: Optional[str] = Field(
+ default=None, description="Calendar quarter for the estimate."
+ )
+ low_estimate: Optional[float] = Field(
+ default=None, description="Estimated EPS low for the period."
+ )
+ high_estimate: Optional[float] = Field(
+ default=None, description="Estimated EPS high for the period."
+ )
+ mean: Optional[float] = Field(
+ default=None, description="Estimated EPS mean for the period."
+ )
+ median: Optional[float] = Field(
+ default=None, description="Estimated EPS median for the period."
+ )
+ standard_deviation: Optional[float] = Field(
+ default=None, description="Estimated EPS standard deviation for the period."
+ )
+ number_of_analysts: Optional[int] = Field(
+ default=None,
+ description="Number of analysts providing estimates for the period.",
+ )
diff --git a/openbb_platform/core/openbb_core/provider/standard_models/forward_sales_estimates.py b/openbb_platform/core/openbb_core/provider/standard_models/forward_sales_estimates.py
new file mode 100644
index 00000000000..8d3e1217d58
--- /dev/null
+++ b/openbb_platform/core/openbb_core/provider/standard_models/forward_sales_estimates.py
@@ -0,0 +1,68 @@
+"""Forward Sales Estimates Standard Model."""
+
+from datetime import date as dateType
+from typing import Optional
+
+from pydantic import Field, field_validator
+
+from openbb_core.provider.abstract.data import Data, ForceInt
+from openbb_core.provider.abstract.query_params import QueryParams
+from openbb_core.provider.utils.descriptions import (
+ DATA_DESCRIPTIONS,
+ QUERY_DESCRIPTIONS,
+)
+
+
+class ForwardSalesEstimatesQueryParams(QueryParams):
+ """Forward Sales Estimates Query Parameters."""
+
+ symbol: Optional[str] = Field(
+ default=None,
+ description=QUERY_DESCRIPTIONS["symbol"],
+ )
+
+ @field_validator("symbol", mode="before", check_fields=False)
+ @classmethod
+ def to_upper(cls, v):
+ """Convert field to uppercase."""
+ return v.upper() if v else None
+
+
+class ForwardSalesEstimatesData(Data):
+ """Forward Sales Estimates Data."""
+
+ symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
+ name: Optional[str] = Field(default=None, description="Name of the entity.")
+ date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
+ fiscal_year: Optional[int] = Field(
+ default=None, description="Fiscal year for the estimate."
+ )
+ fiscal_period: Optional[str] = Field(
+ default=None, description="Fiscal quarter for the estimate."
+ )
+ calendar_year: Optional[int] = Field(
+ default=None, description="Calendar year for the estimate."
+ )
+ calendar_period: Optional[str] = Field(
+ default=None, description="Calendar quarter for the estimate."
+ )
+ low_estimate: Optional[ForceInt] = Field(
+ default=None, description="The sales estimate low for the period."
+ )
+ high_estimate: Optional[ForceInt] = Field(
+ default=None, description="The sales estimate high for the period."
+ )
+ mean: Optional[ForceInt] = Field(
+ default=None, description="The sales estimate mean for the period."
+ )
+ median: Optional[ForceInt] = Field(
+ default=None, description="The sales estimate median for the period."
+ )
+ standard_deviation: Optional[ForceInt] = Field(
+ default=None,
+ description="The sales estimate standard deviation for the period.",
+ )
+ number_of_analysts: Optional[int] = Field(
+ default=None,
+ description="Number of analysts providing estimates for the period.",
+ )
diff --git a/openbb_platform/core/openbb_core/provider/standard_models/price_target_consensus.py b/openbb_platform/core/openbb_core/provider/standard_models/price_target_consensus.py
index f6949cb166a..106879a884f 100644
--- a/openbb_platform/core/openbb_core/provider/standard_models/price_target_consensus.py
+++ b/openbb_platform/core/openbb_core/provider/standard_models/price_target_consensus.py
@@ -15,19 +15,22 @@ from openbb_core.provider.utils.descriptions import (
class PriceTargetConsensusQueryParams(QueryParams):
"""Price Target Consensus Query."""
- symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
+ symbol: Optional[str] = Field(
+ default=None, description=QUERY_DESCRIPTIONS.get("symbol", "")
+ )
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
- def to_upper(cls, v: str) -> str:
+ def to_upper(cls, v):
"""Convert field to uppercase."""
- return v.upper()
+ return v.upper() if v else None
class PriceTargetConsensusData(Data):
"""Price Target Consensus Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
+ name: Optional[str] = Field(default=None, description="The company name")
target_high: Optional[float] = Field(
default=None, description="High target of the price target consensus."
)
diff --git a/openbb_platform/extensions/equity/integration/test_equity_api.py b/openbb_platform/extensions/equity/integration/test_equity_api.py
index 511b4305417..5b3d5bd56b3 100644
--- a/openbb_platform/extensions/equity/integration/test_equity_api.py
+++ b/openbb_platform/extensions/equity/integration/test_equity_api.py
@@ -359,7 +359,7 @@ def test_equity_fundamental_employee_count(params, headers):
@parametrize(
"params",
- [({"symbol": "AAPL", "period": "annual", "limit": 30})],
+ [({"symbol": "AAPL,MSFT", "period": "annual", "limit": 30})],
)
@pytest.mark.integration
def test_equity_estimates_historical(params, headers):
@@ -375,6 +375,67 @@ def test_equity_estimates_historical(params, headers):
@parametrize(
"params",
[
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "fiscal_period": "fy",
+ "fiscal_year": None,
+ "calendar_year": None,
+ "calendar_period": None,
+ "provider": "intrinio",
+ }
+ )
+ ],
+)
+@pytest.mark.integration
+def test_equity_estimates_forward_sales(params, headers):
+ 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/estimates/forward_sales?{query_str}"
+ result = requests.get(url, headers=headers, timeout=10)
+ assert isinstance(result, requests.Response)
+ assert result.status_code == 200
+
+
+@parametrize(
+ "params",
+ [
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "fiscal_period": "fy",
+ "fiscal_year": None,
+ "calendar_year": None,
+ "calendar_period": None,
+ "provider": "intrinio",
+ }
+ ),
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "fiscal_period": "annual",
+ "limit": None,
+ "include_historical": False,
+ "provider": "fmp",
+ }
+ ),
+ ],
+)
+@pytest.mark.integration
+def test_equity_estimates_forward_eps(params, headers):
+ 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/estimates/forward_eps?{query_str}"
+ result = requests.get(url, headers=headers, timeout=10)
+ assert isinstance(result, requests.Response)
+ assert result.status_code == 200
+
+
+@parametrize(
+ "params",
+ [
({"symbol": "AAPL", "period": "annual", "limit": 12, "provider": "fmp"}),
(
{
@@ -718,6 +779,13 @@ def test_equity_estimates_analyst_search(params, headers):
({"symbol": "AAPL", "provider": "fmp"}),
({"symbol": "AAPL,AMZN,RELIANCE.NS", "provider": "yfinance"}),
({"symbol": "TD:US", "provider": "tmx"}),
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "industry_group_number": None,
+ "provider": "intrinio",
+ }
+ ),
],
)
@pytest.mark.integration
diff --git a/openbb_platform/extensions/equity/integration/test_equity_python.py b/openbb_platform/extensions/equity/integration/test_equity_python.py
index 671df076840..ce421776080 100644
--- a/openbb_platform/extensions/equity/integration/test_equity_python.py
+++ b/openbb_platform/extensions/equity/integration/test_equity_python.py
@@ -334,7 +334,7 @@ def test_equity_fundamental_employee_count(params, obb):
@parametrize(
"params",
[
- ({"symbol": "AAPL", "period": "annual", "limit": 30}),
+ ({"symbol": "AAPL,MSFT", "period": "annual", "limit": 30}),
],
)
@pytest.mark.integration
@@ -652,6 +652,13 @@ def test_equity_estimates_analyst_search(params, obb):
({"symbol": "AAPL", "provider": "fmp"}),
({"symbol": "AAPL,AMZN,RELIANCE.NS", "provider": "yfinance"}),
({"symbol": "TD:US", "provider": "tmx"}),
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "industry_group_number": None,
+ "provider": "intrinio",
+ }
+ ),
],
)
@pytest.mark.integration
@@ -665,6 +672,61 @@ def test_equity_estimates_consensus(params, obb):
@parametrize(
"params",
[
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "fiscal_period": "fy",
+ "fiscal_year": None,
+ "calendar_year": None,
+ "calendar_period": None,
+ "provider": "intrinio",
+ }
+ )
+ ],
+)
+@pytest.mark.integration
+def test_equity_estimates_forward_sales(params, obb):
+ result = obb.equity.estimates.forward_sales(**params)
+ assert result
+ assert isinstance(result, OBBject)
+ assert len(result.results) > 0
+
+
+@parametrize(
+ "params",
+ [
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "fiscal_period": "fy",
+ "fiscal_year": None,
+ "calendar_year": None,
+ "calendar_period": None,
+ "provider": "intrinio",
+ }
+ ),
+ (
+ {
+ "symbol": "AAPL,MSFT",
+ "fiscal_period": "annual",
+ "limit": None,
+ "include_historical": False,
+ "provider": "fmp",
+ }
+ ),
+ ],
+)
+@pytest.mark.integration
+def test_equity_estimates_forward_eps(params, obb):
+ result = obb.equity.estimates.forward_eps(**params)
+ assert result
+ assert isinstance(result, OBBject)
+ assert len(result.results) > 0
+
+
+@parametrize(
+ "params",
+ [
({"symbol": "AAPL", "period": "annual", "limit": 12, "provider": "fmp"}),
(
{
diff --git a/openbb_platform/extensions/equity/openbb_equity/estimates/estimates_router.py b/openbb_platform/extensions/equity/openbb_equity/estimates/estimates_router.py
index 07c328c334c..d321c3a9d31 100644
--- a/openbb_platform/extensions/equity/openbb_equity/estimates/estimates_router.py
+++ b/openbb_platform/extensions/equity/openbb_equity/estimates/estimates_router.py
@@ -91,3 +91,49 @@ async def analyst_search(
) -> OBBject:
"""Search for specific analysts and get their forecast track record."""
return await OBBject.from_query(Query(**locals()))
+
+
+@router.command(
+ model="ForwardSalesEstimates",
+ examples=[
+ APIEx(parameters={"symbol": "AAPL", "provider": "intrinio"}),
+ APIEx(
+ parameters={
+ "fiscal_year": 2025,
+ "fiscal_period": "fy",
+ "provider": "intrinio",
+ }
+ ),
+ ],
+)
+async def forward_sales(
+ cc: CommandContext,
+ provider_choices: ProviderChoices,
+ standard_params: StandardParams,
+ extra_params: ExtraParams,
+) -> OBBject:
+ """Get forward sales estimates."""
+ return await OBBject.from_query(Query(**locals()))
+
+
+@router.command(
+ model="ForwardEpsEstimates",
+ examples=[
+ APIEx(parameters={"symbol": "AAPL", "provider": "intrinio"}),
+ APIEx(
+ parameters={
+ "fiscal_year": 2025,
+ "fiscal_period": "fy",
+ "provider": "intrinio",
+ }
+ ),
+ ],
+)
+async def forward_eps(
+ cc: CommandContext,
+ provider_choices: ProviderChoices,
+ standard_params: StandardParams,
+ extra_params: ExtraParams,
+) -> OBBject:
+ """Get forward EPS estimates."""
+ return await OBBject.from_query(Query(**locals()))
diff --git a/openbb_platform/openbb/package/equity_estimates.py b/openbb_platform/openbb/package/equity_estimates.py
index 6a5ef3fa944..9c707ff72c7 100644
--- a/openbb_platform/openbb/package/equity_estimates.py
+++ b/openbb_platform/openbb/package/equity_estimates.py
@@ -14,6 +14,8 @@ class ROUTER_equity_estimates(Container):
"""/equity/estimates
analyst_search
consensus
+ forward_eps
+ forward_sales
historical
price_target
"""
@@ -210,13 +212,13 @@ class ROUTER_equity_estimates(Container):
def consensus(
self,
symbol: Annotated[
- Union[str, List[str]],
+ Union[str, None, List[Optional[str]]],
OpenBBCustomParameter(
- description="Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp, yfinance."
+ description="Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp, intrinio, yfinance."
),
- ],
+ ] = None,
provider: Annotated[
- Optional[Literal["fmp", "yfinance"]],
+ Optional[Literal["fmp", "intrinio", "yfinance"]],
OpenBBCustomParameter(
description="The provider to use for the query, by default None.\n If None, the provider specified in defaults is selected or 'fmp' if there is\n no default."
),
@@ -227,19 +229,21 @@ class ROUTER_equity_estimates(Container):
Parameters
----------
- symbol : Union[str, List[str]]
- Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp, yfinance.
- provider : Optional[Literal['fmp', 'yfinance']]
+ symbol : Union[str, None, List[Optional[str]]]
+ Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp, intrinio, yfinance.
+ provider : Optional[Literal['fmp', 'intrinio', 'yfinance']]
The provider to use for the query, by default None.
If None, the provider specified in defaults is selected or 'fmp' if there is
no default.
+ industry_group_number : Optional[int]
+ The Zacks industry group number. (provider: intrinio)
Returns
-------
OBBject
results : List[PriceTargetConsensus]
Serializable results.
- provider : Optional[Literal['fmp', 'yfinance']]
+ provider : Optional[Literal['fmp', 'intrinio', 'yfinance']]
Provider name.
warnings : Optional[List[Warning_]]
List of warnings.
@@ -252,6 +256,8 @@ class ROUTER_equity_estimates(Container):
--------------------
symbol : str
Symbol representing the entity requested in the data.
+ name : Optional[str]
+ The company name
target_high : Optional[float]
High target of the price target consensus.
target_low : Optional[float]
@@ -260,6 +266,18 @@ class ROUTER_equity_estimates(Container):
Consensus target of the price target consensus.
target_median : Optional[float]
Median target of the price target consensus.
+ standard_deviation : Optional[float]
+ The standard deviation of target price estimates. (provider: intrinio)
+ total_anaylsts : Optional[int]
+ The total number of target price estimates in consensus. (provider: intrinio)
+ raised : Optional[int]
+ The number of analysts that have raised their target price estimates. (provider: intrinio)
+ lowered : Optional[int]
+ The number of analysts that have lowered their target price estimates. (provider: intrinio)
+ most_recent_date : Optional[date]
+ The date of the most recent estimate. (provider: intrinio)
+ industry_group_number : Optional[int]
+ The Zacks industry group number. (provider: intrinio)
recommendation : Optional[str]
Recommendation - buy, sell, etc. (provider: yfinance)
recommendation_mean : Optional[float]
@@ -285,14 +303,260 @@ class ROUTER_equity_estimates(Container):
"provider": self._get_provider(
provider,
"/equity/estimates/consensus",
- ("fmp", "yfinance"),
+ ("fmp", "intrinio", "yfinance"),
+ )
+ },
+ standard_params={
+ "symbol": symbol,
+ },
+ extra_params=kwargs,
+ info={
+ "symbol": {
+ "multiple_items_allowed": ["fmp", "intrinio", "yfinance"]
+ }
+ },
+ )
+ )
+
+ @exception_handler
+ @validate
+ def forward_eps(
+ self,
+ symbol: Annotated[
+ Union[str, None, List[Optional[str]]],
+ OpenBBCustomParameter(
+ description="Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp, intrinio."
+ ),
+ ] = None,
+ provider: Annotated[
+ Optional[Literal["fmp", "intrinio"]],
+ OpenBBCustomParameter(
+ description="The provider to use for the query, by default None.\n If None, the provider specified in defaults is selected or 'fmp' if there is\n no default."
+ ),
+ ] = None,
+ **kwargs
+ ) -> OBBject:
+ """Get forward EPS estimates.
+
+ Parameters
+ ----------
+ symbol : Union[str, None, List[Optional[str]]]
+ Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp, intrinio.
+ provider : Optional[Literal['fmp', 'intrinio']]
+ The provider to use for the query, by default None.
+ If None, the provider specified in defaults is selected or 'fmp' if there is
+ no default.
+ fiscal_period : Optional[Union[Literal['annual', 'quarter'], Literal['fy', 'q1', 'q2', 'q3', 'q4']]]
+ The future fiscal period to retrieve estimates for. (provider: fmp, intrinio)
+ limit : Optional[int]
+ The number of data entries to return. (provider: fmp)
+ include_historical : bool
+ If True, the data will include all past data and the limit will be ignored. (provider: fmp)
+ fiscal_year : Optional[int]
+ The future fiscal year to retrieve estimates for. When no symbol and year is supplied the current calendar year is used. (provider: intrinio)
+ calendar_year : Optio