Skip to main content
Tool Comparisons

How to Master Build Stock Screener Python [Step-by-Step Guide]

Javier Sanz, Founder & Lead Analyst at ValueMarkers
By , Founder & Lead AnalystEditorially reviewed
Last updated: Reviewed by: Javier Sanz
7 min read
Share:

How to Master Build Stock Screener Python [Step-by-Step Guide]

build stock screener python — chart and analysis

To build a stock screener in Python, you write a script that pulls fundamental data, applies a set of numerical filters, ranks the survivors, and saves the results. This guide walks through each step with working code, from data sourcing to an automated output you can run weekly without touching the script again.

The Python approach requires more setup than a ready-made tool like the ValueMarkers screener, but it gives you complete control over filter logic and output format. Both approaches produce better results when you understand the underlying methodology.

Key Takeaways

  • Build stock screener Python projects start with three choices: data source, filter criteria, and output format. Get those right before writing any code.
  • yfinance is the fastest way to start. It is free, requires no API key, and returns the core fundamental fields for most U.S. and major international tickers.
  • ROIC, debt-to-equity, and ROE are the three filters that most cleanly separate quality businesses from mediocre ones across historical periods.
  • A composite ranking function scores survivors on multiple criteria simultaneously, which is more useful than a simple pass/fail filter output.
  • Automating with a GitHub Actions cron job lets your screen run on a schedule without a local machine staying on.
  • Always validate your script's output against a trusted data source before relying on it. API data varies in accuracy, particularly for smaller-cap names.

Environment Setup

You need Python 3.9 or later. Install the required libraries with one command.

pip install yfinance pandas requests scipy

No API key is required for yfinance. ROIC is not in the .info dictionary directly; you calculate it from the income statement and balance sheet, which is what this guide does.

Step 1: Build the Ticker List

Use the S&P 500 Wikipedia table as a free, auto-updating ticker source.

import pandas as pd

def get_sp500_tickers():
 url = "https://en.wikipedia.org/wiki/List_of_S%26P_500_companies"
 df = pd.read_html(url)[0]
 tickers = df["Symbol"].str.replace(".", "-", regex=False).tolist()
 return tickers

tickers = get_sp500_tickers()
print(f"Loaded {len(tickers)} tickers")

For a global screen, replicating 73 exchanges in Python requires a paid data provider or significant manual maintenance.

Step 2: Fetch Fundamentals and Calculate ROIC

Fetch the .info dictionary and financial statement data for each ticker. ROIC requires operating income from the income statement and total debt and equity from the balance sheet.

import yfinance as yf
import time

def get_stock_data(ticker):
 try:
 stock = yf.Ticker(ticker)
 info = stock.info

 # Try to calculate ROIC from financial statements
 income_stmt = stock.income_stmt
 balance_sheet = stock.balance_sheet

 roic = None
 if not income_stmt.empty and not balance_sheet.empty:
 try:
 operating_income = income_stmt.loc["Operating Income"].iloc[0]
 total_debt = balance_sheet.loc["Total Debt"].iloc[0]
 total_equity = balance_sheet.loc["Stockholders Equity"].iloc[0]
 invested_capital = total_debt + total_equity
 if invested_capital > 0:
 tax_rate = 0.21 # approximate U.S. corporate tax rate
 nopat = operating_income * (1 - tax_rate)
 roic = nopat / invested_capital
 except (KeyError, IndexError):
 roic = None

 return {
 "ticker": ticker,
 "name": info.get("longName", ""),
 "sector": info.get("sector", ""),
 "pe_ratio": info.get("trailingPE"),
 "roe": info.get("returnOnEquity"),
 "debt_to_equity": info.get("debtToEquity"),
 "roic": roic,
 "revenue_growth": info.get("revenueGrowth"),
 "market_cap": info.get("marketCap"),
 }
 except Exception as e:
 print(f"Error: {ticker} - {e}")
 return None

records = []
for ticker in tickers[:100]:
 data = get_stock_data(ticker)
 if data:
 records.append(data)
 time.sleep(0.4)

df = pd.DataFrame(records)

The ROIC calculation uses NOPAT (net operating profit after tax) divided by invested capital. This matches the standard definition: it measures the after-tax return on the capital the business has deployed, regardless of how that capital was financed.

Step 3: Apply the Core Filters

With the DataFrame populated, apply your filters. Build stock screener Python logic with pandas boolean indexing.

def apply_filters(df):
 df = df.copy()

 # Convert D/E: yfinance returns it as percentage in some versions
 # Check your data: if Apple (AAPL) shows D/E ~150, divide by 100
 df["de_ratio"] = df["debt_to_equity"] / 100

 filtered = df[
 (df["pe_ratio"].notna()) &
 (df["roe"].notna()) &
 (df["de_ratio"].notna()) &
 (df["pe_ratio"] > 0) &
 (df["pe_ratio"] < 22) & # Valuation filter
 (df["roe"] > 0.10) & # Quality filter: ROE above 10%
 (df["de_ratio"] < 1.0) & # Health filter: D/E below 1.0x
 ]

 # Add ROIC filter if data is available
 if df["roic"].notna().sum() > 10:
 filtered = filtered[
 filtered["roic"].isna() | # Include if ROIC unavailable
 (filtered["roic"] > 0.10) # ROIC above 10% if available
 ]

 return filtered.reset_index(drop=True)

df_filtered = apply_filters(df)
print(f"Passed filters: {len(df_filtered)} stocks")

The ROIC filter uses a conditional: include the stock if ROIC is unavailable rather than excluding it. This prevents data gaps from silently eliminating valid candidates.

Step 4: Rank the Results

Pass/fail output lists are less useful than ranked lists. A composite score reveals which survivors look best on the combined criteria.

from scipy.stats import rankdata
import numpy as np

def rank_results(df):
 df = df.copy()

 # Rank each metric (higher rank = better)
 df["rank_pe"] = rankdata(-df["pe_ratio"].fillna(999)) # Lower P/E = better
 df["rank_roe"] = rankdata(df["roe"].fillna(0)) # Higher ROE = better
 df["rank_de"] = rankdata(-df["de_ratio"].fillna(999)) # Lower D/E = better

 # ROIC rank if available
 if df["roic"].notna().sum() > 5:
 median_roic = df["roic"].median()
 df["roic_filled"] = df["roic"].fillna(median_roic)
 df["rank_roic"] = rankdata(df["roic_filled"])
 else:
 df["rank_roic"] = 1 # Neutral if no ROIC data

 # Weighted composite score
 df["composite_score"] = (
 df["rank_pe"] * 0.30 +
 df["rank_roe"] * 0.35 +
 df["rank_de"] * 0.15 +
 df["rank_roic"] * 0.20
)

 return df.sort_values("composite_score", ascending=False)

df_ranked = rank_results(df_filtered)

The weights (30% P/E, 35% ROE, 15% D/E, 20% ROIC) reflect a quality-value tilt. A pure deep-value approach would increase the P/E weight to 50% and reduce the ROE weight accordingly.

Step 5: Output and Compare Against Benchmarks

Save the top results and display a clean summary table.

output_cols = ["ticker", "name", "sector", "pe_ratio", "roe", "de_ratio", "roic", "composite_score"]
top20 = df_ranked[output_cols].head(20)

print(top20.to_string(index=False))
top20.to_csv("screener_output.csv", index=False)

Before trusting this output, validate it against known benchmarks. Apple's ROE from your script should be close to 160% (the high level that results from massive buybacks reducing book equity). Microsoft's ROE should be around 35 to 40%. If these diverge significantly from those ranges, investigate whether yfinance is returning stale or incorrectly scaled data.

MetricApple (AAPL)Microsoft (MSFT)JNJ
P/E ratio~28.3~32.1~15.4
ROE~160%~35%~25%
ROIC~45.1%~35.2%~18%
Debt-to-equity~1.8x~0.4x~0.5x

Note that AAPL's high D/E reflects deliberate financial engineering, not distress. Apply D/E filters with sector context in mind.

Step 6: Automate with GitHub Actions

To run your screen weekly without a local machine, use a GitHub Actions workflow. Create a file at .github/workflows/screener.yml in your repository.

name: Weekly Stock Screen

on:
 schedule:
 - cron: "0 21 * * 5" # Fridays at 9:00 PM UTC (4:00 PM Eastern)
 workflow_dispatch:

jobs:
 run-screener:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v3
 - uses: actions/setup-python@v4
 with:
 python-version: "3.11"
 - run: pip install yfinance pandas scipy
 - run: python screener.py
 - uses: actions/upload-artifact@v3
 with:
 name: screener-results
 path: screener_output.csv

This workflow runs every Friday at 9:00 PM UTC and uploads the CSV as a downloadable artifact. You receive a GitHub email when the run completes.

When to Use Python vs. a Dedicated Screener

Building a Python screener is worth the investment if you need logic a UI cannot express: custom composite formulas, sector-relative thresholds, or integration with your own portfolio database. For most investors, the ValueMarkers screener handles the job in minutes without code. It covers 120+ indicators including ROIC, runs across 73 global exchanges, and avoids the data-gap problem that affects free Python libraries. Use Python for customization the UI cannot provide. Use the screener when speed and coverage matter more.

Why python screener tutorial Matters

This section anchors the discussion on python screener tutorial. The detailed treatment, formula, and worked examples appear in the body of this article above. The points below summarize the most important takeaways for value investors who want to apply python screener tutorial in real portfolio decisions. ValueMarkers exposes the underlying data on every covered ticker via the screener and stock profile pages, so the concepts in this article translate directly into actionable filters.

Key inputs for python screener tutorial

See the main discussion of python screener tutorial in the sections above for the full treatment, including the inputs, the calculation methodology, the typical sector benchmarks, and the most common pitfalls to avoid. The ValueMarkers screener lets value investors filter the full universe of 100,000+ stocks across 73 exchanges using python screener tutorial alongside the rest of the 120-indicator composite, with sector percentiles and historical trends shown on every stock profile.

Sector benchmarks for python screener tutorial

See the main discussion of python screener tutorial in the sections above for the full treatment, including the inputs, the calculation methodology, the typical sector benchmarks, and the most common pitfalls to avoid. The ValueMarkers screener lets value investors filter the full universe of 100,000+ stocks across 73 exchanges using python screener tutorial alongside the rest of the 120-indicator composite, with sector percentiles and historical trends shown on every stock profile.

Frequently Asked Questions

what happens if the stock market crashes

When the market crashes, your Python screener becomes more active, not less. Falling prices push more stocks below your P/E threshold, and the filter returns more candidates. The key is not to adjust your filter thresholds reactively. The filters you set based on a calm, rational framework will surface quality businesses at discount prices precisely when the market is least rational about pricing them.

what time does the stock market open

U.S. markets open at 9:30 a.m. Eastern Time. For a Python screener using yfinance, run your data fetch after 5:00 p.m. Eastern to capture complete end-of-day prices. Running before close gives you intraday prices that will differ from the official close, which can cause temporary mismatches in your P/E and P/B calculations.

are stock markets closed today

U.S. markets close on 10 federal holidays. yfinance automatically returns the most recent trading day's data on holiday requests, so your script does not need holiday-handling logic. However, if you schedule your script to run at a fixed time, verify that the returned data timestamp matches your expected trading date, especially around holiday periods.

what time does the stock market close

The NYSE and Nasdaq close at 4:00 p.m. Eastern Time. If your Python screener runs before that time on a trading day, you are working with the prior session's closing prices. This is fine for fundamental filters (P/E, ROE, ROIC) since those metrics change on earnings release dates, not daily. It matters more for pure price-momentum screens where intraday price level is significant.

when does the stock market open

The U.S. market opens at 9:30 a.m. Eastern. If you are building a Python screener for global markets, schedule separate data pulls for each region's close. European exchanges close around 4:30 to 5:30 p.m. local time, Asian exchanges around 3:00 to 4:00 p.m. local time. A global screen that pulls all regions at the same UTC time will mix fresh data with stale data unless you account for session timing.

why is the stock market down today

Markets decline for many reasons: macro data releases, Fed policy shifts, geopolitical events, or sector-specific developments. When running your Python screener on a down day, check the result count. If the screen now returns 40 stocks where it returned 20 last week, the additional 20 stocks are candidates whose prices fell into your valuation range. Those are the names to investigate first: why did the price fall, and did the underlying business change?


Test on 50 tickers, validate the output against the ValueMarkers screener, and then scale to the full universe once the data checks out. The screener's 120+ indicators give you a cross-reference that makes your Python results more trustworthy from day one.

Written by Javier Sanz, Founder of ValueMarkers. Last updated April 2026.


Ready to find your next value investment?

ValueMarkers tracks 120+ fundamental indicators across 100,000+ stocks on 73 global exchanges. Run the methodology above in seconds with our stock screener, or see today's top-ranked names on the leaderboard.

Related tools: DCF Calculator · Methodology · Compare ValueMarkers

Disclaimer: This content is for informational and educational purposes only and does not constitute investment advice, a recommendation, or an offer to buy or sell any security. Past performance does not guarantee future results. Consult a licensed financial advisor before making investment decisions.

Key Metrics Mentioned

Related Articles

Tool Comparisons

How to Use Create Custom Stock Screener for Better Investment Decisions [Tutorial]

Learn how to create custom stock screener filters that match your investment strategy, with step-by-step setup using real indicators and data.

9 min read

Tool Comparisons

Ttd Zacks Rank: A Detailed Look for Value-Focused Investors

Learn about ttd zacks rank with real data, stock examples, and actionable analysis. Updated April 2026 with 120+ indicators.

10 min read

Tool Comparisons

Morningstar Premium Review: Is It Worth the Cost in 2026?

Morningstar Investor (formerly Morningstar Premium) costs $249/year. Is the star rating, fair value estimate, moat rating, and analyst report library worth the price for individual value investors? A detailed review with three real-stock comparisons (AAPL, JNJ, INTC) showing where Morningstar adds value and where it falls short.

13 min read

Tool Comparisons

Wisesheets Alternative: Why ValueMarkers Offers More

If you use Wisesheets to pull stock data into Excel or Google Sheets, you already understand its fundamental appeal: custom functions automatically...

9 min read

Tool Comparisons

Gurufocus Undervalued Stocks: What the Data Tells Value Investors

Gurufocus undervalued stocks flags use the GF Value metric to surface potential discounts. This analysis explains what the data actually shows, which signals hold up historically.

8 min read

Tool Comparisons

Free Advanced Stock Screener: A Step-by-Step Tutorial for Investors

A step-by-step tutorial on using a free advanced stock screener. Covers Piotroski F-Score, EV/EBITDA, ROIC, and multi-factor filter logic for serious fundamental analysis.

8 min read

Explore More

Investing Tools

Compare Competitors

Browse Stocks

Weekly Stock Analysis - Free

5 undervalued stocks, fully modeled. Every Monday. No spam.

Cookie Preferences

We use cookies to analyze site usage and improve your experience. You can accept all, reject all, or customize your preferences.