fix: restore ScoringConfig improvements lost in refactor commit docs: rewrite README and CLAUDE.md to reflect current architecture code-format code fixes
Market Screener & Personal Finance Assistant
A Node.js CLI tool that screens stocks, ETFs, bonds, and crypto using live Yahoo Finance data. It scores each asset under two lenses — Market-Adjusted (what's acceptable in today's inflated market) and Fundamental (Graham-style strict value investing) — and gives you an honest signal by comparing both.
It also connects to your brokerage accounts via SimpleFIN to track net worth, spending, and give hold/sell/add advice on your actual portfolio.
Quick Start
npm install
cp .env.example .env # add SIMPLEFIN_SETUP_TOKEN if you have SimpleFIN
npm start # screen today's catalyst tickers from Yahoo news
Commands
| Command | What it does |
|---|---|
npm start |
Fetches today's market news, extracts catalyst tickers, screens them |
npm start -- watch |
Screens the default watchlist instead |
npm start -- AAPL MSFT VOO |
Screens specific tickers |
npm run finance |
Portfolio advice + SimpleFIN account data → finance-report.html |
npm run import-portfolio -- file.csv |
Imports Robinhood/Vanguard/Fidelity CSV into portfolio.json |
npm test |
Runs all unit tests (51 tests, zero external dependencies) |
npm run test:watch |
Re-runs tests on file changes during development |
npm run format |
Formats all source files with Prettier |
npm run format:check |
Checks formatting without writing (useful in CI) |
Both commands generate self-contained HTML reports that open in any browser.
How the Screener Works
Every asset is scored twice:
Market-Adjusted — gates derived from live Yahoo Finance benchmarks:
- Stock P/E gate = S&P 500 P/E (via SPY) × 1.5
- Tech P/E gate = XLK sector P/E × 1.3
- REIT min yield = XLRE dividend yield × 0.85
- Bond min spread = LQD − TNX live spread × 0.80
Fundamental — strict Graham/value-investing gates from src/config/ScoringConfig.js:
- Stock P/E < 20x, PEG < 1.5
- Bond spread > 1.0% above risk-free rate
The comparison produces a Signal:
| Signal | Meaning |
|---|---|
| ✅ Strong Buy | Passes both — genuinely good value |
| ⚡ Momentum | Passes market-adjusted, holds fundamentally |
| ⚠️ Speculation | Passes market-adjusted, fails fundamental — priced for perfection |
| 🔄 Neutral | Hold territory in one or both lenses |
| ❌ Avoid | Fails both |
Personal Finance
Edit portfolio.json with your holdings (or import from a broker CSV):
{
"holdings": [
{ "ticker": "AAPL", "shares": 10, "costBasis": 150.00, "source": "Robinhood", "type": "stock" },
{ "ticker": "VOO", "shares": 8, "costBasis": 380.00, "source": "Vanguard", "type": "etf" },
{ "ticker": "BTC-USD", "shares": 0.25, "costBasis": 45000, "source": "Coinbase", "type": "crypto" }
]
}
npm run finance screens your holdings, fetches crypto prices, and generates hold/sell/add advice based on the screener signal crossed with your gain/loss position.
SimpleFIN (optional)
Connects to your real bank and brokerage accounts for net worth, balances, and 30-day spending breakdown.
- Get your setup token from beta-bridge.simplefin.org
- Add to
.env:SIMPLEFIN_SETUP_TOKEN=aHR0cHM6Ly... - Run
npm run finance— the Access URL is claimed and saved automatically
Importing broker holdings
npm run import-portfolio -- ~/Downloads/robinhood_holdings.csv
npm run import-portfolio -- ~/Downloads/vanguard_holdings.csv
Broker is auto-detected from CSV headers. Running multiple imports merges them into portfolio.json.
Project Structure
├── bin/
│ ├── screen.js # Market screener entry point
│ ├── finance.js # Personal finance entry point
│ └── import-portfolio.js # Broker CSV importer
│
├── prompts/
│ └── catalyst-analysis.md # Daily catalyst analysis playbook
│
├── src/
│ ├── config/
│ │ └── ScoringConfig.js # All scoring gates, weights, thresholds
│ │
│ ├── market/ # Yahoo Finance data layer
│ │ ├── YahooClient.js
│ │ ├── BenchmarkProvider.js
│ │ └── MarketRegime.js # Derives inflated gate overrides from live data
│ │
│ ├── screener/ # Core screening domain
│ │ ├── ScreenerEngine.js
│ │ ├── DataMapper.js
│ │ ├── RuleMerger.js
│ │ ├── Chunker.js
│ │ ├── assets/ # Stock, Etf, Bond data containers
│ │ └── scorers/ # StockScorer, EtfScorer, BondScorer
│ │
│ ├── analyst/
│ │ └── CatalystAnalyst.js # Extracts tickers from Yahoo Finance news
│ │
│ ├── finance/
│ │ ├── clients/
│ │ │ └── SimpleFINClient.js
│ │ ├── PersonalFinanceAnalyzer.js
│ │ ├── PortfolioAdvisor.js
│ │ └── PortfolioImporter.js
│ │
│ └── reporters/
│ ├── HtmlReporter.js # screener-report.html
│ └── FinanceReporter.js # finance-report.html
│
├── portfolio.json # Your holdings (edit this)
└── .env # SIMPLEFIN_SETUP_TOKEN / SIMPLEFIN_ACCESS_URL
Metrics Scored per Stock
| Metric | Source | Why it matters |
|---|---|---|
| P/E ratio | Yahoo forwardPE / trailingPE | Valuation |
| PEG ratio | Yahoo or computed (trailingPE ÷ earningsGrowth) | Valuation vs growth |
| Price-to-Book | Yahoo | Graham's primary value metric |
| ROE | Yahoo returnOnEquity | Buffett's primary quality metric |
| Operating margin | Yahoo operatingMargins | Pricing power |
| Net profit margin | Yahoo profitMargins | Bottom-line profitability |
| Revenue growth | Yahoo revenueGrowth | Top-line momentum |
| FCF yield | Computed (freeCashflow ÷ market cap) | Cash generation quality |
| Debt/Equity | Yahoo debtToEquity | Balance sheet risk |
| Quick ratio | Yahoo quickRatio (falls back to currentRatio) | Liquidity |
| Beta | Yahoo beta | Market sensitivity |
| 52-week position | Yahoo fiftyTwoWeekHigh/Low | Momentum / opportunity flag |
Sector overrides apply: REIT scores on yield + P/FFO, FINANCIAL on ROE + P/B, TECHNOLOGY with realistic D/E tolerance.