From 60171359ba47f92b54366f6873d67e3cc2756c3d Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Sat, 20 Jan 2024 16:26:16 -0800 Subject: price target consensus --- .../equity/integration/test_equity_api.py | 5 +- .../equity/integration/test_equity_python.py | 5 +- .../providers/yfinance/openbb_yfinance/__init__.py | 4 + .../openbb_yfinance/models/equity_quote.py | 8 +- .../models/price_target_consensus.py | 114 +++++++++++++++++++++ ...t_y_finance_price_target_consensus_fetcher.yaml | 108 +++++++++++++++++++ .../yfinance/tests/test_yfinance_fetchers.py | 12 +++ 7 files changed, 250 insertions(+), 6 deletions(-) create mode 100644 openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py create mode 100644 openbb_platform/providers/yfinance/tests/record/http/test_yfinance_fetchers/test_y_finance_price_target_consensus_fetcher.yaml diff --git a/openbb_platform/extensions/equity/integration/test_equity_api.py b/openbb_platform/extensions/equity/integration/test_equity_api.py index cef2762a0d7..595bb047616 100644 --- a/openbb_platform/extensions/equity/integration/test_equity_api.py +++ b/openbb_platform/extensions/equity/integration/test_equity_api.py @@ -640,7 +640,10 @@ def test_equity_estimates_price_target(params, headers): @parametrize( "params", - [({"symbol": "AAPL"})], + [ + ({"symbol": "AAPL", "provider": "fmp"}), + ({"symbol": "AAPL,AMZN,RELIANCE.NS", "provider": "yfinance"}), + ], ) @pytest.mark.integration def test_equity_estimates_consensus(params, headers): diff --git a/openbb_platform/extensions/equity/integration/test_equity_python.py b/openbb_platform/extensions/equity/integration/test_equity_python.py index dee4e7ffa69..68572f01fa8 100644 --- a/openbb_platform/extensions/equity/integration/test_equity_python.py +++ b/openbb_platform/extensions/equity/integration/test_equity_python.py @@ -598,7 +598,10 @@ def test_equity_estimates_price_target(params, obb): @parametrize( "params", - [({"symbol": "AAPL"})], + [ + ({"symbol": "AAPL", "provider": "fmp"}), + ({"symbol": "AAPL,AMZN,RELIANCE.NS", "provider": "yfinance"}), + ], ) @pytest.mark.integration def test_equity_estimates_consensus(params, obb): diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py b/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py index 5c7853b1603..7a442117fe9 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/__init__.py @@ -26,6 +26,9 @@ from openbb_yfinance.models.losers import YFLosersFetcher from openbb_yfinance.models.market_indices import ( YFinanceMarketIndicesFetcher, ) +from openbb_yfinance.models.price_target_consensus import ( + YFinancePriceTargetConsensusFetcher, +) from openbb_yfinance.models.undervalued_growth_equities import ( YFUndervaluedGrowthEquitiesFetcher, ) @@ -60,5 +63,6 @@ yfinance_provider = Provider( "IncomeStatement": YFinanceIncomeStatementFetcher, "IndexHistorical": YFinanceIndexHistoricalFetcher, "MarketIndices": YFinanceMarketIndicesFetcher, + "PriceTargetConsensus": YFinancePriceTargetConsensusFetcher, }, ) diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py b/openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py index d9293520fe4..9632e24aea8 100644 --- a/openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py +++ b/openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py @@ -1,4 +1,4 @@ -"""YFinance Equity Quote fetcher""" +"""YFinance Equity Quote Model""" # pylint: disable=unused-argument import asyncio import warnings @@ -16,11 +16,11 @@ _warn = warnings.warn class YFinanceEquityQuoteQueryParams(EquityQuoteQueryParams): - """YFinance Equity Profile query params.""" + """YFinance Equity Quote query params.""" class YFinanceEquityQuoteData(EquityQuoteData): - """YFinance Equity Profile Data.""" + """YFinance Equity Quote Data.""" __alias_dict__ = { "name": "longName", @@ -62,7 +62,7 @@ class YFinanceEquityQuoteData(EquityQuoteData): class YFinanceEquityQuoteFetcher( Fetcher[YFinanceEquityQuoteQueryParams, List[YFinanceEquityQuoteData]] ): - """YFinance Equity Profile fetcher.""" + """YFinance Equity Quote fetcher.""" @staticmethod def transform_query(params: Dict[str, Any]) -> YFinanceEquityQuoteQueryParams: diff --git a/openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py b/openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py new file mode 100644 index 00000000000..5f065c56f62 --- /dev/null +++ b/openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py @@ -0,0 +1,114 @@ +"""YFinance Price Target Consensus Model""" +# pylint: disable=unused-argument +import asyncio +import warnings +from typing import Any, Dict, List, Optional + +from openbb_core.provider.abstract.fetcher import Fetcher +from openbb_core.provider.standard_models.price_target_consensus import ( + PriceTargetConsensusData, + PriceTargetConsensusQueryParams, +) +from pydantic import Field +from yfinance import Ticker + +_warn = warnings.warn + + +class YFinancePriceTargetConsensusQueryParams(PriceTargetConsensusQueryParams): + """YFinance Price Target Consensus query params.""" + + +class YFinancePriceTargetConsensusData(PriceTargetConsensusData): + """YFinance Price Target Consensus Data.""" + + __alias_dict__ = { + "target_high": "targetHighPrice", + "target_low": "targetLowPrice", + "target_consensus": "targetMeanPrice", + "target_median": "targetMedianPrice", + "recommendation": "recommendationKey", + "recommendation_mean": "recommendationMean", + "number_of_analysts": "numberOfAnalystOpinions", + "current_price": "currentPrice", + } + + recommendation: Optional[str] = Field( + default=None, + description="Recommendation - buy, sell, etc.", + ) + recommendation_mean: Optional[float] = Field( + default=None, + description="Mean recommendation score where 1 is strong buy and 5 is strong sell.", + ) + current_price: Optional[float] = Field( + default=None, + description="Current price of the stock.", + ) + + +class YFinancePriceTargetConsensusFetcher( + Fetcher[ + YFinancePriceTargetConsensusQueryParams, List[YFinancePriceTargetConsensusData] + ] +): + """YFinance Price Target Consensus fetcher.""" + + @staticmethod + def transform_query( + params: Dict[str, Any] + ) -> YFinancePriceTargetConsensusQueryParams: + """Transform the query.""" + return YFinancePriceTargetConsensusQueryParams(**params) + + @staticmethod + async def aextract_data( + query: YFinancePriceTargetConsensusQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[Dict]: + """Extract the raw data from YFinance""" + + symbols = query.symbol.split(",") + results = [] + fields = [ + "symbol", + "currentPrice", + "targetHighPrice", + "targetLowPrice", + "targetMeanPrice", + "targetMedianPrice", + "recommendationMean", + "recommendationKey", + "numberOfAnalystOpinions", + ] + + async def get_one(symbol): + """Get the data for one ticker symbol.""" + result = {} + ticker = {} + try: + ticker = Ticker(symbol).get_info() + except Exception as e: + _warn(f"Error getting data for {symbol}: {e}") + if ticker: + for field in fields: + if field in ticker: + result[field] = ticker.get(field, None) + if result: + results.append(result) + + tasks = [get_one(symbol) for symbol in symbols] + + await asyncio.gather(*tasks) + + return results + + @staticmethod + def transform_data( + query: YFinancePriceTargetConsensusQueryParams, + data: List[Dict], + **kwargs: Any, + ) -> List[YFinancePriceTargetConsensusData]: + """Transform the data.""" + return [YFinancePriceTargetConsensusData.model_validate(d) for d in data] diff --git a/openbb_platform/providers/yfinance/tests/record/http/test_yfinance_fetchers/test_y_finance_price_target_consensus_fetcher.yaml b/openbb_platform/providers/yfinance/tests/record/http/test_yfinance_fetchers/test_y_finance_price_target_consensus_fetcher.yaml new file mode 100644 index 00000000000..86ba01594f2 --- /dev/null +++ b/openbb_platform/providers/yfinance/tests/record/http/test_yfinance_fetchers/test_y_finance_price_target_consensus_fetcher.yaml @@ -0,0 +1,108 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + Cookie: + - MOCK_COOKIE + method: GET + uri: https://query2.finance.yahoo.com/v10/finance/quoteSummary/AAPL?corsDomain=finance.yahoo.com&crumb=MOCK_CRUMB&formatted=false&modules=financialData%2CquoteType%2CdefaultKeyStatistics%2CassetProfile%2CsummaryDetail&symbol=AAPL + response: + body: + string: !!binary | + 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 + Al++/BuKRBv1sh4AAA== + headers: + Age: + - '1' + Connection: + - keep-alive + Expect-CT: + - max-age=31536000, report-uri="http://csp.yahoo.com/beacon/csp?src=yahoocom-expect-ct-report-only" + Referrer-Policy: + - no-referrer-when-downgrade + Strict-Transport-Security: + - max-age=31536000 + X-Content-Type-Options: + - nosniff + X-XSS-Protection: + - 1; mode=block + cache-control: + - public, max-age=1, stale-while-revalidate=9 + content-encoding: + - gzip + content-length: + - '3034' + content-type: + - application/json;charset=utf-8 + date: + - Sun, 21 Jan 2024 00:25:54 GMT + server: + - ATS + vary: + - Origin,Accept-Encoding + x-envoy-decorator-operation: + - finance-company-fundamentals-api--mtls-production-gq1.finance-k8s.svc.yahoo.local:4080/* + x-envoy-upstream-service-time: + - '4' + status: + code: 200 + message: OK +version: 1 diff --git a/openbb_platform/providers/yfinance/tests/test_yfinance_fetchers.py b/openbb_platform/providers/yfinance/tests/test_yfinance_fetchers.py index 80a809c99e1..0be7b538da6 100644 --- a/openbb_platform/providers/yfinance/tests/test_yfinance_fetchers.py +++ b/openbb_platform/providers/yfinance/tests/test_yfinance_fetchers.py @@ -26,6 +26,9 @@ from openbb_yfinance.models.losers import YFLosersFetcher from openbb_yfinance.models.market_indices import ( YFinanceMarketIndicesFetcher, ) +from openbb_yfinance.models.price_target_consensus import ( + YFinancePriceTargetConsensusFetcher, +) from openbb_yfinance.models.undervalued_growth_equities import ( YFUndervaluedGrowthEquitiesFetcher, ) @@ -273,3 +276,12 @@ def test_y_finance_equity_quote_fetcher(credentials=test_credentials): fetcher = YFinanceEquityQuoteFetcher() result = fetcher.test(params, credentials) assert result is None + + +@pytest.mark.record_http +def test_y_finance_price_target_consensus_fetcher(credentials=test_credentials): + params = {"symbol": "AAPL"} + + fetcher = YFinancePriceTargetConsensusFetcher() + result = fetcher.test(params, credentials) + assert result is None -- cgit v1.2.3