Download market data from Yahoo! Finance API
Build custom queries to screen and filter financial instruments based on various criteria with predefined screening options. These capabilities enable systematic investment research and opportunity discovery across equity and fund markets.
Build custom screening queries for stocks and equities with flexible operators and criteria combinations.
class EquityQuery:
def __init__(self, operator: str, operand: list):
"""
Create a custom equity screening query.
Parameters:
- operator: str, query operator ('EQ', 'GT', 'LT', 'GTE', 'LTE', 'BTWN', 'IS-IN', 'AND', 'OR')
- operand: list, operand values or nested query objects
Operators:
- 'EQ': Equal to
- 'GT': Greater than
- 'LT': Less than
- 'GTE': Greater than or equal to
- 'LTE': Less than or equal to
- 'BTWN': Between (requires 2-element list)
- 'IS-IN': In list of values
- 'AND': Logical AND (combines multiple queries)
- 'OR': Logical OR (combines multiple queries)
"""
# Properties for query validation
valid_fields: Dict # Valid field names organized by category
valid_values: Dict # Valid values for specific fieldsCommon equity screening fields include:
Price & Performance:
percentchange: Percent change from previous closeprice: Current stock pricedayrange: Day's trading range52weekrange: 52-week price rangeValuation Metrics:
pe: Price-to-earnings ratiopeg: Price/earnings to growth ratiopb: Price-to-book ratiops: Price-to-sales ratiomarketcap: Market capitalizationFinancial Metrics:
roe: Return on equityroa: Return on assetsdebttoequity: Debt-to-equity ratiocurrentratio: Current ratioMarket Data:
volume: Trading volumeavgvolume: Average volumesector: Business sectorindustry: Industry classificationregion: Geographic regionimport yfinance as yf
from yfinance import EquityQuery, screen
# Simple percentage change filter
gainers_query = EquityQuery('gt', ['percentchange', 5])
# Price range filter
price_range_query = EquityQuery('btwn', ['price', [50, 200]])
# Market cap filter
large_cap_query = EquityQuery('gte', ['marketcap', 10000000000]) # >= $10B
# Sector filter
tech_query = EquityQuery('eq', ['sector', 'Technology'])
# Complex combined query
complex_query = EquityQuery('and', [
EquityQuery('gt', ['percentchange', 3]), # Up more than 3%
EquityQuery('gte', ['marketcap', 1000000000]), # Market cap >= $1B
EquityQuery('eq', ['region', 'us']), # US stocks only
EquityQuery('lt', ['pe', 25]) # P/E ratio < 25
])
# Alternative OR combination
alternative_query = EquityQuery('or', [
EquityQuery('gt', ['percentchange', 10]), # Up more than 10%
EquityQuery('gt', ['volume', 5000000]) # High volume
])Build custom screening queries for mutual funds and ETFs with fund-specific criteria.
class FundQuery:
def __init__(self, operator: str, operand: list):
"""
Create a custom fund screening query.
Parameters:
- operator: str, query operator (same as EquityQuery operators)
- operand: list, operand values or nested query objects
Fund-specific fields include expense ratios, fund categories,
performance metrics, and asset allocation criteria.
"""
# Properties for query validation (fund-specific)
valid_fields: Dict # Valid fund field names
valid_values: Dict # Valid values for fund-specific fieldsFund Characteristics:
fundtype: Fund type (ETF, mutual fund, index fund)category: Fund category (large cap, small cap, bond, etc.)expenserate: Annual expense rationav: Net asset valuePerformance Metrics:
ytdreturn: Year-to-date return1yearreturn: 1-year return3yearreturn: 3-year annualized return5yearreturn: 5-year annualized returnFund Size & Activity:
totalassets: Total fund assets under managementavgvolume: Average trading volume (for ETFs)from yfinance import FundQuery
# Low-cost funds filter
low_cost_query = FundQuery('lt', ['expenserate', 0.5]) # Expense ratio < 0.5%
# High-performing funds
performance_query = FundQuery('gt', ['1yearreturn', 15]) # > 15% 1-year return
# Large fund filter
large_fund_query = FundQuery('gte', ['totalassets', 1000000000]) # >= $1B AUM
# Combined fund criteria
quality_fund_query = FundQuery('and', [
FundQuery('lt', ['expenserate', 0.75]), # Low expense ratio
FundQuery('gt', ['3yearreturn', 10]), # Good 3-year performance
FundQuery('eq', ['fundtype', 'ETF']), # ETFs only
FundQuery('gte', ['totalassets', 500000000]) # Adequate size
])Execute screening queries against the financial database with sorting and pagination options.
def screen(query: Union[str, EquityQuery, FundQuery],
offset: int = None, size: int = None, count: int = None,
sortField: str = None, sortAsc: bool = None,
userId: str = None, userIdType: str = None,
session = None) -> dict:
"""
Execute a screening query to find matching financial instruments.
Parameters:
- query: str (predefined query name) or EquityQuery/FundQuery object
- offset: int, starting position for pagination
- size: int, number of results to return (max 250)
- count: int, alias for size parameter
- sortField: str, field to sort results by (default: 'ticker')
- sortAsc: bool, sort ascending (True) or descending (False, default)
- userId: str, user identifier for personalized results
- userIdType: str, type of user identifier
- session: requests.Session, optional session for HTTP requests
Returns:
dict with screening results including matched instruments and metadata
"""{
'finance': {
'result': [{
'id': 'screener_result_id',
'title': 'Screening Results',
'description': 'Custom equity screen',
'canonicalName': 'custom_screen',
'count': 45,
'quotes': [
{
'symbol': 'AAPL',
'shortName': 'Apple Inc.',
'longName': 'Apple Inc.',
'sector': 'Technology',
'industry': 'Consumer Electronics',
'marketCap': 2800000000000,
'regularMarketPrice': 175.43,
'regularMarketChange': 2.15,
'regularMarketChangePercent': 1.24,
'volume': 45672100
},
# ... more results
],
'start': 0,
'total': 45
}]
},
'error': None
}# Execute predefined screen
day_gainers = screen("day_gainers")
results = day_gainers['finance']['result'][0]['quotes']
# Execute custom query with sorting
custom_query = EquityQuery('and', [
EquityQuery('gt', ['percentchange', 5]),
EquityQuery('eq', ['region', 'us'])
])
custom_results = screen(custom_query,
size=50,
sortField='percentchange',
sortAsc=False)
# Pagination example
page_1 = screen("most_actives", size=25, offset=0)
page_2 = screen("most_actives", size=25, offset=25)
# Fund screening
fund_query = FundQuery('lt', ['expenserate', 0.5])
low_cost_funds = screen(fund_query, size=30, sortField='expenserate')Access a comprehensive set of predefined screening queries for common investment research scenarios.
PREDEFINED_SCREENER_QUERIES: Dict[str, str]
# Dictionary containing predefined query names and descriptionsEquity Screens:
Growth & Performance:
'aggressive_small_caps': High-growth potential small cap stocks'day_gainers': Top daily percentage gainers'day_losers': Top daily percentage losers'growth_technology_stocks': Technology stocks with strong growth metrics'small_cap_gainers': Best performing small cap stocks'undervalued_growth_stocks': Growth stocks trading at reasonable valuations'undervalued_large_caps': Large cap value opportunitiesActivity & Interest:
'most_actives': Most actively traded stocks by volume'most_shorted_stocks': Stocks with highest short interestFund Screens:
Conservative Options:
'conservative_foreign_funds': Conservative international funds'high_yield_bond': High-yield bond funds'portfolio_anchors': Stable, core holding fundsGrowth Oriented:
'solid_large_growth_funds': Reliable large cap growth funds'solid_midcap_growth_funds': Quality mid cap growth funds'top_mutual_funds': Top-rated mutual funds across categories# View all available predefined screens
from yfinance import PREDEFINED_SCREENER_QUERIES
print("Available Predefined Screens:")
for query_name, description in PREDEFINED_SCREENER_QUERIES.items():
print(f" {query_name}: {description}")
# Execute predefined screens
day_gainers = screen("day_gainers", size=20)
most_active = screen("most_actives", size=15)
growth_tech = screen("growth_technology_stocks", size=25)
# Get results from predefined screens
gainers_list = day_gainers['finance']['result'][0]['quotes']
for stock in gainers_list[:5]: # Top 5
print(f"{stock['symbol']}: {stock['regularMarketChangePercent']:+.2f}%")def comprehensive_stock_screen(min_market_cap=1000000000, min_change=2.0,
max_pe=20, sectors=None):
"""Create a comprehensive stock screening strategy."""
# Build base criteria
criteria = [
EquityQuery('gte', ['marketcap', min_market_cap]), # Market cap filter
EquityQuery('gt', ['percentchange', min_change]), # Performance filter
EquityQuery('lt', ['pe', max_pe]), # Valuation filter
EquityQuery('gt', ['volume', 500000]) # Liquidity filter
]
# Add sector filter if specified
if sectors:
if len(sectors) == 1:
criteria.append(EquityQuery('eq', ['sector', sectors[0]]))
else:
sector_queries = [EquityQuery('eq', ['sector', sector]) for sector in sectors]
criteria.append(EquityQuery('or', sector_queries))
# Combine all criteria
comprehensive_query = EquityQuery('and', criteria)
# Execute screen
results = screen(comprehensive_query, size=100, sortField='percentchange', sortAsc=False)
return results
# Usage
tech_healthcare_screen = comprehensive_stock_screen(
min_market_cap=5000000000, # $5B minimum
min_change=3.0, # 3%+ gain
max_pe=25, # P/E < 25
sectors=['Technology', 'Healthcare']
)def momentum_screen_strategy(timeframes=['1day', '1week', '1month']):
"""Screen for momentum stocks across multiple timeframes."""
momentum_results = {}
# Daily momentum
daily_momentum = EquityQuery('and', [
EquityQuery('gt', ['percentchange', 5]), # Up 5%+ today
EquityQuery('gt', ['volume', 1000000]), # High volume
EquityQuery('gte', ['marketcap', 1000000000]) # Large enough
])
# Execute momentum screens
if '1day' in timeframes:
momentum_results['daily'] = screen(daily_momentum,
size=50,
sortField='percentchange')
# Note: Weekly and monthly momentum would require additional data
# This demonstrates the pattern for extending to multiple timeframes
return momentum_results
# Usage
momentum_stocks = momentum_screen_strategy(['1day'])
daily_momentum = momentum_stocks['daily']['finance']['result'][0]['quotes']
print("Top Daily Momentum Stocks:")
for stock in daily_momentum[:10]:
print(f"{stock['symbol']}: {stock['regularMarketChangePercent']:+.2f}% "
f"(Vol: {stock['volume']:,})")def value_investing_screen(max_pe=15, max_pb=2.0, min_roe=10):
"""Screen for value investment opportunities."""
value_criteria = EquityQuery('and', [
EquityQuery('lt', ['pe', max_pe]), # Low P/E ratio
EquityQuery('lt', ['pb', max_pb]), # Low price-to-book
EquityQuery('gt', ['roe', min_roe]), # Good return on equity
EquityQuery('gte', ['marketcap', 1000000000]), # Minimum size
EquityQuery('gt', ['volume', 200000]) # Adequate liquidity
])
value_results = screen(value_criteria,
size=75,
sortField='pe', # Sort by P/E ratio
sortAsc=True) # Lowest P/E first
return value_results
# Usage
value_opportunities = value_investing_screen(max_pe=12, max_pb=1.5, min_roe=15)
value_stocks = value_opportunities['finance']['result'][0]['quotes']
print("Value Investment Opportunities:")
for stock in value_stocks[:10]:
pe_ratio = stock.get('trailingPE', 'N/A')
print(f"{stock['symbol']}: P/E={pe_ratio}, Sector={stock.get('sector', 'N/A')}")def low_cost_index_fund_screen(max_expense=0.5, min_assets=1000000000):
"""Screen for low-cost index funds."""
index_fund_criteria = FundQuery('and', [
FundQuery('lt', ['expenserate', max_expense]), # Low expense ratio
FundQuery('gte', ['totalassets', min_assets]), # Adequate size
FundQuery('eq', ['fundtype', 'ETF']), # ETFs preferred
FundQuery('gt', ['avgvolume', 100000]) # Good liquidity
])
results = screen(index_fund_criteria,
size=50,
sortField='expenserate',
sortAsc=True) # Lowest expenses first
return results
def high_yield_fund_screen(min_yield=4.0, max_expense=1.0):
"""Screen for high-yield income funds."""
yield_criteria = FundQuery('and', [
FundQuery('gt', ['yield', min_yield]), # High yield
FundQuery('lt', ['expenserate', max_expense]), # Reasonable expenses
FundQuery('gte', ['totalassets', 500000000]) # Minimum fund size
])
results = screen(yield_criteria,
size=30,
sortField='yield',
sortAsc=False) # Highest yield first
return results
# Usage
low_cost_etfs = low_cost_index_fund_screen(max_expense=0.25, min_assets=5000000000)
high_yield_funds = high_yield_fund_screen(min_yield=5.0, max_expense=0.75)def sector_leader_screen(sector, performance_threshold=10):
"""Screen for leaders within a specific sector."""
sector_leaders = EquityQuery('and', [
EquityQuery('eq', ['sector', sector]), # Specific sector
EquityQuery('gt', ['percentchange', performance_threshold]), # Strong performance
EquityQuery('gt', ['marketcap', 1000000000]), # Large cap preferred
EquityQuery('gt', ['volume', 500000]), # Good liquidity
EquityQuery('lt', ['pe', 30]) # Not overvalued
])
results = screen(sector_leaders,
size=25,
sortField='marketcap',
sortAsc=False) # Largest companies first
return results
def emerging_sector_screen(sectors, min_growth=20):
"""Screen for emerging opportunities across multiple sectors."""
# Create sector filter
if len(sectors) == 1:
sector_filter = EquityQuery('eq', ['sector', sectors[0]])
else:
sector_queries = [EquityQuery('eq', ['sector', s]) for s in sectors]
sector_filter = EquityQuery('or', sector_queries)
emerging_criteria = EquityQuery('and', [
sector_filter, # Target sectors
EquityQuery('gt', ['percentchange', min_growth]), # High growth
EquityQuery('btwn', ['marketcap', [100000000, 10000000000]]), # Mid-cap range
EquityQuery('gt', ['volume', 250000]) # Adequate volume
])
results = screen(emerging_criteria,
size=40,
sortField='percentchange',
sortAsc=False)
return results
# Usage
tech_leaders = sector_leader_screen('Technology', performance_threshold=5)
clean_energy_emerging = emerging_sector_screen(['Utilities', 'Materials'], min_growth=15)def analyze_screen_results(screen_results, additional_metrics=True):
"""Analyze and enhance screening results with additional metrics."""
quotes = screen_results['finance']['result'][0]['quotes']
enhanced_results = []
for quote in quotes:
symbol = quote['symbol']
# Basic data from screen
result = {
'symbol': symbol,
'name': quote.get('shortName', ''),
'sector': quote.get('sector', ''),
'price': quote.get('regularMarketPrice', 0),
'change_percent': quote.get('regularMarketChangePercent', 0),
'volume': quote.get('volume', 0),
'market_cap': quote.get('marketCap', 0)
}
# Add additional metrics if requested
if additional_metrics:
try:
ticker = yf.Ticker(symbol)
info = ticker.info
result.update({
'pe_ratio': info.get('trailingPE', None),
'pb_ratio': info.get('priceToBook', None),
'dividend_yield': info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 0,
'profit_margin': info.get('profitMargins', 0) * 100 if info.get('profitMargins') else 0,
'debt_to_equity': info.get('debtToEquity', None)
})
except:
# Handle cases where additional data isn't available
pass
enhanced_results.append(result)
return enhanced_results
# Usage
screen_result = screen("day_gainers", size=20)
analyzed_results = analyze_screen_results(screen_result, additional_metrics=True)
for stock in analyzed_results[:5]:
print(f"{stock['symbol']}: {stock['change_percent']:+.2f}% "
f"P/E={stock.get('pe_ratio', 'N/A')} "
f"Yield={stock.get('dividend_yield', 0):.1f}%")def build_portfolio_from_screens(screen_configs, max_positions=20,
max_sector_weight=0.3):
"""Build a diversified portfolio from multiple screening strategies."""
all_candidates = []
# Execute multiple screens
for config in screen_configs:
screen_name = config['name']
query = config['query']
weight = config.get('weight', 1.0)
results = screen(query, size=config.get('size', 25))
quotes = results['finance']['result'][0]['quotes']
for quote in quotes:
all_candidates.append({
'symbol': quote['symbol'],
'name': quote.get('shortName', ''),
'sector': quote.get('sector', ''),
'screen_source': screen_name,
'weight': weight,
'change_percent': quote.get('regularMarketChangePercent', 0),
'market_cap': quote.get('marketCap', 0)
})
# Remove duplicates (keep highest weighted occurrence)
unique_candidates = {}
for candidate in all_candidates:
symbol = candidate['symbol']
if symbol not in unique_candidates or candidate['weight'] > unique_candidates[symbol]['weight']:
unique_candidates[symbol] = candidate
# Sort by weighted score
candidates = list(unique_candidates.values())
candidates.sort(key=lambda x: x['change_percent'] * x['weight'], reverse=True)
# Build diversified portfolio
portfolio = []
sector_weights = {}
for candidate in candidates:
if len(portfolio) >= max_positions:
break
sector = candidate['sector']
current_sector_weight = sector_weights.get(sector, 0)
# Check sector concentration limits
if current_sector_weight < max_sector_weight:
portfolio.append(candidate)
sector_weights[sector] = current_sector_weight + (1.0 / max_positions)
return portfolio
# Usage
portfolio_screens = [
{
'name': 'momentum',
'query': EquityQuery('and', [
EquityQuery('gt', ['percentchange', 3]),
EquityQuery('gt', ['volume', 1000000])
]),
'weight': 1.5,
'size': 30
},
{
'name': 'value',
'query': EquityQuery('and', [
EquityQuery('lt', ['pe', 20]),
EquityQuery('gt', ['roe', 10])
]),
'weight': 1.0,
'size': 25
}
]
diversified_portfolio = build_portfolio_from_screens(portfolio_screens,
max_positions=15,
max_sector_weight=0.25)
print("Constructed Portfolio:")
for i, holding in enumerate(diversified_portfolio, 1):
print(f"{i:2d}. {holding['symbol']} ({holding['sector']}) - "
f"Source: {holding['screen_source']}")Install with Tessl CLI
npx tessl i tessl/pypi-yfinance