# CLAUDE.md Guidance for Claude Code working in this repo. ## What This Is Prism v2: FastAPI backend (yfinance + SQLite watchlist) + Next.js frontend (Plotly chart, ticker search, market bar, watchlist). Single Overview page. ## Commands ```bash # Backend (port 8001 to avoid conflicts with other local services) backend/.venv/bin/uvicorn app.main:app --reload --host 127.0.0.1 --port 8001 pytest # all backend tests pytest backend/tests/test_api.py # single file backend/.venv/bin/python -m py_compile backend/app/main.py backend/app/schemas.py # Frontend (port 3001) NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8001 npm run dev -- --hostname 127.0.0.1 --port 3001 npm run lint npm run build ``` `./scripts/stack.sh {start|stop|restart|status}` runs both locally; logs in `.run/logs/`. ## Architecture **Backend:** `app/main.py` routes → `app/services/data_service.py` → yfinance. `get_ticker_overview()` tries `get_company_info()` (full `.info`), then `get_fast_info()`, then search + price history. `OverviewMeta.sources` records which source supplied each field. TTL caching in `HISTORY_CACHE`. Key files: - `app/main.py` — routes, CORS, lifespan (DB init) - `app/schemas.py` — Pydantic models (keep in sync with `frontend/types/api.ts`) - `app/services/data_service.py` — yfinance wrapper, caching, signal/stat computation - `app/db/watchlist.py` — SQLite CRUD; uppercase, capped at 10 symbols **Frontend:** `app/page.tsx` → `lib/api.ts` (typed fetch) → backend. Selected ticker in URL `?ticker=SYMBOL`. **Tests:** Stub `data_service` with `monkeypatch` — never hit live yfinance. Use `tmp_path` for SQLite tests. No frontend test runner; use lint + build. ## Production Topology: uvicorn on `127.0.0.1:8001`, `next start` on `127.0.0.1:3001`, nginx TLS reverse-proxy at `prism.tylerhoang.xyz` (`/api/` → backend, `/` → frontend). Service user `www-data`, code at `/var/www/prism-v2/`. Full deploy instructions in `README.md`. **Redeploy:** ```bash cd /var/www/prism-v2 && sudo -u www-data git pull origin master sudo -u www-data backend/.venv/bin/pip install -r backend/requirements.txt sudo -u www-data env HOME=/var/www/prism-v2/frontend NPM_CONFIG_CACHE=/var/www/prism-v2/frontend/.npm npm --prefix frontend ci sudo -u www-data env HOME=/var/www/prism-v2/frontend NPM_CONFIG_CACHE=/var/www/prism-v2/frontend/.npm npm --prefix frontend run build sudo systemctl restart prismv2-backend.service prismv2-frontend.service ``` **Logs / status:** ```bash sudo systemctl status prismv2-backend.service prismv2-frontend.service --no-pager sudo journalctl -u prismv2-backend.service -f ``` **Rules:** - Browser code must NOT hardcode a localhost API base. `frontend/lib/api.ts` defaults to empty so production hits same-origin `/api/*` via nginx. - Frontend runs `next start` in production — never `next dev`. - nginx config and systemd units are in-tree at `nginx/` and `systemd/`; redeploying those requires sudo cp + reload. ## Design System (`design-system/`) All frontend work must follow it — never invent colors, radii, or type choices. Import `design-system/colors_and_type.css` (mirrored to `frontend/public/design-system/`). - Background `#0B0E13`, text `#F2ECDC`. Champagne `#C2AA7A` for accents (never a large fill). - Semantic: gain `#4F8C5E`, loss `#B5494B`, caution `#C49545`, info `#4A78B5`. - Card radius 6px, button 2px, chip 999px. Hairline `1px` borders (`#232934`). No gradients (except chart fill), no glass, no bounces. - Fonts: `--font-serif` EB Garamond, `--font-sans` IBM Plex Sans, `--font-mono` IBM Plex Mono (`tabular-nums` on all numerics). - No emoji. SVGs from `design-system/assets/icons/` or unicode geometrics (`▲ ▼ ◈ ✦ ↗ ·`). Read `design-system/ui_kits/prism/parts{1,2,3}.jsx` and `prism.css` before building new dashboard surfaces. Skill: `design-system/SKILL.md` (`tyler-hoang-design`). ## Reference: Prism v1 (`../prism`) Read-only reference for finance logic — do not import at runtime. - `services/data_service.py` — yfinance wrappers; `compute_ttm_ratios()` self-computes P/E, P/B, P/S, EV/EBITDA, margins, ROE/ROA/ROIC, leverage, dividends from quarterly statements. Caps outliers (P/B and EV/Sales at 100). - `services/valuation_service.py` — `run_dcf()` (Gordon Growth, median historical FCF growth capped ±50%, skips sign flips, EV→equity bridge), plus `run_ev_ebitda`, `run_ev_revenue`, `run_price_to_book`. - `services/fmp_service.py`, `services/news_service.py` — FMP peers/estimates/news, Finnhub sentiment. - `components/overview.py`, `utils/formatters.py` — signal badges, 52w range, number formatting. v1 cache TTLs to mirror: financials 1 h, indices 5 min, search 60 s. Data hierarchy: yfinance → FMP fallback → Finnhub for sentiment. ## Environment `.env.example` → `.env`. Current feature set works without keys. - `NEXT_PUBLIC_API_BASE_URL` — empty in production (same-origin via nginx); `http://127.0.0.1:8001` locally - `FMP_API_KEY`, `FINNHUB_API_KEY` — reserved for future enrichment