Skip to content

Commit

Permalink
Remove redundant arg 'quoteType', improve RST
Browse files Browse the repository at this point in the history
  • Loading branch information
ValueRaider committed Jan 9, 2025
1 parent dddceec commit 453651a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 39 deletions.
82 changes: 50 additions & 32 deletions yfinance/screener/screener.py
Original file line number Diff line number Diff line change
@@ -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_
Expand All @@ -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
Expand All @@ -79,7 +93,6 @@ def screen(query: Union[str, QueryBase],
])
response = yf.screen(q, sortField = 'percentchange', sortAsc = True)
Predefined Screeners
{predefined_screeners}
"""

Expand All @@ -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()
Expand All @@ -100,14 +112,20 @@ 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)}"')

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"}
Expand Down
16 changes: 9 additions & 7 deletions yfinance/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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]
Expand Down

0 comments on commit 453651a

Please sign in to comment.