Add ETFQuery

This commit is contained in:
Ethan Balakumar
2026-04-14 22:19:02 -05:00
parent 1aac9aea29
commit 6a1e9d1e86
7 changed files with 439 additions and 8 deletions
+1
View File
@@ -26,6 +26,7 @@ The following are the publicly available classes, and functions exposed by the `
- :attr:`Industry <yfinance.Industry>`: Domain class for accessing industry information.
- :attr:`EquityQuery <yfinance.EquityQuery>`: Class to build equity query filters.
- :attr:`FundQuery <yfinance.FundQuery>`: Class to build fund query filters.
- :attr:`ETFQuery <yfinance.ETFQuery>`: Class to build ETF query filters.
- :attr:`screen <yfinance.screen>`: Run equity/fund queries.
- :attr:`enable_debug_mode <yfinance.enable_debug_mode>`: Function to enable debug mode for logging.
- :attr:`set_tz_cache_location <yfinance.set_tz_cache_location>`: Function to set the timezone cache location.
@@ -13,6 +13,7 @@ The `Sector` and `Industry` modules allow you to access the sector and industry
EquityQuery
FundQuery
ETFQuery
screen
.. seealso::
@@ -24,4 +25,8 @@ The `Sector` and `Industry` modules allow you to access the sector and industry
supported operand values for query
:attr:`FundQuery.valid_values <yfinance.FundQuery.valid_values>`
supported `EQ query operand parameters`
:attr:`ETFQuery.valid_fields <yfinance.ETFQuery.valid_fields>`
supported operand values for query
:attr:`ETFQuery.valid_values <yfinance.ETFQuery.valid_values>`
supported `EQ query operand parameters`
+2 -2
View File
@@ -34,7 +34,7 @@ from .domain.industry import Industry
from .domain.market import Market
from .config import YfConfig as config
from .screener.query import EquityQuery, FundQuery
from .screener.query import EquityQuery, FundQuery, ETFQuery
from .screener.screener import screen, PREDEFINED_SCREENER_QUERIES
__version__ = version.version
@@ -45,7 +45,7 @@ warnings.filterwarnings('default', category=DeprecationWarning, module='^yfinanc
__all__ = ['download', 'Market', 'Search', 'Lookup', 'Ticker', 'Tickers', 'enable_debug_mode', 'set_tz_cache_location', 'Sector', 'Industry', 'WebSocket', 'AsyncWebSocket', 'Calendars']
# screener stuff:
__all__ += ['EquityQuery', 'FundQuery', 'screen', 'PREDEFINED_SCREENER_QUERIES']
__all__ += ['EquityQuery', 'FundQuery', 'ETFQuery', 'screen', 'PREDEFINED_SCREENER_QUERIES']
# Config stuff:
_NOTSET=object()
+375
View File
@@ -759,6 +759,381 @@ EQUITY_SCREENER_FIELDS = {
"highest_controversy"}
}
EQUITY_SCREENER_FIELDS = merge_two_level_dicts(EQUITY_SCREENER_FIELDS, COMMON_SCREENER_FIELDS)
ETF_SCREENER_EQ_MAP = {
"exchange": {
'ae': {'DFM'},
'ar': {'BUE'},
'at': {'VIE'},
'au': {'ASX', 'CXA'},
'be': {'BRU'},
'br': {'SAO'},
'ca': {'CNQ', 'NEO', 'TOR', 'VAN'},
'ch': {'EBS'},
'cl': {'SGO'},
'cn': {'SHH', 'SHZ'},
'co': {'BVC'},
'cz': {'PRA'},
'de': {'BER', 'DUS', 'EUX', 'FRA', 'HAM', 'HAN', 'GER', 'MUN', 'STU'},
'dk': {'CPH'},
'ee': {'TAL'},
'eg': {'CAI'},
'es': {'MAD', 'MCE'},
'fi': {'HEL'},
'fr': {'ENX', 'PAR'},
'gb': {'AQS', 'CXE', 'IOB', 'LSE'},
'gr': {'ATH'},
'hk': {'HKG'},
'hu': {'BUD'},
'id': {'JKT'},
'ie': {'ISE'},
'il': {'TLV'},
'in': {'BSE', 'NSI'},
'is': {'ICE'},
'it': {'MDD', 'MIL', 'TLO'},
'jp': {'FKA', 'JPX', 'OSA', 'SAP'},
'kr': {'KOE', 'KSC'},
'kw': {'KUW'},
'lk': {'CSE'},
'lt': {'LIT'},
'lv': {'RIS'},
'mx': {'MEX'},
'my': {'KLS'},
'nl': {'AMS', 'DXE'},
'no': {'OSL'},
'nz': {'NZE'},
'pe': {},
'ph': {'PHP', 'PHS'},
'pk': {'KAR'},
'pl': {'WSE'},
'pt': {'LIS'},
'qa': {'DOH'},
'ro': {'BVB'},
'ru': {'MCX'},
'sa': {'SAU'},
'se': {'STO'},
'sg': {'SES'},
'sr': {},
'th': {'SET'},
'tr': {'IST'},
'tw': {'TAI', 'TWO'},
'us': {'ASE', 'BTS', 'CXI', 'NAE', 'NCM', 'NGM', 'NMS', 'NYQ', 'OEM', 'OQB', 'OQX', 'PCX', 'PNK', 'YHD'},
've': {'CCS'},
'vn': {'VSE'},
'za': {'JNB'}
},
"categoryname": {
"Allocation--15% to 30% Equity",
"Allocation--30% to 50% Equity",
"Allocation--50% to 70% Equity",
"Allocation--70% to 85% Equity",
"Allocation--85%+ Equity",
"Bank Loan",
"Bear Market",
"China Region",
"Commodities Agriculture",
"Commodities Broad Basket",
"Convertibles",
"Corporate Bond",
"Diversified Emerging Mkts",
"Diversified Pacific/Asia",
"Emerging Markets Bond",
"Emerging-Markets Local-Currency Bond",
"Energy Limited Partnership",
"Equity Energy",
"Equity Precious Metals",
"Europe Stock",
"Financial",
"Foreign Large Blend",
"Foreign Large Growth",
"Foreign Large Value",
"Foreign Small/Mid Blend",
"Foreign Small/Mid Growth",
"Foreign Small/Mid Value",
"Global Real Estate",
"Health",
"High Yield Bond",
"High Yield Muni",
"Inflation-Protected Bond",
"Infrastructure",
"Intermediate Government",
"Intermediate-Term Bond",
"Japan Stock",
"Large Blend",
"Large Growth",
"Large Value",
"Long Government",
"Long-Short Credit",
"Long-Short Equity",
"Long-Term Bond",
"Managed Futures",
"Market Neutral",
"Mid-Cap Blend",
"Mid-Cap Growth",
"Mid-Cap Value",
"Miscellaneous Region",
"Multialternative",
"Multicurrency",
"Multisector Bond",
"Muni California Intermediate",
"Muni California Long",
"Muni Massachusetts",
"Muni Minnesota",
"Muni National Interm",
"Muni National Long",
"Muni National Short",
"Muni New Jersey",
"Muni New York Intermediate",
"Muni New York Long",
"Muni Ohio",
"Muni Pennsylvania",
"Muni Single State Interm",
"Muni Single State Long",
"Muni Single State Short",
"Natural Resources",
"Nontraditional Bond",
"Option Writing",
"Other",
"Other Allocation",
"Pacific/Asia ex-Japan Stk",
"Preferred Stock",
"Real Estate",
"Short Government",
"Short-Term Bond",
"Small Blend",
"Small Growth",
"Small Value",
"Tactical Allocation",
"Target-Date 2000-2010",
"Target-Date 2015",
"Target-Date 2020",
"Target-Date 2025",
"Target-Date 2030",
"Target-Date 2035",
"Target-Date 2040",
"Target-Date 2045",
"Target-Date 2050",
"Target-Date 2055",
"Target-Date 2060+",
"Target-Date Retirement",
"Technology",
"Trading - Leveraged/Inverse Commodities",
"Trading - Leveraged/Inverse Equity",
"Trading--Inverse Equity",
"Trading--Leveraged Equity",
"Ultrashort Bond",
"Utilities",
"World Allocation",
"World Bond",
"World Stock"
},
"fundfamilyname": {
"ALPS",
"AMG Funds",
"AQR Funds",
"Aberdeen",
"Alger",
"AllianceBernstein",
"Allianz Funds",
"American Beacon",
"American Century Investments",
"American Funds",
"Aquila",
"Artisan",
"BMO Funds",
"BNY Mellon Funds",
"Baird",
"Barclays Funds",
"Barings Funds",
"Baron Capital Group",
"BlackRock",
"Brown Advisory Funds",
"Calamos",
"Calvert Investments",
"Catalyst Mutual Funds",
"Cohen & Steers",
"Columbia",
"Commerz Funds Solutions SA",
"Commerzbank AG, Frankfurt am Main",
"Davis Funds",
"Delaware Investments",
"Deutsche Asset Management",
"Deutsche Bank AG",
"Diamond Hill Funds",
"Dimensional Fund Advisors",
"Direxion Funds",
"DoubleLine",
"Dreyfus",
"Dunham Funds",
"Eagle Funds",
"Eaton Vance",
"Federated",
"Fidelity Investments",
"First Investors",
"First Trust",
"Flexshares Trust",
"Franklin Templeton Investments",
"GMO",
"Gabelli",
"Global X Funds",
"Goldman Sachs",
"Great-West Funds",
"Guggenheim Investments",
"GuideStone Funds",
"HSBC",
"Hancock Horizon",
"Harbor",
"Hartford Mutual Funds",
"Henderson Global",
"Hennessy",
"Highland Funds",
"ICON Funds",
"Invesco",
"Ivy Funds",
"JPMorgan",
"Janus",
"John Hancock",
"Lazard",
"Legg Mason",
"Lord Abbett",
"MFS",
"Madison Funds",
"MainStay",
"Manning & Napier",
"Market Vectors",
"MassMutual",
"Matthews Asia Funds",
"Morgan Stanley",
"Nationwide",
"Natixis Funds",
"Neuberger Berman",
"Northern Funds",
"Nuveen",
"OppenheimerFunds",
"PNC Funds",
"Pacific funds series trust",
"Pax World",
"Paydenfunds",
"Pimco",
"Pioneer Investments",
"PowerShares",
"Principal Funds",
"ProFunds",
"ProShares",
"Prudential Investments",
"Putnam",
"RBC Global Asset Management.",
"RidgeWorth",
"Royce",
"Russell",
"Rydex Funds",
"SEI",
"SPDR State Street Global Advisors",
"Salient Funds",
"Saratoga",
"Schwab Funds",
"Sentinel",
"Shelton Capital Management",
"State Farm",
"State Street Global Advisors (Chicago)",
"Sterling Capital Funds",
"SunAmerica",
"T. Rowe Price",
"TCW",
"TIAA-CREF Asset Management",
"Teton Westwood Funds",
"Thornburg",
"Thrivent",
"Timothy Plan",
"Touchstone",
"Transamerica",
"UBS",
"UBS Group AG",
"USAA",
"VALIC",
"Vanguard",
"Vantagepoint Funds",
"Victory",
"Virtus",
"Voya",
"Waddell & Reed",
"Wasatch",
"Wells Fargo Funds",
"William Blair",
"WisdomTree",
"iShares"
},
"morningstar_economic_moat": {
"Wide",
"Narrow",
"None"
},
"morningstar_stewardship": {
"Exemplary",
"Standard",
"Poor"
},
"morningstar_uncertainty": {
"Low",
"Medium",
"High",
"Very High",
"Extreme"
},
"morningstar_moat_trend": {
"Stable",
"Positive",
"Negative"
},
"morningstar_rating_change": {
"Upgrade",
"Downgrade"
}
}
ETF_SCREENER_FIELDS = {
"eq_fields": {
"categoryname",
"fundfamilyname",
"region",
"primary_sector",
"morningstar_economic_moat",
"morningstar_stewardship",
"morningstar_uncertainty",
"morningstar_moat_trend",
"morningstar_rating_change"},
"fundamentals": {
"fundnetassets",
"ticker"},
"feesandexpenses": {
"annualreportgrossexpenseratio",
"annualreportnetexpenseratio",
"turnoverratio"},
"historicalperformance": {
"annualreturnnavy1",
"annualreturnnavy1categoryrank",
"annualreturnnavy3",
"annualreturnnavy5"},
"keystats": {
"avgdailyvol3m",
"dayvolume",
"eodvolume",
"fiftytwowkpercentchange",
"percentchange"},
"morningstar_rating": {
"morningstar_last_close_price_to_fair_value",
"morningstar_rating",
"morningstar_rating_updated_time"},
"portfoliostatistics": {
"marketcapitalvaluelong"},
"purchasedetails": {
"initialinvestment"},
"trailingperformance": {
"performanceratingoverall",
"quarterendtrailingreturnytd",
"riskratingoverall",
"trailing_3m_return",
"trailing_ytd_return"}
}
ETF_SCREENER_FIELDS = merge_two_level_dicts(ETF_SCREENER_FIELDS, COMMON_SCREENER_FIELDS)
USER_AGENTS = [
# Chrome
+2 -2
View File
@@ -1,4 +1,4 @@
from .query import EquityQuery
from .query import EquityQuery, FundQuery, ETFQuery
from .screener import screen, PREDEFINED_SCREENER_QUERIES
__all__ = ['EquityQuery', 'FundQuery', 'screen', 'PREDEFINED_SCREENER_QUERIES']
__all__ = ['EquityQuery', 'FundQuery', 'ETFQuery', 'screen', 'PREDEFINED_SCREENER_QUERIES']
+39
View File
@@ -4,6 +4,7 @@ from typing import List, Union, Dict, TypeVar, Literal
from yfinance.const import EQUITY_SCREENER_EQ_MAP, EQUITY_SCREENER_FIELDS
from yfinance.const import FUND_SCREENER_EQ_MAP, FUND_SCREENER_FIELDS
from yfinance.const import ETF_SCREENER_EQ_MAP, ETF_SCREENER_FIELDS
from yfinance.exceptions import YFNotImplementedError
from ..utils import dynamic_docstring, generate_list_table_from_dict_universal
@@ -218,3 +219,41 @@ class FundQuery(QueryBase):
"""
return FUND_SCREENER_EQ_MAP
class ETFQuery(QueryBase):
"""
The `ETFQuery` class constructs filters for ETFs based on specific criteria such as category, fund family, exchange, and performance ratings.
Start with value operations: `EQ` (equals), `IS-IN` (is in), `BTWN` (between), `GT` (greater than), `LT` (less than), `GTE` (greater or equal), `LTE` (less or equal).
Combine them with logical operations: `AND`, `OR`.
Example:
Predefined Yahoo query `top_etfs_us`:
.. code-block:: python
from yfinance import ETFQuery
ETFQuery('and', [
ETFQuery('gt', ['intradayprice', 10]),
ETFQuery('is-in', ['performanceratingoverall', 4, 5]),
ETFQuery('eq', ['region', 'us'])
])
"""
@dynamic_docstring({"valid_operand_fields_table": generate_list_table_from_dict_universal(ETF_SCREENER_FIELDS)})
@property
def valid_fields(self) -> Dict:
"""
Valid operands, grouped by category.
{valid_operand_fields_table}
"""
return ETF_SCREENER_FIELDS
@dynamic_docstring({"valid_values_table": generate_list_table_from_dict_universal(ETF_SCREENER_EQ_MAP)})
@property
def valid_values(self) -> Dict:
"""
Most operands take number values, but some have a restricted set of valid values.
{valid_values_table}
"""
return ETF_SCREENER_EQ_MAP
+15 -4
View File
@@ -9,7 +9,8 @@ from ..utils import dynamic_docstring, generate_list_table_from_dict_universal
from .query import EquityQuery as EqyQy
from .query import FundQuery as FndQy
from .query import QueryBase, EquityQuery, FundQuery
from .query import ETFQuery as EtfQy
from .query import QueryBase, EquityQuery, FundQuery, ETFQuery
_SCREENER_URL_ = f"{_QUERY1_URL_}/v1/finance/screener"
_PREDEFINED_URL_ = f"{_SCREENER_URL_}/predefined/saved"
@@ -48,11 +49,19 @@ PREDEFINED_SCREENER_QUERIES = {
'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'])])}
"query": FndQy('and', [FndQy('gt', ['intradayprice', 15]), FndQy('is-in', ['performanceratingoverall', 4, 5]), FndQy('gt', ['initialinvestment', 1000]), FndQy('eq', ['exchange', 'NAS'])])},
'top_etfs_us': {"sortField":"percentchange", "sortType":"DESC",
"query": EtfQy('and', [EtfQy('gt', ['intradayprice', 10]), EtfQy('is-in', ['performanceratingoverall', 4, 5]), EtfQy('eq', ['region', 'us'])])},
'top_performing_etfs': {"sortField":"annualreportnetexpenseratio", "sortType":"ASC",
"query": EtfQy('and', [EtfQy('eq', ['region', 'us']), EtfQy('is-in', ['performanceratingoverall', 4, 5]), EtfQy('gt', ['intradayprice', 10])])},
'technology_etfs': {"sortField":"annualreportnetexpenseratio", "sortType":"ASC",
"query": EtfQy('and', [EtfQy('eq', ['region', 'us']), EtfQy('eq', ['categoryname', 'Technology'])])},
'bond_etfs': {"sortField":"annualreportnetexpenseratio", "sortType":"ASC",
"query": EtfQy('and', [EtfQy('eq', ['region', 'us']), EtfQy('is-in', ['categoryname', 'Corporate Bond', 'Emerging Markets Bond', 'Emerging-Markets Local-Currency Bond', 'High Yield Bond', 'Intermediate-Term Bond', 'Long-Term Bond', 'Inflation-Protected Bond', 'Multisector Bond', 'Nontraditional Bond', 'Short-Term Bond', 'Ultrashort Bond', 'World Bond'])])}
}
@dynamic_docstring({"predefined_screeners": generate_list_table_from_dict_universal(PREDEFINED_SCREENER_QUERIES, bullets=True, title='Predefined queries (Dec-2024)')})
def screen(query: Union[str, EquityQuery, FundQuery],
def screen(query: Union[str, EquityQuery, FundQuery, ETFQuery],
offset: int = None,
size: int = None,
count: int = None,
@@ -65,7 +74,7 @@ def screen(query: Union[str, EquityQuery, FundQuery],
Run a screen: predefined query, or custom query.
:Parameters:
* Defaults only apply if query = EquityQuery or FundQuery
* Defaults only apply if query = EquityQuery, FundQuery, or ETFQuery
query : str | Query:
The query to execute, either name of predefined or custom query.
For predefined list run yf.PREDEFINED_SCREENER_QUERIES.keys()
@@ -194,6 +203,8 @@ def screen(query: Union[str, EquityQuery, FundQuery],
post_query['quoteType'] = 'EQUITY'
elif isinstance(post_query['query'], FndQy):
post_query['quoteType'] = 'MUTUALFUND'
elif isinstance(post_query['query'], EtfQy):
post_query['quoteType'] = 'ETF'
post_query['query'] = post_query['query'].to_dict()
data = dumps(post_query, separators=(",", ":"), ensure_ascii=False)