#!/usr/bin/env python3 """GARP scan on broad ticker list.""" import yfinance as yf import json, time EXCLUDE = {"BAC", "CFG", "FITB", "INCY"} with open("broad_tickers.txt") as f: tickers = [l.strip().replace(".", "-") for l in f if l.strip()] print(f"Screening {len(tickers)} tickers...") passed = [] for i, tick in enumerate(tickers): if tick in EXCLUDE: continue if i % 100 == 0: print(f" Progress: {i}/{len(tickers)} ({len(passed)} passed so far)") try: t = yf.Ticker(tick) info = t.info or {} mc = info.get("marketCap", 0) or 0 if mc < 5e9: continue tpe = info.get("trailingPE") if not tpe or tpe >= 25 or tpe <= 0: continue fpe = info.get("forwardPE") if not fpe or fpe >= 15 or fpe <= 0: continue rg = info.get("revenueGrowth") if rg is None or rg < 0.10: continue eg = info.get("earningsGrowth") if eg is None or eg < 0.15: continue roe = info.get("returnOnEquity") if roe is None or roe < 0.05: continue peg = info.get("pegRatio") if peg is not None and peg > 1.2: continue dte = info.get("debtToEquity") if dte is not None and dte > 35: continue entry = { "ticker": tick, "name": info.get("longName", tick), "market_cap_B": round(mc / 1e9, 1), "trailing_pe": round(tpe, 2), "forward_pe": round(fpe, 2), "peg": round(peg, 2) if peg else None, "revenue_growth_pct": round(rg * 100, 1), "earnings_growth_pct": round(eg * 100, 1), "roe_pct": round(roe * 100, 1), "debt_to_equity": round(dte, 1) if dte else None, "sector": info.get("sector"), "industry": info.get("industry"), } passed.append(entry) print(f" ✅ {tick}: PE={tpe:.1f} FPE={fpe:.1f} RG={rg*100:.0f}% EG={eg*100:.0f}% ROE={roe*100:.0f}%") except Exception as e: continue passed.sort(key=lambda x: x.get("forward_pe", 99)) with open("garp-expanded-scan.json", "w") as f: json.dump(passed, f, indent=2) print(f"\nDone! {len(passed)} stocks passed GARP screen") for s in passed: print(f" {s['ticker']:6s} ${s['market_cap_B']:6.1f}B PE:{s['trailing_pe']:5.1f} FPE:{s['forward_pe']:5.1f} RG:{s['revenue_growth_pct']:5.1f}% EG:{s['earnings_growth_pct']:5.1f}% ROE:{s['roe_pct']:5.1f}%")