Merge pull request #2757 from ranaroussi/dev

sync dev -> main
This commit is contained in:
ValueRaider
2026-04-16 20:48:40 +01:00
committed by GitHub
12 changed files with 565 additions and 24 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:`config.debug.logging <yfinance.config>`: Enable verbose debug logging (``yf.config.debug.logging = True``).
- :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`
+50
View File
@@ -19,6 +19,7 @@ from yfinance.config import YfConfig
import unittest
# import requests_cache
from unittest.mock import patch, MagicMock
from typing import Union, Any, get_args, _GenericAlias
# from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
@@ -1235,6 +1236,54 @@ class TestTickerFundsData(unittest.TestCase):
sector_weightings = ticker.funds_data.sector_weightings
self.assertIsInstance(sector_weightings, dict)
class TestTickerValuationMeasures(unittest.TestCase):
_MOCK_HTML = """<html><body>
<table>
<tr><td></td><td>Current</td><td>12/31/2025</td><td>9/30/2025</td></tr>
<tr><td>Market Cap</td><td>3.76T</td><td>4.00T</td><td>3.76T</td></tr>
<tr><td>Enterprise Value</td><td>3.78T</td><td>4.04T</td><td>3.81T</td></tr>
<tr><td>Trailing P/E</td><td>32.39</td><td>36.44</td><td>38.64</td></tr>
<tr><td>Forward P/E</td><td>29.76</td><td>32.79</td><td>31.65</td></tr>
<tr><td>PEG Ratio (5yr expected)</td><td>2.27</td><td>2.75</td><td>2.44</td></tr>
<tr><td>Price/Sales</td><td>8.77</td><td>9.80</td><td>9.41</td></tr>
<tr><td>Price/Book</td><td>42.60</td><td>54.21</td><td>57.14</td></tr>
<tr><td>Enterprise Value/Revenue</td><td>8.68</td><td>9.71</td><td>9.32</td></tr>
<tr><td>Enterprise Value/EBITDA</td><td>24.73</td><td>27.92</td><td>26.87</td></tr>
</table>
</body></html>"""
def _make_ticker_with_mock(self, html):
mock_response = MagicMock()
mock_response.text = html
with patch("yfinance.data.YfData.cache_get", return_value=mock_response):
dat = yf.Ticker("AAPL")
data = dat.valuation_measures
return data
def test_valuation_measures(self):
data = self._make_ticker_with_mock(self._MOCK_HTML)
self.assertEqual(data.shape, (9, 3), "unexpected shape")
self.assertListEqual(list(data.columns), ["Current", "12/31/2025", "9/30/2025"])
self.assertIn("Market Cap", data.index)
self.assertIn("Trailing P/E", data.index)
self.assertIn("Enterprise Value/EBITDA", data.index)
self.assertIsNone(data.index.name)
self.assertEqual(data.loc["Market Cap", "Current"], "3.76T")
self.assertEqual(data.loc["Forward P/E", "12/31/2025"], "32.79")
def test_valuation_measures_no_table(self):
data = self._make_ticker_with_mock("<html><body><p>No tables here</p></body></html>")
self.assertIsInstance(data, pd.DataFrame)
self.assertTrue(data.empty)
def test_valuation_measures_fetch_error(self):
with patch("yfinance.data.YfData.cache_get", side_effect=Exception("network error")):
dat = yf.Ticker("AAPL")
data = dat.valuation_measures
self.assertIsInstance(data, pd.DataFrame)
self.assertTrue(data.empty)
def suite():
suite = unittest.TestSuite()
suite.addTest(TestTicker('Test ticker'))
@@ -1244,6 +1293,7 @@ def suite():
suite.addTest(TestTickerMiscFinancials('Test misc financials'))
suite.addTest(TestTickerInfo('Test info & fast_info'))
suite.addTest(TestTickerFundsData('Test Funds Data'))
suite.addTest(TestTickerValuationMeasures('Test valuation measures'))
return suite
+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()
+4
View File
@@ -287,6 +287,10 @@ class TickerBase:
self._fast_info = FastInfo(self)
return self._fast_info
def get_valuation_measures(self):
data = self._quote.valuation_measures
return data
def get_sustainability(self, as_dict=False):
data = self._quote.sustainability
if as_dict:
+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
+26 -16
View File
@@ -358,26 +358,35 @@ class PriceHistory:
if splits is not None:
splits = utils.set_df_tz(splits, interval, tz_exchange)
self._splits = splits
self._splits = splits['Stock Splits'].rename_axis('Date')
else:
self._splits = pd.Series()
if dividends is not None:
dividends = utils.set_df_tz(dividends, interval, tz_exchange)
self._dividends = dividends
if dividends is not None and 'currency' in dividends.columns:
# Rare, only seen with Vietnam market
# or companies that distribute dividends in a different currency
price_currency = self._history_metadata['currency']
if price_currency is None:
price_currency = ''
f_currency_mismatch = dividends['currency'] != price_currency
if f_currency_mismatch.any():
if repair and price_currency != '':
# Attempt repair = currency conversion
dividends = self._dividends_convert_fx(dividends, price_currency, repair)
dividends = dividends.drop('currency', axis=1)
if 'currency' in dividends.columns:
# Rare, only seen with Vietnam market, or
# companies that distribute dividends in a different currency
self._dividends = dividends.rename_axis('Date')
price_currency = self._history_metadata['currency']
if price_currency is None:
price_currency = ''
f_currency_mismatch = dividends['currency'] != price_currency
if f_currency_mismatch.any():
if repair and price_currency != '':
# Attempt repair = currency conversion
dividends = self._dividends_convert_fx(dividends, price_currency, repair)
dividends = dividends.drop('currency', axis=1)
else:
self._dividends = dividends['Dividends'].rename_axis('Date')
else:
self._dividends = pd.Series()
if capital_gains is not None:
capital_gains = utils.set_df_tz(capital_gains, interval, tz_exchange)
self._capital_gains = capital_gains
self._capital_gains = capital_gains['Capital Gains'].rename_axis('Date')
else:
self._capital_gains = pd.Series()
if start is not None:
if not quotes.empty:
start_d = quotes.index[0].floor('D')
@@ -562,7 +571,7 @@ class PriceHistory:
df = data['prices']
divs = data['dividends']
if divs is not None and 'currency' in divs.columns:
if divs is not None and isinstance(divs, pd.DataFrame) and 'currency' in divs.columns:
# Add dividends currency column
df = utils.safe_merge_dfs(df.drop('Dividends', axis=1), divs, '1d')
df['currency'] = df['currency'].fillna('')
@@ -573,6 +582,7 @@ class PriceHistory:
actions = df[[c for c in cols if c in df.columns]]
cols_numeric = ['Dividends', 'Stock Splits', 'Capital Gains']
cols_numeric = [c for c in cols_numeric if c in actions.columns]
actions = actions[(actions[cols_numeric]!=0).any(axis=1)]
for c in cols_numeric:
if (actions[c] == 0.0).all():
+42
View File
@@ -3,6 +3,7 @@ import datetime
import json
import numpy as _np
import pandas as pd
from bs4 import BeautifulSoup
from yfinance import utils
from yfinance.config import YfConfig
@@ -492,6 +493,7 @@ class Quote:
self._upgrades_downgrades = None
self._calendar = None
self._sec_filings = None
self._valuation_measures = None
self._already_scraped = False
self._already_fetched = False
@@ -572,6 +574,12 @@ class Quote:
self._sec_filings = {} if f is None else f
return self._sec_filings
@property
def valuation_measures(self) -> pd.DataFrame:
if self._valuation_measures is None:
self._fetch_valuation_measures()
return self._valuation_measures
@staticmethod
def valid_modules():
return quote_summary_valid_modules
@@ -661,6 +669,40 @@ class Quote:
self._info = {k: _format(k, v) for k, v in query1_info.items()}
def _fetch_valuation_measures(self):
url = f"https://finance.yahoo.com/quote/{self._symbol}/key-statistics"
try:
response = self._data.cache_get(url=url)
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
utils.get_yf_logger().error(f"Failed to fetch key-statistics page: {e}")
self._valuation_measures = pd.DataFrame()
return
try:
soup = BeautifulSoup(response.text, "html.parser")
table = soup.find("table")
if table is None:
self._valuation_measures = pd.DataFrame()
return
headers = [th.get_text(strip=True) for th in table.find("tr").find_all(["th", "td"])]
rows = []
for tr in table.find_all("tr")[1:]:
cells = [td.get_text(strip=True) for td in tr.find_all(["th", "td"])]
rows.append(cells)
df = pd.DataFrame(rows, columns=headers)
df = df.set_index(df.columns[0])
df.index.name = None
self._valuation_measures = df
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
utils.get_yf_logger().error(f"Failed to parse key-statistics page: {e}")
self._valuation_measures = pd.DataFrame()
def _fetch_complementary(self):
if self._already_fetched_complementary:
return
+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)
+4
View File
@@ -162,6 +162,10 @@ class Ticker(TickerBase):
def fast_info(self):
return self.get_fast_info()
@property
def valuation(self) -> _pd.DataFrame:
return self.get_valuation_measures()
@property
def calendar(self) -> dict:
"""