A step-by-step guide to building a relative strength stock screener in 30 minutes — define your universe and benchmark, pull and normalize OHLCV data, compute RS/composite ranks, and export/schedule a screener table you can iterate with better signal filters.
A step-by-step guide to building a relative strength stock screener in 30 minutes — define your universe and benchmark, pull and normalize OHLCV data, compute RS/composite ranks, and export/schedule a screener table you can iterate with better signal filters.

If you’ve ever “felt” a stock was strong but couldn’t prove it, you’re not alone—and that’s exactly where most screeners fall short. Without a clear benchmark, timeframe, and ranking method, you end up chasing noise.
In the next 30 minutes, you’ll build a relative strength screener that’s explicit about what it measures and why. You’ll fetch price data, compute RS scores and composite ranks, generate a clean table, export it for review, and then automate runs with caching and alerts so it stays useful week after week.
You’re building a screener you can rerun next week and get comparable results. That requires fixed choices: universe, benchmark, timeframe, and liquidity rules.
Example goal: “Find large-cap stocks beating SPY over 3–12 months, with enough volume to trade.”
Your universe decides what “good” even means, and it prevents accidental cherry-picking. Pick one source, then write rules you won’t change mid-test.
If your tickers change silently, your “edge” becomes a moving target.
Relative strength is always “versus something,” so pick the yardstick that matches your intent. Use SPY for broad large-cap, QQQ for growth-heavy tech, or a sector ETF for intra-sector strength.
Define the formula in plain terms, then keep it constant:
If you switch benchmarks later, you’re asking a different question.
Timeframes control your signal’s personality. Short windows chase momentum bursts, and long windows reward sustained leaders.
When in doubt, rank each window, then average ranks for stability.
Filters stop your screener from recommending stuff you can’t trade or shouldn’t touch. Write them as hard thresholds, like “price > $5” and “$ volume > $20M/day.”
A practical starter set:
Your signal isn’t useful if execution breaks it.
Your screener lives or dies on clean adjusted prices. You want one table where each column is a ticker, plus your benchmark, with aligned dates and no surprises. Think: “Adj Close only, one row per trading day.”
You need adjusted closes, stable symbols, and a sane rate limit. Pick the source you can rerun daily without babysitting.
| Source | Adjusted prices | Reliability | Limits |
|---|---|---|---|
| Stooq | Yes | High | None obvious |
| Yahoo | Yes | Medium | Unofficial, flaky |
| Alpha Vantage | Yes | Medium | Tight free quota |
If you want frictionless reruns, Stooq is the boring choice that keeps working.
Install the packages once, then prove imports work in your environment. That one-minute test saves an hour later.
pip install pandas numpy pandas-datareader yfinance.import pandas as pd; import numpy as np.import pandas_datareader as pdr; import yfinance as yf.If imports fail now, your screener won’t “magically” work later.
Fetch daily adjusted close for every ticker and your benchmark. Keep it in one DataFrame, because relative strength is column math.
tickers = [...] and benchmark = 'SPY' (or your market proxy).yf.download(tickers+[benchmark], auto_adjust=False)['Adj Close'].adj.to_parquet('adj_close.parquet').Caching turns “data work” into a two-second reload.
Markets have holidays, IPOs, and stale series. You need aligned dates and a rule for missing history, or your rankings will lie.
Start by dropping duplicated dates and sorting the index. Then align everything to the benchmark’s trading calendar, because that’s your comparison baseline. Finally, enforce a minimum lookback, like 252 trading days, and drop tickers that don’t meet it.
The first time you see a top-ranked stock with 40 days of data, you’ll be glad you added that filter.
Relative strength is just performance in context. You’re comparing each ticker to the same yardstick, then ranking the spread.
Your output should make leaders obvious and laggards undeniable. Think “AAPL +6% vs SPY” instead of “AAPL +6%.”
Compute returns per lookback so every ticker and your benchmark share identical windows. Do it on adjusted closes, or splits and dividends will lie to you.
If your dates don’t line up, your ranks won’t either.
RS is the gap between a ticker’s return and the benchmark’s return. That gap is what you rank, because it shows who is truly leading.
Core formula:
Optional ratio version:
Use diff when you want “percentage points of outperformance.” Use ratio when you care about compounding symmetry.
Pick one and stick with it, or you’ll compare apples to math tricks.

Single-window RS is noisy, so you blend timeframes into one score. Weights force your screener to match your holding period.
Your weights are your strategy in disguise.
You’re hunting for “obviously wrong,” not statistical perfection. A quick check beats a week of debugging blind.
Spot-check a few names:
Then scan for data issues:
If the extremes don’t make intuitive sense, trust the bug, not the rank.
You need one table that answers, “What do I buy, and why this ticker?” Make it sortable, filterable, and boringly consistent across every row.
Use this structure for your final screener output.
| Column | Type | Example | Sort/Filter |
|---|---|---|---|
| Ticker | Text | NVDA | Filter |
| RS Score (0–100) | Number | 87.4 | Sort desc |
| RS Rank | Integer | 12 | Sort asc |
| Trend Filter | Pass/Fail | Pass | Filter |
| Liquidity ($ vol) | Number | 45M | Filter min |
If you can’t sort by RS and instantly drop illiquid names, you built a spreadsheet, not a screener.
Save your picks somewhere you can sort, filter, and sanity-check. Then rerun the screener to verify it keeps surfacing the same leaders.
If your top names churn on identical settings, your data or calculations are drifting.

A screener only helps if it stays fresh. You want one weekly job that pulls new prices, recalculates ranks, and drops a file you can open fast.
Pick a consistent output path like ./out/rs_screener_latest.csv. Your future self will thank you.
One command should run the whole pipeline. Parameters keep you from editing code every week.
run_screener.py that calls: download → cache → rank → export.--universe, --benchmark, --start, --end, --out.--end to today, and --start to end - 400d.Once it’s one command, scheduling becomes boring. Boring ships.
For concrete examples of weekly timing, see these weekly cron expressions.
Repeated downloads waste time and invite rate limits. Cache locally, then only fetch what’s missing.
Use a simple layout like:
./data/raw/{symbol}.parquet./data/meta/last_updated.jsonOn each run:
--end.Speed changes your habits. Fast reruns make you test more ideas.
Weekly automation removes “I forgot to run it” risk. Use the scheduler your machine already trusts.
0 8 * * 6 /usr/bin/python3 /path/run_screener.py ....python.exe with args./reports/rs/./reports/rs/logs/run_YYYYMMDD.txt for debugging.Consistency beats cleverness here. One folder, one schedule, no surprises.
A file is passive. Alerts tell you when leadership shifts, which is the point of relative strength.
If you aren’t notified on change, you’re just warehousing data.
Relative strength finds what’s winning, even when it’s a one-week fluke. Add trend checks and simple risk gates, so your “leaders” behave like leaders.
Use trend confirmation to stop buying “strong” charts in downtrends. You’re filtering for names with both long-term support and fresh momentum.
When RS and trend agree, you’re trading tailwinds, not rebounds.
Volatility filters keep “lottery tickets” from topping your list. Pick one metric, then cap it.
You want smooth strength, because smooth strength is tradable.
Relative strength is often a sector story in disguise. Add sector labels and a sector-RS column, so you see when a stock is surfing a group wave.
Example: If a stock ranks top-10, but its sector ranks bottom-quartile, treat it as suspect. The opposite is better: “top stock in a top sector.”
Sector context turns your screener into a map, not a list.
Outliers can hijack rankings after a single spike or crash. Clip extremes and enforce enough history, so your RS score reflects a real window.
If one day can change the leaderboard, your screener is measuring noise.
Building a relative strength stock screener is the easy part; keeping data fresh, improving signal quality, and reviewing leaders fast is where most workflows break.
Open Swing Trading delivers daily RS rankings, breadth, and sector/theme rotation context across ~5,000 stocks so you can shortlist breakout leaders in minutes—get 7-day free access with no credit card.