Market Watch: multiplayer GARP paper trading simulator
- Game engine with multiplayer support (create games, join, leaderboard) - GARP stock screener (S&P 500 + 400 MidCap, 900+ tickers) - Automated trading logic for AI player (Case) - Web portal at marketwatch.local:8889 with dark theme - Systemd timer for Mon-Fri market hours - Telegram alerts on trades and daily summary - Stock analysis deep dive data (BAC, CFG, FITB, INCY) - Expanded scan results (22 GARP candidates) - Craigslist account setup + credentials
This commit is contained in:
85
data/garp_scan2.py
Normal file
85
data/garp_scan2.py
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""GARP scan - batch download approach for speed."""
|
||||
import yfinance as yf
|
||||
import json, sys
|
||||
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
|
||||
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 in batches...")
|
||||
|
||||
passed = []
|
||||
batch_size = 20
|
||||
|
||||
for batch_start in range(0, len(tickers), batch_size):
|
||||
batch = tickers[batch_start:batch_start + batch_size]
|
||||
batch = [t for t in batch if t not in EXCLUDE]
|
||||
if not batch:
|
||||
continue
|
||||
|
||||
print(f"Batch {batch_start}-{batch_start+len(batch)} / {len(tickers)}...")
|
||||
|
||||
try:
|
||||
data = yf.Tickers(" ".join(batch))
|
||||
for tick in batch:
|
||||
try:
|
||||
info = data.tickers[tick].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:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f" Batch error: {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}%")
|
||||
Reference in New Issue
Block a user