From 453651ac5fe5b40837ac24cd1503525c908ee5d3 Mon Sep 17 00:00:00 2001 From: ValueRaider Date: Thu, 9 Jan 2025 21:18:41 +0000 Subject: [PATCH] Remove redundant arg 'quoteType', improve RST --- yfinance/screener/screener.py | 82 +++++++++++++++++++++-------------- yfinance/utils.py | 16 ++++--- 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/yfinance/screener/screener.py b/yfinance/screener/screener.py index 3e315af9..dbadc0cf 100644 --- a/yfinance/screener/screener.py +++ b/yfinance/screener/screener.py @@ -1,5 +1,5 @@ -from .query import EquityQuery as EqyOp -from .query import FundQuery as FndOp +from .query import EquityQuery as EqyQy +from .query import FundQuery as FndQy from .query import QueryBase from yfinance.const import _BASE_URL_ @@ -16,52 +16,66 @@ } PREDEFINED_SCREENER_BODY_MAP = { - 'aggressive_small_caps': {"sortField":"eodvolume", "sortType":"desc", "quoteType":"equity", - "query": EqyOp('and', [EqyOp('is-in', ['exchange', 'NMS', 'NYQ']), EqyOp('lt', ["epsgrowth.lasttwelvemonths", 15])])}, + 'aggressive_small_caps': {"sortField":"eodvolume", "sortType":"desc", + "query": EqyQy('and', [EqyQy('is-in', ['exchange', 'NMS', 'NYQ']), EqyQy('lt', ["epsgrowth.lasttwelvemonths", 15])])}, 'day_gainers': {"sortField":"percentchange", "sortType":"DESC", "quoteType":"EQUITY", - "query": EqyOp('and', [EqyOp('gt', ['percentchange', 3]), EqyOp('eq', ['region', 'us']), EqyOp('gte', ['intradaymarketcap', 2000000000]), EqyOp('gte', ['intradayprice', 5]), EqyOp('gt', ['dayvolume', 15000])])}, + "query": EqyQy('and', [EqyQy('gt', ['percentchange', 3]), EqyQy('eq', ['region', 'us']), EqyQy('gte', ['intradaymarketcap', 2000000000]), EqyQy('gte', ['intradayprice', 5]), EqyQy('gt', ['dayvolume', 15000])])}, 'day_losers': {"sortField":"percentchange", "sortType":"ASC", "quoteType":"EQUITY", - "query": EqyOp('and', [EqyOp('lt', ['percentchange', -2.5]), EqyOp('eq', ['region', 'us']), EqyOp('gte', ['intradaymarketcap', 2000000000]), EqyOp('gte', ['intradayprice', 5]), EqyOp('gt', ['dayvolume', 20000])])}, - 'growth_technology_stocks': {"sortField":"eodvolume", "sortType":"desc", "quoteType":"equity", - "query": EqyOp('and', [EqyOp('gte', ['quarterlyrevenuegrowth.quarterly', 25]), EqyOp('gte', ['epsgrowth.lasttwelvemonths', 25]), EqyOp('eq', ['sector', 'Technology']), EqyOp('is-in', ['exchange', 'NMS', 'NYQ'])])}, + "query": EqyQy('and', [EqyQy('lt', ['percentchange', -2.5]), EqyQy('eq', ['region', 'us']), EqyQy('gte', ['intradaymarketcap', 2000000000]), EqyQy('gte', ['intradayprice', 5]), EqyQy('gt', ['dayvolume', 20000])])}, + 'growth_technology_stocks': {"sortField":"eodvolume", "sortType":"desc", + "query": EqyQy('and', [EqyQy('gte', ['quarterlyrevenuegrowth.quarterly', 25]), EqyQy('gte', ['epsgrowth.lasttwelvemonths', 25]), EqyQy('eq', ['sector', 'Technology']), EqyQy('is-in', ['exchange', 'NMS', 'NYQ'])])}, 'most_actives': {"sortField":"dayvolume", "sortType":"DESC", "quoteType":"EQUITY", - "query": EqyOp('and', [EqyOp('eq', ['region', 'us']), EqyOp('gte', ['intradaymarketcap', 2000000000]), EqyOp('gt', ['dayvolume', 5000000])])}, + "query": EqyQy('and', [EqyQy('eq', ['region', 'us']), EqyQy('gte', ['intradaymarketcap', 2000000000]), EqyQy('gt', ['dayvolume', 5000000])])}, 'most_shorted_stocks': {"size":25, "offset":0, "sortField":"short_percentage_of_shares_outstanding.value", "sortType":"DESC", "quoteType":"EQUITY", - "query": EqyOp('and', [EqyOp('eq', ['region', 'us']), EqyOp('gt', ['intradayprice', 1]), EqyOp('gt', ['avgdailyvol3m', 200000])])}, - 'small_cap_gainers': {"sortField":"eodvolume", "sortType":"desc", "quoteType":"equity", + "query": EqyQy('and', [EqyQy('eq', ['region', 'us']), EqyQy('gt', ['intradayprice', 1]), EqyQy('gt', ['avgdailyvol3m', 200000])])}, + 'small_cap_gainers': {"sortField":"eodvolume", "sortType":"desc", "query":{"operator":"and", "operands":[{"operator":"lt", "operands":["intradaymarketcap",2000000000]},{"operator":"or", "operands":[{"operator":"eq", "operands":["exchange", "NMS"]},{"operator":"eq", "operands":["exchange", "NYQ"]}]}]}}, 'undervalued_growth_stocks': {"sortType":"DESC", "sortField":"eodvolume", "quoteType":"EQUITY", - "query": EqyOp('and', [EqyOp('btwn', ['peratio.lasttwelvemonths', 0, 20]), EqyOp('lt', ['pegratio_5y', 1]), EqyOp('gte', ['epsgrowth.lasttwelvemonths', 25]), EqyOp('is-in', ['exchange', 'NMS', 'NYQ'])])}, - 'undervalued_large_caps': {"sortField":"eodvolume", "sortType":"desc", "quoteType":"equity", - "query": EqyOp('and', [EqyOp('btwn', ['peratio.lasttwelvemonths', 0, 20]), EqyOp('lt', ['pegratio_5y', 1]), EqyOp('btwn', ['intradaymarketcap', 10000000000, 100000000000]), EqyOp('is-in', ['exchange', 'NMS', 'NYQ'])])}, - 'conservative_foreign_funds': {"sortType":"DESC", "sortField":"fundnetassets", "quoteType":"MUTUALFUND", - "query": FndOp('and', [FndOp('is-in', ['categoryname', 'Foreign Large Value', 'Foreign Large Blend', 'Foreign Large Growth', 'Foreign Small/Mid Growth', 'Foreign Small/Mid Blend', 'Foreign Small/Mid Value']), FndOp('is-in', ['performanceratingoverall', 4, 5]), FndOp('lt', ['initialinvestment', 100001]), FndOp('lt', ['annualreturnnavy1categoryrank', 50]), FndOp('is-in', ['riskratingoverall', 1, 2, 3]), FndOp('eq', ['exchange', 'NAS'])])}, - 'high_yield_bond': {"sortType":"DESC", "sortField":"fundnetassets", "quoteType":"MUTUALFUND", - "query": FndOp('and', [FndOp('is-in', ['performanceratingoverall', 4, 5]), FndOp('lt', ['initialinvestment', 100001]), FndOp('lt', ['annualreturnnavy1categoryrank', 50]), FndOp('is-in', ['riskratingoverall', 1, 2, 3]), FndOp('eq', ['categoryname', 'High Yield Bond']), FndOp('eq', ['exchange', 'NAS'])])}, - 'portfolio_anchors': {"sortType":"DESC", "sortField":"fundnetassets", "quoteType":"MUTUALFUND", - "query": FndOp('and', [FndOp('eq', ['categoryname', 'Large Blend']), FndOp('is-in', ['performanceratingoverall', 4, 5]), FndOp('lt', ['initialinvestment', 100001]), FndOp('lt', ['annualreturnnavy1categoryrank', 50]), FndOp('eq', ['exchange', 'NAS'])])}, - 'solid_large_growth_funds': {"sortType":"DESC", "sortField":"fundnetassets", "quoteType":"MUTUALFUND", - "query": FndOp('and', [FndOp('eq', ['categoryname', 'Large Growth']), FndOp('is-in', ['performanceratingoverall', 4, 5]), FndOp('lt', ['initialinvestment', 100001]), FndOp('lt', ['annualreturnnavy1categoryrank', 50]), FndOp('eq', ['exchange', 'NAS'])])}, - 'solid_midcap_growth_funds': {"sortType":"DESC", "sortField":"fundnetassets", "quoteType":"MUTUALFUND", - "query": FndOp('and', [FndOp('eq', ['categoryname', 'Mid-Cap Growth']), FndOp('is-in', ['performanceratingoverall', 4, 5]), FndOp('lt', ['initialinvestment', 100001]), FndOp('lt', ['annualreturnnavy1categoryrank', 50]), FndOp('eq', ['exchange', 'NAS'])])}, - 'top_mutual_funds': {"sortType":"DESC", "sortField":"percentchange", "quoteType":"MUTUALFUND", - "query": FndOp('and', [FndOp('gt', ['intradayprice', 15]), FndOp('is-in', ['performanceratingoverall', 4, 5]), FndOp('gt', ['initialinvestment', 1000]), FndOp('eq', ['exchange', 'NAS'])])} + "query": EqyQy('and', [EqyQy('btwn', ['peratio.lasttwelvemonths', 0, 20]), EqyQy('lt', ['pegratio_5y', 1]), EqyQy('gte', ['epsgrowth.lasttwelvemonths', 25]), EqyQy('is-in', ['exchange', 'NMS', 'NYQ'])])}, + 'undervalued_large_caps': {"sortField":"eodvolume", "sortType":"desc", + "query": EqyQy('and', [EqyQy('btwn', ['peratio.lasttwelvemonths', 0, 20]), EqyQy('lt', ['pegratio_5y', 1]), EqyQy('btwn', ['intradaymarketcap', 10000000000, 100000000000]), EqyQy('is-in', ['exchange', 'NMS', 'NYQ'])])}, + 'conservative_foreign_funds': {"sortType":"DESC", "sortField":"fundnetassets", + "query": FndQy('and', [FndQy('is-in', ['categoryname', 'Foreign Large Value', 'Foreign Large Blend', 'Foreign Large Growth', 'Foreign Small/Mid Growth', 'Foreign Small/Mid Blend', 'Foreign Small/Mid Value']), FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('lt', ['initialinvestment', 100001]), FndQy('lt', ['annualreturnnavy1categoryrank', 50]), FndQy('is-in', ['riskratingoverall', 1, 2, 3]), FndQy('eq', ['exchange', 'NAS'])])}, + 'high_yield_bond': {"sortType":"DESC", "sortField":"fundnetassets", + "query": FndQy('and', [FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('lt', ['initialinvestment', 100001]), FndQy('lt', ['annualreturnnavy1categoryrank', 50]), FndQy('is-in', ['riskratingoverall', 1, 2, 3]), FndQy('eq', ['categoryname', 'High Yield Bond']), FndQy('eq', ['exchange', 'NAS'])])}, + 'portfolio_anchors': {"sortType":"DESC", "sortField":"fundnetassets", + "query": FndQy('and', [FndQy('eq', ['categoryname', 'Large Blend']), FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('lt', ['initialinvestment', 100001]), FndQy('lt', ['annualreturnnavy1categoryrank', 50]), FndQy('eq', ['exchange', 'NAS'])])}, + 'solid_large_growth_funds': {"sortType":"DESC", "sortField":"fundnetassets", + "query": FndQy('and', [FndQy('eq', ['categoryname', 'Large Growth']), FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('lt', ['initialinvestment', 100001]), FndQy('lt', ['annualreturnnavy1categoryrank', 50]), FndQy('eq', ['exchange', 'NAS'])])}, + 'solid_midcap_growth_funds': {"sortType":"DESC", "sortField":"fundnetassets", + "query": FndQy('and', [FndQy('eq', ['categoryname', 'Mid-Cap Growth']), FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('lt', ['initialinvestment', 100001]), FndQy('lt', ['annualreturnnavy1categoryrank', 50]), FndQy('eq', ['exchange', 'NAS'])])}, + 'top_mutual_funds': {"sortType":"DESC", "sortField":"percentchange", + "query": FndQy('and', [FndQy('gt', ['intradayprice', 15]), FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('gt', ['initialinvestment', 1000]), FndQy('eq', ['exchange', 'NAS'])])} } -@dynamic_docstring({"predefined_screeners": generate_list_table_from_dict_of_dict(PREDEFINED_SCREENER_BODY_MAP,bullets=False)}) +@dynamic_docstring({"predefined_screeners": generate_list_table_from_dict_of_dict(PREDEFINED_SCREENER_BODY_MAP, bullets=False, title='Predefined queries')}) def screen(query: Union[str, QueryBase], offset: int = 0, size: int = 25, sortField: str = "ticker", - # sortType: str = "desc", sortAsc: bool = False, - quoteType: str = "equity", userId: str = "", userIdType: str = "guid", session = None, proxy = None): """ Run a screen: predefined query, or custom query + :Parameters: + query : str | Query: + The query to execute, either name of predefined or custom query + offset : int + The offset for the results. Default 0. + size : int + number of results to return. Default 100, maximum 250 (Yahoo) + sortField : str + field to sort by. Default "ticker" + sortAsc : bool + Sort ascending? Default False + userId : str + The user ID. Default empty. + userIdType : str + Type of user ID (e.g., "guid"). Default "guid". + Example: predefined query .. code-block:: python @@ -79,7 +93,6 @@ def screen(query: Union[str, QueryBase], ]) response = yf.screen(q, sortField = 'percentchange', sortAsc = True) - Predefined Screeners {predefined_screeners} """ @@ -88,7 +101,6 @@ def screen(query: Union[str, QueryBase], if query not in PREDEFINED_SCREENER_BODY_MAP.keys(): raise ValueError(f'Invalid key {query} provided for predefined screener') post_query = PREDEFINED_SCREENER_BODY_MAP[query] - post_query['query'] = post_query['query'].to_dict() elif isinstance(query, QueryBase): args = locals() @@ -100,7 +112,7 @@ def screen(query: Union[str, QueryBase], del args['sortAsc'] post_query = args - post_query['query'] = query.to_dict() + post_query['query'] = query else: raise ValueError(f'Query must be type str or QueryBase, not "{type(query)}"') @@ -108,6 +120,12 @@ def screen(query: Union[str, QueryBase], if query is None: raise ValueError('No query provided') + if isinstance(post_query['query'], EqyQy): + post_query['quoteType'] = 'EQUITY' + elif isinstance(post_query['query'], FndQy): + post_query['quoteType'] = 'MUTUALFUND' + post_query['query'] = post_query['query'].to_dict() + # Fetch _data = YfData(session=session) params_dict = {"corsDomain": "finance.yahoo.com", "formatted": "false", "lang": "en-US", "region": "US"} diff --git a/yfinance/utils.py b/yfinance/utils.py index d155b7ff..7193257c 100644 --- a/yfinance/utils.py +++ b/yfinance/utils.py @@ -943,10 +943,12 @@ def decorator(func): return func return decorator -def _generate_table_configurations() -> str: +def _generate_table_configurations(title = None) -> str: import textwrap - table = textwrap.dedent(""" - .. list-table:: Permitted Keys/Values + if title is None: + title = "Permitted Keys/Values" + table = textwrap.dedent(f""" + .. list-table:: {title} :widths: 25 75 :header-rows: 1 @@ -956,11 +958,11 @@ def _generate_table_configurations() -> str: return table -def generate_list_table_from_dict(data: dict, bullets: bool=True) -> str: +def generate_list_table_from_dict(data: dict, bullets: bool=True, title: str=None) -> str: """ Generate a list-table for the docstring showing permitted keys/values. """ - table = _generate_table_configurations() + table = _generate_table_configurations(title) # for k, values in data.items(): for k in sorted(data.keys()): # Sort keys by total string length of values. @@ -981,11 +983,11 @@ def generate_list_table_from_dict(data: dict, bullets: bool=True) -> str: table += ' '*5 + f"- {value_str}\n" return table -def generate_list_table_from_dict_of_dict(data: dict, bullets: bool=True) -> str: +def generate_list_table_from_dict_of_dict(data: dict, bullets: bool=True, title: str=None) -> str: """ Generate a list-table for the docstring showing permitted keys/values. """ - table = _generate_table_configurations() + table = _generate_table_configurations(title) # for k, values in data.items(): for k in sorted(data.keys()): values = data[k]