650 lines
20 KiB
Markdown
650 lines
20 KiB
Markdown
# Market Screener
|
||
|
||
A personal stock screener and portfolio tracker. Scores stocks, ETFs, and bonds under two lenses — **Market-Adjusted** (what's acceptable in today's market) and **Fundamental** (strict Graham value-investing) — then compares them to produce an actionable signal. Comes with a live SvelteKit dashboard.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
- [Developer Setup](#developer-setup)
|
||
- [Environment Variables](#environment-variables)
|
||
- [Commands](#commands)
|
||
- [Running Tests](#running-tests)
|
||
- [Project Structure](#project-structure)
|
||
- [User Guide](#user-guide)
|
||
- [API Testing with Bruno](#api-testing-with-bruno)
|
||
|
||
---
|
||
|
||
## Developer Setup
|
||
|
||
### Prerequisites
|
||
|
||
- **Node.js 20+** (v22 recommended)
|
||
- **npm 10+**
|
||
|
||
**Check your versions:**
|
||
```bash
|
||
node --version # Should output v20.x.x or higher
|
||
npm --version # Should output 10.x.x or higher
|
||
```
|
||
|
||
**Not on Node 20+?** See [NODE_VERSION_FIX.md](./NODE_VERSION_FIX.md) for upgrade instructions.
|
||
|
||
### Install
|
||
|
||
```bash
|
||
# Install server dependencies
|
||
npm install
|
||
|
||
# Install UI dependencies (first time only)
|
||
npm run ui:install
|
||
```
|
||
|
||
### Start
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
This starts both the API server on **port 3000** and the SvelteKit UI on **port 5173** concurrently. Open [http://localhost:5173](http://localhost:5173).
|
||
|
||
To run the API server alone:
|
||
|
||
```bash
|
||
npm run server
|
||
```
|
||
|
||
---
|
||
|
||
## Environment Variables
|
||
|
||
Create a `.env` file in the project root. None are required to run the app — it works with Yahoo Finance data out of the box. Optional keys unlock additional features.
|
||
|
||
### `ANTHROPIC_API_KEY` — LLM news analysis *(optional)*
|
||
|
||
Powers the **Analyze** button on each screener section. Without this key the button is disabled.
|
||
|
||
1. Go to [console.anthropic.com](https://console.anthropic.com)
|
||
2. Create an API key under **API Keys**
|
||
3. Add to `.env`:
|
||
|
||
```env
|
||
ANTHROPIC_API_KEY=sk-ant-...
|
||
```
|
||
|
||
### `SIMPLEFIN_SETUP_TOKEN` — Live bank/brokerage balances *(optional)*
|
||
|
||
Powers the personal finance section of the Portfolio page (net worth, account balances, spending breakdown).
|
||
|
||
1. Go to [beta-bridge.simplefin.org](https://beta-bridge.simplefin.org) and create a Setup Token
|
||
2. Add to `.env`:
|
||
|
||
```env
|
||
SIMPLEFIN_SETUP_TOKEN=aHR0cHM6Ly...
|
||
```
|
||
|
||
On first request the token is claimed automatically and the resulting Access URL is saved back to `.env` as `SIMPLEFIN_ACCESS_URL`. Subsequent restarts use the Access URL directly.
|
||
|
||
### `API_KEY` — Bearer token auth *(optional)*
|
||
|
||
When set, every API route requires `Authorization: Bearer <key>`. Useful when the server is exposed to a network. `/health` and OPTIONS preflight are exempt.
|
||
|
||
```env
|
||
API_KEY=your-secret-key
|
||
```
|
||
|
||
### `CLIENT_ORIGIN` — CORS allowed origin *(optional)*
|
||
|
||
Defaults to `http://localhost:5173`. Change if the UI is served from a different origin.
|
||
|
||
```env
|
||
CLIENT_ORIGIN=https://yourdomain.com
|
||
```
|
||
|
||
### `EDGAR_USER_AGENT` — SEC filings poller *(recommended)*
|
||
|
||
The news pipeline polls SEC EDGAR for 8-K / SC 13D / S-4 / DEFM14A filings.
|
||
The SEC requires a descriptive User-Agent with contact info:
|
||
|
||
```env
|
||
EDGAR_USER_AGENT=market-screener/1.0 you@example.com
|
||
```
|
||
|
||
### `DISCORD_WEBHOOK_URL` — Daily digest alerts *(optional)*
|
||
|
||
The daily change digest (`npm run digest:daily`) posts signal flips + their
|
||
news catalysts to Discord. Create: channel → Settings → Integrations →
|
||
Webhooks → New Webhook → copy URL. Paste it RAW (no quotes, no escaping).
|
||
Forum channels are supported (each digest becomes a dated post).
|
||
Test with `npm run discord:test`.
|
||
|
||
```env
|
||
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
|
||
```
|
||
|
||
### `NEWS_PRWIRE_FEEDS` — Override press-release RSS feeds *(optional)*
|
||
|
||
Comma-separated RSS URLs. Defaults to GlobeNewswire + PR Newswire. Only
|
||
needed if a default feed goes stale or you want to add one.
|
||
|
||
### `NEWS_POLL` — Disable in-server news polling *(optional)*
|
||
|
||
Set `NEWS_POLL=off` if you prefer running `npm run news:poll` from cron
|
||
instead of polling inside the server (EDGAR 10 min, PR-wire 15 min).
|
||
|
||
### Complete `.env` example
|
||
|
||
```env
|
||
ANTHROPIC_API_KEY=sk-ant-...
|
||
SIMPLEFIN_SETUP_TOKEN=aHR0cHM6Ly...
|
||
API_KEY=optional-secret
|
||
CLIENT_ORIGIN=http://localhost:5173
|
||
EDGAR_USER_AGENT=market-screener/1.0 you@example.com
|
||
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
|
||
```
|
||
|
||
---
|
||
|
||
## Commands
|
||
|
||
| Command | Description |
|
||
|---|---|
|
||
| `npm run dev` | Start API (port 3000) + UI (port 5173) together |
|
||
| `npm run server` | Start API server only |
|
||
| `npm run ui:install` | Install UI dependencies (first time / after `git pull`) |
|
||
| `npm test` | Run all unit + integration tests |
|
||
| `npm run test:watch` | Watch mode — re-run on file changes |
|
||
| `npm run typecheck` | TypeScript type check without emitting |
|
||
| `npm run format` | Format all source files with Prettier |
|
||
| `npm run format:check` | Check formatting without writing (used in CI) |
|
||
| `npm run lint` | Run ESLint on all TypeScript files |
|
||
| `npm run lint:fix` | Auto-fix ESLint issues |
|
||
| `npm run screen:daily` | Screen watchlist + holdings, write signal snapshots (cron at market close) |
|
||
| `npm run news:poll` | One-shot news poll: EDGAR + PR wires → news DB (cron alternative) |
|
||
| `npm run digest:daily` | Daily change digest: signal flips + catalysts → terminal/Discord (run after screen:daily) |
|
||
| `npm run discord:test` | Send a fake digest to verify the Discord webhook |
|
||
|
||
**Recommended cron (weekdays, market close):**
|
||
|
||
```
|
||
30 16 * * 1-5 cd /path/to/market_screener && npm run screen:daily && npm run digest:daily
|
||
```
|
||
|
||
---
|
||
|
||
## Running Tests
|
||
|
||
```bash
|
||
npm test
|
||
```
|
||
|
||
Uses Node's built-in `node:test` runner — no external framework. **114 test cases** across 9 files cover:
|
||
|
||
| Test File | Tests | Coverage |
|
||
|-----------|-------|----------|
|
||
| `app.test.ts` | 9 | App bootstrap, CORS, health endpoints |
|
||
| `screener-controller.test.ts` | 10 | `/api/screen` endpoints |
|
||
| `screener-engine.test.ts` | 11 | Screening orchestration logic |
|
||
| `stock-scorer.test.ts` | 13 | Stock valuation gates |
|
||
| `etf-scorer.test.ts` | 17 | ETF fund gates |
|
||
| `bond-scorer.test.ts` | 16 | Bond credit analysis |
|
||
| `portfolio-advisor.test.ts` | 12 | Portfolio advice logic |
|
||
| `portfolio-controller.test.ts` | 12 | Portfolio endpoints |
|
||
| `calls-controller.test.ts` | 14 | Market calls endpoints |
|
||
|
||
### Pre-Commit & Pre-Push Hooks
|
||
|
||
On `git commit`, the **pre-commit hook** automatically:
|
||
|
||
1. **Formats** all files with Prettier
|
||
2. **Lints & fixes** staged files with ESLint
|
||
3. **Runs tests** to catch errors early
|
||
|
||
On `git push`, the **pre-push hook** runs tests again for safety.
|
||
|
||
---
|
||
|
||
## Project Structure
|
||
|
||
**Phase 9: Domain-Driven Architecture** (completed)
|
||
|
||
```
|
||
bin/
|
||
server.ts API server entry point
|
||
|
||
server/
|
||
app.ts Fastify app factory — wires DI, rate limiting, auth hook
|
||
domains/ Domain-driven structure (shared, screener, portfolio, calls, finance)
|
||
shared/ Infrastructure & cross-domain utilities
|
||
adapters/ YahooFinanceClient, AnthropicClient, SimpleFINClient
|
||
services/ BenchmarkProvider, CatalystAnalyst, LLMAnalyst
|
||
entities/ Asset, Stock, Etf, Bond
|
||
persistence/ MarketCallRepository, PortfolioRepository
|
||
config/ ScoringConfig (gates/weights), constants
|
||
scoring/ MarketRegime, scoring overrides
|
||
types/ TypeScript interfaces (one file per domain)
|
||
screener/ Stock/ETF/Bond filtering & scoring
|
||
ScreenerEngine.ts Orchestrates: fetch → score × 2 (fundamental + inflated)
|
||
scorers/ StockScorer, EtfScorer, BondScorer
|
||
transform/ DataMapper, RuleMerger
|
||
portfolio/ Holdings management & investment advice
|
||
PortfolioAdvisor.ts Cross-references holdings with screener signals
|
||
calls/ Market call tracking & earnings calendar
|
||
CalendarService.ts Earnings calendar logic
|
||
finance/ Portfolio metrics & reporting
|
||
|
||
ui/
|
||
src/
|
||
routes/ SvelteKit pages: /, /portfolio, /calls, /safe-buys
|
||
lib/
|
||
components/ Shared UI components organized by domain
|
||
stores/ Svelte 5 reactive stores
|
||
api/ Fetch wrappers for each API domain
|
||
styles/ Global SCSS design tokens and partials
|
||
|
||
tests/ Unit + integration tests (9 files, 114 test cases)
|
||
Controllers, services, scorers fully covered
|
||
|
||
portfolio.json Your holdings (gitignored — create manually or via the UI)
|
||
market-calls.json Persisted market thesis calls (gitignored)
|
||
.benchmark-cache.json Benchmark data cache — survives server restart (gitignored)
|
||
```
|
||
|
||
See **[CLAUDE.md](./CLAUDE.md)** for detailed architecture and **[PHASES.md](./PHASES.md)** for the complete roadmap.
|
||
|
||
---
|
||
|
||
## User Guide
|
||
|
||
### Screener tab
|
||
|
||
The main view. On load it automatically fetches today's financial news, extracts the most-mentioned tickers, and screens them.
|
||
|
||
#### Market context strip
|
||
|
||
The row of chips at the top shows live benchmark data fetched from Yahoo Finance:
|
||
|
||
| Chip | Meaning |
|
||
|---|---|
|
||
| 10Y | 10-year Treasury yield — the risk-free rate |
|
||
| VIX | Volatility index — market fear gauge |
|
||
| S&P | S&P 500 index price |
|
||
| S&P P/E | Trailing P/E of the S&P 500 (via SPY) |
|
||
| Tech P/E | Trailing P/E of the tech sector (via XLK) |
|
||
| REIT Yld | REIT dividend yield (via XLRE) |
|
||
| IG Sprd | Investment-grade bond spread above risk-free (LQD − TNX) |
|
||
| Rates | Rate regime: LOW / NORMAL / HIGH (based on 10Y yield) |
|
||
| Vol | Volatility regime: LOW / NORMAL / HIGH (based on VIX) |
|
||
|
||
The rate regime affects how strict the Market-Adjusted gates are — in a HIGH rate environment the P/E multiplier compresses and bond spreads tighten.
|
||
|
||
#### Signal Summary table
|
||
|
||
Quick overview of all screened tickers with their signal, market-adjusted verdict, fundamental verdict, market cap tier, and growth style.
|
||
|
||
#### Per-asset detail tables
|
||
|
||
Expand each section (STOCK / ETF / BOND) for full metrics: P/E, PEG, ROE, margins, FCF yield, D/E, analyst consensus, DCF intrinsic value, 52-week movement, and more.
|
||
|
||
#### Analyze button
|
||
|
||
Runs an Anthropic LLM over the latest Yahoo Finance news for assets in that section. Returns a sentiment summary, affected industries, and related tickers to watch. Requires `ANTHROPIC_API_KEY`.
|
||
|
||
#### Search tickers
|
||
|
||
Click **Search tickers** to screen any custom list — type tickers comma or space separated and press Enter or click Screen.
|
||
|
||
#### Signals explained
|
||
|
||
| Signal | What it means |
|
||
|---|---|
|
||
| ✅ Strong Buy | Passes both Market-Adjusted AND Fundamental gates — genuine value at current prices |
|
||
| ⚡ Momentum | Passes Market-Adjusted, holds fundamentally — good in the current market but not a bargain |
|
||
| ⚠️ Speculation | Passes Market-Adjusted, fails Fundamental — priced for perfection, high risk |
|
||
| 🔄 Neutral | Borderline in one or both lenses — hold, no clear edge |
|
||
| ❌ Avoid | Fails both lenses |
|
||
|
||
#### How scoring works
|
||
|
||
Every asset is scored twice:
|
||
|
||
**Market-Adjusted** gates move with the market. The stock P/E gate = SPY trailing P/E × 1.5 (compresses to × 1.2 in a HIGH rate regime). Tech P/E = XLK P/E × 1.3. This reflects what the market is currently willing to pay.
|
||
|
||
**Fundamental** gates are fixed Graham/value-investing standards that never change:
|
||
|
||
| Gate | Threshold | Rationale |
|
||
|---|---|---|
|
||
| Stock P/E | < 15× | Graham's actual rule |
|
||
| Stock PEG | < 1.0 | Lynch: PEG > 1.0 = paying full price |
|
||
| D/E ratio | < 1.5× | Distress typically starts above 2× |
|
||
| Quick ratio | > 0.8 | Below 0.8 = real liquidity stress |
|
||
|
||
Sector overrides apply in both modes — e.g. tech stocks allow P/E up to 35× and D/E up to 2.0, REITs are scored on yield rather than P/E.
|
||
|
||
---
|
||
|
||
### Portfolio tab
|
||
|
||
Track your holdings and get hold/sell/add advice cross-referenced with screener signals.
|
||
|
||
**Adding holdings** — click **+ Add Holding** and fill in ticker, shares, cost basis, asset type, and source broker. Holdings are saved to `portfolio.json` on disk.
|
||
|
||
**Inline editing** — click the ✎ pencil icon on any row to edit shares, cost basis, type, or source directly in the table.
|
||
|
||
**Advice column** — each holding is screened live and the signal is combined with your gain/loss position:
|
||
|
||
| Situation | Advice |
|
||
|---|---|
|
||
| ✅ Strong Buy signal | Hold & Add |
|
||
| ⚡ Momentum + > 30% gain | Consider partial profit-taking |
|
||
| ⚠️ Speculation + > 20% gain | Reduce position |
|
||
| ❌ Avoid signal + in profit | Sell (Take Profits) |
|
||
| ❌ Avoid signal + at a loss | Sell (Cut Loss) |
|
||
| Crypto | Hold / Review position (no fundamental scoring) |
|
||
|
||
**Personal finance section** *(requires SimpleFIN)* — when configured, the page also shows net worth, total assets vs liabilities, cash vs investments ratio, monthly income/spend, account balances, and a spending breakdown by category for the last 30 days.
|
||
|
||
---
|
||
|
||
### Market Calls tab
|
||
|
||
Record and track quarterly investment theses from the day you make the call.
|
||
|
||
**Creating a call** — click **+ New Call** and fill in:
|
||
- **Title** — e.g. "Q3 2025 — Rate pivot & tech rotation"
|
||
- **Quarter** — the quarter this thesis applies to
|
||
- **Thesis** — the macro reasoning behind the call (min 10 characters)
|
||
- **Tickers** — the assets you're watching for this thesis
|
||
|
||
When saved, the current price and signal for each ticker are snapshotted automatically.
|
||
|
||
**Viewing performance** — click any call card to see the current price and signal for each ticker alongside the original snapshot, so you can measure how the thesis played out.
|
||
|
||
**Calendar** — shows upcoming earnings dates, ex-dividend dates, and dividend payment dates for all tickers across your active calls.
|
||
|
||
---
|
||
|
||
### Safe Buys tab
|
||
|
||
A filtered view showing only tickers with a **✅ Strong Buy** signal across both lenses. A quick watchlist of assets passing the strictest criteria in the current market.
|
||
|
||
---
|
||
|
||
### API rate limits
|
||
|
||
`/api/screen`, `/api/screen/catalysts`, and `/api/analyze` are capped at **10 requests per minute** per IP. All other routes allow 60 per minute.
|
||
|
||
---
|
||
|
||
## API Testing with Bruno
|
||
|
||
### What is Bruno?
|
||
|
||
[Bruno](https://www.usebruno.com/) is a lightweight, open-source API client that's Git-friendly and perfect for testing REST APIs. It stores collections as plain text files instead of JSON blobs, making them easy to version control and collaborate on.
|
||
|
||
### Installing Bruno
|
||
|
||
#### macOS (via Homebrew)
|
||
```bash
|
||
brew install bruno
|
||
```
|
||
|
||
#### macOS (Direct Download)
|
||
1. Visit [usebruno.com/downloads](https://www.usebruno.com/downloads)
|
||
2. Download the macOS version
|
||
3. Drag `Bruno.app` to Applications folder
|
||
|
||
#### Windows
|
||
1. Visit [usebruno.com/downloads](https://www.usebruno.com/downloads)
|
||
2. Download the Windows installer (.exe)
|
||
3. Run the installer and follow the prompts
|
||
4. Or via Chocolatey: `choco install bruno`
|
||
|
||
#### Linux (Ubuntu/Debian)
|
||
```bash
|
||
# Add Bruno repository
|
||
curl -1sLf 'https://dl.usebruno.com/install.sh' | sudo bash
|
||
|
||
# Install
|
||
sudo apt-get install bruno
|
||
```
|
||
|
||
#### Linux (Fedora/RHEL)
|
||
```bash
|
||
curl -1sLf 'https://dl.usebruno.com/install.sh' | sudo bash
|
||
sudo dnf install bruno
|
||
```
|
||
|
||
### Installing Bruno CLI (brucli)
|
||
|
||
For running tests from the command line without the GUI:
|
||
|
||
#### macOS
|
||
```bash
|
||
brew install brucli
|
||
```
|
||
|
||
#### Windows
|
||
```bash
|
||
choco install bruno-cli
|
||
```
|
||
|
||
#### Linux
|
||
```bash
|
||
curl -1sLf 'https://dl.usebruno.com/install.sh' | sudo bash
|
||
```
|
||
|
||
### Importing the API Collection
|
||
|
||
#### Method 1: Import via Bruno GUI (Easiest)
|
||
|
||
1. **Open Bruno**
|
||
2. **File → Import Collection**
|
||
3. **Select** `api_collections/market-screener.postman_collection.json`
|
||
4. **Choose location** where to save the converted collection (e.g., `api_collections/market-screener`)
|
||
5. **Click Import** — Bruno automatically converts and structures the collection
|
||
|
||
#### Method 2: Import via Bruno CLI
|
||
|
||
```bash
|
||
# Navigate to the project root
|
||
cd market-screener
|
||
|
||
# Import the Postman collection
|
||
bru import api_collections/market-screener.postman_collection.json -o api_collections/market-screener
|
||
|
||
# Output: Collection imported to api_collections/market-screener/
|
||
```
|
||
|
||
#### Method 3: Convert Postman to Bruno Format (Manual)
|
||
|
||
If you prefer to manually convert the collection:
|
||
|
||
```bash
|
||
# Install conversion dependencies (if needed)
|
||
pip install requests
|
||
|
||
# Run the conversion script
|
||
python3 api_collections/convert_postman_to_bruno.py \
|
||
api_collections/market-screener.postman_collection.json \
|
||
api_collections/market-screener
|
||
```
|
||
|
||
### Running Tests
|
||
|
||
#### Via Bruno GUI
|
||
|
||
1. **Open the imported collection** in Bruno
|
||
2. **Set the `baseUrl` variable** (default: `http://localhost:3000`)
|
||
3. **Click the Play button** to run all tests
|
||
4. **View results** for each request in the UI
|
||
|
||
#### Via Bruno CLI (brucli)
|
||
|
||
```bash
|
||
# Navigate to the collection directory
|
||
cd api_collections/market-screener
|
||
|
||
# Run all tests in the collection
|
||
bru run
|
||
|
||
# Run with specific environment
|
||
bru run --env local
|
||
|
||
# Run with output format
|
||
bru run --output json > test-results.json
|
||
|
||
# Run specific test file
|
||
bru run "Screener/Screen - Mixed.bru"
|
||
```
|
||
|
||
### Collection Structure
|
||
|
||
After import, you'll have:
|
||
|
||
```
|
||
api_collections/market-screener/
|
||
├── bruno.json # Collection metadata
|
||
├── Health/
|
||
│ └── Health Check.bru
|
||
├── Screener/
|
||
│ ├── Screen - Mixed.bru
|
||
│ ├── Screen - Tech Stocks.bru
|
||
│ ├── Screen - REIT.bru
|
||
│ ├── Validation empty tickers.bru
|
||
│ ├── Validation 50 plus tickers.bru
|
||
│ └── Get Catalysts.bru
|
||
├── Market Context/
|
||
│ └── Get Market Context.bru
|
||
├── Portfolio/
|
||
│ ├── Add Holding AAPL.bru
|
||
│ ├── Add Holding VOO.bru
|
||
│ ├── Add Holding BTC-USD.bru
|
||
│ ├── Add Holding Validation.bru
|
||
│ ├── Get Portfolio.bru
|
||
│ ├── Remove Holding AAPL.bru
|
||
│ └── Remove Holding Non-existent.bru
|
||
├── Market Calls/
|
||
│ ├── List Calls.bru
|
||
│ ├── Create Market Call.bru
|
||
│ ├── Get Call by ID.bru
|
||
│ ├── Get Call Non-existent.bru
|
||
│ ├── Get Earnings Calendar.bru
|
||
│ ├── Get Calendar Specific Tickers.bru
|
||
│ ├── Create Call Validation.bru
|
||
│ ├── Delete Call.bru
|
||
│ └── Delete Call Already Deleted.bru
|
||
└── LLM Analysis/
|
||
├── Analyze Tickers.bru
|
||
└── Analyze Validation.bru
|
||
```
|
||
|
||
### Configuration
|
||
|
||
#### Setting Variables
|
||
|
||
Variables are stored in `bruno.json` and can be overridden per request:
|
||
|
||
**Default variables:**
|
||
- `baseUrl`: `http://localhost:3000`
|
||
- `callId`: (auto-populated by Create Market Call request)
|
||
|
||
To change variables in the GUI:
|
||
1. Right-click collection → **Settings**
|
||
2. Click **Variables** tab
|
||
3. Edit `baseUrl` or other variables
|
||
4. Click **Save**
|
||
|
||
#### Environment Files
|
||
|
||
Create a `.env.bruno` file in the collection directory for local overrides:
|
||
|
||
```env
|
||
baseUrl=http://localhost:3000
|
||
apiKey=your-secret-key
|
||
```
|
||
|
||
### Common Workflows
|
||
|
||
#### 1. Test the full API flow
|
||
|
||
```bash
|
||
cd api_collections/market-screener
|
||
bru run
|
||
```
|
||
|
||
#### 2. Test just the Screener endpoints
|
||
|
||
```bash
|
||
cd api_collections/market-screener
|
||
bru run "Screener"
|
||
```
|
||
|
||
#### 3. Test and save results
|
||
|
||
```bash
|
||
cd api_collections/market-screener
|
||
bru run --output json > test-results-$(date +%Y%m%d).json
|
||
```
|
||
|
||
#### 4. Continuous testing (while developing)
|
||
|
||
```bash
|
||
# Terminal 1: Run the API server
|
||
npm run dev
|
||
|
||
# Terminal 2: Watch and run tests every 5 seconds
|
||
cd api_collections/market-screener
|
||
watch -n 5 'bru run'
|
||
```
|
||
|
||
### Troubleshooting
|
||
|
||
#### "You can run only at the root of a collection" error
|
||
|
||
Make sure you're in the correct directory:
|
||
|
||
```bash
|
||
# ❌ Wrong — project root
|
||
cd market-screener
|
||
bru run
|
||
|
||
# ✅ Correct — collection root
|
||
cd api_collections/market-screener
|
||
bru run
|
||
```
|
||
|
||
#### Variables not found
|
||
|
||
Verify variable names in `bruno.json`:
|
||
|
||
```bash
|
||
# Check variables
|
||
cat api_collections/market-screener/bruno.json | grep -A 10 "vars"
|
||
```
|
||
|
||
#### Tests failing with "undefined" errors
|
||
|
||
Common causes:
|
||
- Variable name mismatch (case-sensitive)
|
||
- Server not running on the expected port
|
||
- Port conflict (try `lsof -i :3000` to check)
|
||
|
||
### Postman vs Bruno
|
||
|
||
| Feature | Postman | Bruno |
|
||
|---------|---------|-------|
|
||
| **Download Size** | ~380MB | ~50MB |
|
||
| **Collection Format** | Single JSON blob | Plain text `.bru` files |
|
||
| **Git-Friendly** | ❌ Binary | ✅ Text-based, diffable |
|
||
| **API Response** | UI-only | CLI + GUI |
|
||
| **Cost** | Free tier + paid | ✅ Completely free |
|
||
| **IDE Integration** | None | Can edit `.bru` files directly |
|
||
|
||
### References
|
||
|
||
- **Bruno Docs**: [docs.usebruno.com](https://docs.usebruno.com)
|
||
- **Bruno GitHub**: [github.com/usebruno/bruno](https://github.com/usebruno/bruno)
|
||
- **Postman Collection**: `api_collections/market-screener.postman_collection.json`
|