Stock Screener with Python: Rank 50 Stocks by ROIC, FCF Yield, and Quality Metrics No Free API Provides
Screen 50 stocks by ROIC, FCF Yield, and 3 other metrics yfinance cannot provide. Percentile-ranked composite score: Quality (60%) + Value (40%). One script, one API call, no signup.
Mastercard. Visa. Adobe. The top 50 stocks by market cap, ranked by quality and value — not just PE ratio.
==========================================================================
STOCK SCREENER: TOP 10 OF 50 STOCKS
==========================================================================
Quality weight: 60% | Value weight: 40%
Rank Ticker Company ROIC FCF Yld PE Score
--------------------------------------------------------------------------
1 MA Mastercard Inc 100.0% 3.3% 35.02 83.0 QUALITY
2 BAC BANK OF AMERICA CORP 11.4% 15.6% 15.20 75.8 VALUE
3 BRK-B BERKSHIRE HATHAWAY I 55.0% N/A N/A 70.6 VALUE
4 V VISA INC. 37.8% 3.4% 31.01 69.3 QUALITY
5 PM Philip Morris Intern 47.1% 4.3% 25.15 69.3 QUALITY
6 MRK Merck & Co., Inc. 26.4% 6.2% 16.11 67.4 VALUE
7 LRCX LAM RESEARCH CORP 75.9% 3.4% 50.66 64.7 QUALITY
8 LLY ELI LILLY & Co 52.4% 2.3% 51.64 64.6 QUALITY
9 CAT CATERPILLAR INC 388.4% 3.9% 37.09 64.4
10 PG PROCTER & GAMBLE Co 66.1% 4.1% 22.64 62.9
--------------------------------------------------------------------------
Screened 50 stocks | 5 metrics | ~250 credits
==========================================================================
This is Stock Screener — a Python tool that ranks stocks by composite Quality + Value score using SEC-filed data from the MetricDuck API.
Three of the five screening metrics — ROIC, FCF Yield, and FCF Margin — are not available in yfinance or any free alternative.
No API key required. No signup. Screening 50 stocks costs ~250 credits. Guest access works immediately — clone, install, run.
Quick Start
Python 3.10+ required.
git clone https://github.com/metric-duck/build-with-metricduck.git
cd build-with-metricduck/labs/04-stock-screener
pip install -r requirements.txt
python screener.py
That's it. Screens the top 50 companies by market cap and shows the top 10 by composite score.
Custom ticker list: python screener.py --tickers AAPL,MSFT,GOOGL,AMZN,META,NVDA
Why Screen on Quality, Not Just PE
Most stock screeners filter by PE ratio and dividend yield — metrics that yfinance provides. But a low PE tells you the stock is cheap. It doesn't tell you if the business is good.
ROIC (Return on Invested Capital) tells you how efficiently the business converts capital into profit. A company with 50% ROIC earns $50 for every $100 of capital it deploys. A company with 5% ROIC earns $5. One is a compounding machine. The other is destroying value.
FCF Yield (Free Cash Flow / Market Cap) tells you how much real cash the business generates relative to its price. Unlike earnings, free cash flow can't be inflated by accounting choices.
Screening by both — quality (ROIC + FCF Margin) and value (PE + FCF Yield + EV/EBITDA) — finds stocks that are strong businesses and reasonably priced.
| What Most Screeners Do | What This Screener Adds |
|---|---|
| PE Ratio (lower is cheaper) | ROIC — is the business efficient? |
| Dividend Yield | FCF Yield — how much real cash per dollar invested? |
| Market Cap filter | FCF Margin — what % of revenue becomes free cash? |
| Sector filter | Composite scoring — quality + value, percentile-ranked |
How It Works
Step 1: Get the Ticker Universe
The screener starts by fetching the top N companies by market cap from the MetricDuck universe endpoint:
response = httpx.get(
"https://api.metricduck.com/api/v1/companies/universe",
params={"limit": 50},
)
companies = response.json()["companies"]
# [{"ticker": "AAPL", "company_name": "Apple Inc.", "rank": 1, "sic": "3571"}, ...]
Each company comes with its market cap rank and SIC code (for sector filtering). Or skip the universe and pass your own tickers with --tickers.
Step 2: Fetch Metrics for All 50 Stocks
One API call fetches all 5 metrics for all 50 tickers:
response = httpx.get(
"https://api.metricduck.com/api/v1/data/metrics",
params={
"tickers": ",".join(tickers), # "AAPL,MSFT,GOOGL,..."
"metrics": "roic,fcf_yield,fcf_margin,pe_ratio,ev_ebitda",
"period": "ttm",
"price": "current",
"years": 1,
},
)
The response contains all 50 companies with all 5 metrics — a single request, not 50 separate calls. The price=current parameter recalculates valuation metrics at today's market price.
Step 3: Percentile Scoring
Raw metric values aren't comparable. ROIC is a percentage (0-100%+), PE is a ratio (5-100+), FCF Yield is a small percentage (0-10%). To combine them, the screener converts each metric to a percentile rank within the screened universe:
def compute_percentile_ranks(values, direction):
valid = {t: v for t, v in values.items() if v is not None}
# Sort: highest value = highest percentile for "higher" metrics
# lowest value = highest percentile for "lower" metrics
reverse = direction == "lower"
sorted_tickers = sorted(valid, key=lambda t: valid[t], reverse=reverse)
n = len(sorted_tickers)
return {
ticker: (i / (n - 1)) * 100 if n > 1 else 50.0
for i, ticker in enumerate(sorted_tickers)
}
If you're ranked 90th percentile in ROIC, you have higher ROIC than 90% of the screened stocks. If you're 90th percentile in PE (where lower is better), you have lower PE than 90% of the screened stocks.
Step 4: Composite Score
The composite score is a weighted average of two category averages:
q_avg = average of [ROIC percentile, FCF Margin percentile]
v_avg = average of [PE percentile, FCF Yield percentile, EV/EBITDA percentile]
composite = 0.6 * q_avg + 0.4 * v_avg # Quality 60%, Value 40%
The 60/40 split favors quality over value — the tool finds great businesses first, then considers price. Change the weights in the config to match your investment thesis.
Signal labels highlight the stock's strength:
- QUALITY — top 30% in quality metrics
- VALUE — top 30% in value metrics
- BALANCED — top 30% in both
Handling Missing Data
Not every stock reports every metric. Banks may lack ROIC. Recently listed companies may lack FCF history. The screener handles this by scoring each stock only on the metrics it has — missing metrics are excluded from the average, not penalized.
Usage Budget
Screening costs scale linearly: tickers × metrics × years.
| Mode | What You Can Do |
|---|---|
| Guest (no key) | Screen top 10 stocks, 5 requests/day |
| Free (registered) | Screen 50 stocks, 500 credits/day (~2 screens/day) |
| Builder ($29/mo) | Screen 200 stocks, 200,000 credits/month |
| Screen Size | Credits |
|---|---|
| 10 stocks × 5 metrics × 1yr | 50 |
| 50 stocks × 5 metrics × 1yr | 250 |
| 100 stocks × 5 metrics × 1yr | 500 |
| 200 stocks × 5 metrics × 1yr | 1,000 |
Guest access (no signup) screens the top 10 stocks immediately. Register free for 500 credits/day — enough for two full 50-stock screens every day.
Build It with Claude Code
The screener is a single Python file with no frameworks. AI assistants can read, understand, and extend it in one pass.
Change the scoring weights:
"Set QUALITY_WEIGHT to 0.4 and VALUE_WEIGHT to 0.6. I want to find cheap stocks first, quality second."
Add more metrics:
"Add
gross_marginandtotal_shareholder_yieldto QUALITY_METRICS. I want to see capital return efficiency."
Add dimensions for trend analysis:
"Add
dimensions=Q.TREND8to the API call. Then add a column showing whether ROIC is rising or falling. Flag stocks where ROIC is rising AND PE is falling — the same OPPORTUNITY signal from Lab 03."
Chain with Lab 03 (Value Trap Detection):
"After screening, pipe the top 5 through pulse.py to check for value traps:
python screener.py --top 5 --json | ..."
Export to CSV:
"After displaying the results, write them to a CSV file with columns: rank, ticker, company, roic, fcf_yield, pe_ratio, composite_score, signal."
The full screener.py is a single self-contained file with no class hierarchies and no abstractions. AI coding assistants can read the entire file, understand it, and extend it in one pass.
Try It Yourself
| Command | What You Get |
|---|---|
python screener.py | Top 10 of 50 largest companies by composite score |
python screener.py --count 100 --top 20 | Screen 100 companies, show top 20 |
python screener.py --tickers AAPL,MSFT,GOOGL,AMZN,META,NVDA | Screen specific stocks |
python screener.py --json | Machine-readable output for piping to other tools |
The scoring reveals interesting patterns. Payment processors (MA, V) score high on quality — near-100% ROIC from asset-light models. Banks (BAC) score high on value — low PE and high FCF Yield. Drug makers (MRK, LLY) split between value and quality depending on pipeline stage.
What's Next
The screener tells you which stocks score highest. But are any of them value traps?
Lab 03: Stock Pulse — Value Trap Detector answers this by checking any stock against its own 2-year history. Screen with Lab 04, then pulse the top picks with Lab 03.
# Screen the top 50, then check the #1 pick for value traps
python screener.py
python ../03-stock-pulse/pulse.py MA
And if you want to compare two top picks head-to-head, use Lab 02: Stock Showdown:
python ../02-stock-showdown/showdown.py MA V
Browse all 70+ available metrics at metricduck.com/metrics.
Full Source Code
The complete screener.py is on GitHub:
github.com/metric-duck/build-with-metricduck/labs/04-stock-screener
Clone it, read it, modify it. The only dependency is httpx.
API Access
| Tier | Daily Limit | Max Tickers | Cost |
|---|---|---|---|
| Guest (no key) | 5 requests/day | 10 tickers | Free |
| Free (registered) | 500 credits/day | 200 tickers | Free |
| Builder | 200,000 credits/mo | 200 tickers | $29/mo |
| Production | 1,000,000 credits/mo | 200 tickers | $79/mo |
Guest access screens the top 10 stocks immediately — no signup required. Register free for 500 credits/day and full 50-stock screening. Builder tier unlocks serious screening at scale.
MetricDuck Team
Building financial intelligence you can trust. Sourced directly from SEC Edgar.