diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 118 |
1 files changed, 56 insertions, 62 deletions
@@ -1,98 +1,92 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +Guidance for Claude Code working in this repo. ## What This Is -Prism v2 is a financial overview app: FastAPI backend (Python) + Next.js frontend (TypeScript). The backend pulls market data from yfinance with TTL caching and stores a watchlist in SQLite. The frontend renders a single Overview page with ticker search, price chart (Plotly), market bar, and watchlist. +Prism v2: FastAPI backend (yfinance + SQLite watchlist) + Next.js frontend (Plotly chart, ticker search, market bar, watchlist). Single Overview page. ## Commands -**Backend — run from repo root or `backend/`:** ```bash -# Start API (ports 8001 to avoid conflicts) +# 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 - -# Run all tests (pytest.ini sets pythonpath=backend) -pytest - -# Run a single test file -pytest backend/tests/test_api.py - -# Compile-check without running +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 — run from `frontend/`:** -```bash +# 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 # ESLint -npm run build # Production build (validates TypeScript + Next.js) +npm run lint +npm run build ``` +`./scripts/stack.sh {start|stop|restart|status}` runs both locally; logs in `.run/logs/`. + ## Architecture -### Backend data flow -`main.py` routes → `services/data_service.py` → yfinance (primary source). -`data_service.py` has three data-fetching helpers that `get_ticker_overview()` calls in priority order: `get_company_info()` (full yfinance `.info`), `get_fast_info()` (lightweight), then search + price history as last resort. `OverviewMeta.sources` records which source each field came from. +**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 backend files -- `app/main.py` — FastAPI routes, CORS config, lifespan (DB init) -- `app/schemas.py` — all Pydantic response models -- `app/services/data_service.py` — yfinance wrapper, TTL cache (`HISTORY_CACHE`), signal/stat computation -- `app/db/watchlist.py` — SQLite CRUD; watchlist capped at 10 symbols, normalized to uppercase +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 data flow -`app/page.tsx` (Overview shell) → `lib/api.ts` (typed fetch client) → backend. -Selected ticker lives in the URL query param `?ticker=SYMBOL`. Types are shared in `frontend/types/api.ts` — keep these in sync with `backend/app/schemas.py`. +**Frontend:** `app/page.tsx` → `lib/api.ts` (typed fetch) → backend. Selected ticker in URL `?ticker=SYMBOL`. -### Testing approach -Backend tests use `monkeypatch` to stub `data_service` functions — never make live yfinance calls in tests. Use a `tmp_path` fixture for any test that touches SQLite. There is no frontend test runner; use `npm run lint` and `npm run build` for frontend validation. +**Tests:** Stub `data_service` with `monkeypatch` — never hit live yfinance. Use `tmp_path` for SQLite tests. No frontend test runner; use lint + build. -## Design System (`design-system/`) +## Production -The repo includes a full personal-brand design system at `design-system/`. All frontend work must follow it — never invent colors, radii, or type choices. +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`. -**Token entry point:** `design-system/colors_and_type.css` (also mirrored into `frontend/public/design-system/` for serving). Import it for all new components; it declares every CSS custom property: ink surfaces, fg tints, brass/champagne accent, oxford navy, burgundy, semantic gain/loss/caution/info colors, spacing scale, radii, shadows, and font stacks. +**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 +``` -**Typefaces:** -- `--font-serif` — EB Garamond (variable). Display + body serif; italic at large sizes is the signature move. -- `--font-sans` — IBM Plex Sans. UI labels, eyebrows, buttons, badges. -- `--font-mono` — IBM Plex Mono. All prices, percentages, and tabular numerics (`font-variant-numeric: tabular-nums`). +**Logs / status:** +```bash +sudo systemctl status prismv2-backend.service prismv2-frontend.service --no-pager +sudo journalctl -u prismv2-backend.service -f +``` -**Visual rules that must not be broken:** -- Background is `#0B0E13` (ink-0), never pure black. Text is `#F2ECDC` (fg-0), never pure white. -- Primary accent is champagne `#C2AA7A` — for links, focus rings, eyebrows, button fills. Never as a large background fill. -- Semantic colors: gain `#4F8C5E`, loss `#B5494B`, caution `#C49545`, info `#4A78B5`. -- Card radius is 6px; buttons are 2px. No radius above 6px except capsule chips (999px). No bubbly rounding. -- Hairline `1px` borders only (`#232934`). No gradients except the chart area fill. No glass effects. -- Transitions are 150ms ease. No bounces, springs, or scroll-jacking. -- No emoji — ever. Use SVG icons from `design-system/assets/icons/` or unicode geometrics (`▲ ▼ ◈ ✦ ↗ ·`). +**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. -**Prism UI kit:** `design-system/ui_kits/prism/` is a forward-looking redesign showing the target component shapes: `<Sidebar>`, `<TopBar>`, `<TickerHeader>`, `<KPIStrip>`, `<ChartCard>`, `<QuoteTable>`, `<ValuationPanel>`, `<FilingsList>`, `<InsiderRow>`. Read `parts{1,2,3}.jsx` and `prism.css` before building new dashboard surfaces. +## Design System (`design-system/`) -**Skill:** `design-system/SKILL.md` is a Claude Code skill (`tyler-hoang-design`) — invoke it when doing design-heavy frontend work. +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/`). -## Reference: Prism v1 (`../prism`) +- 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 (`▲ ▼ ◈ ✦ ↗ ·`). -The old Streamlit-based app at `../prism` is the canonical reference for finance logic being ported into v2. Do not import from it at runtime, but read it when implementing new financial features. +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`). -Key source files to consult: +## Reference: Prism v1 (`../prism`) -- **`services/data_service.py`** — yfinance wrappers for price history, financials (income/balance/cash flow), options chain, insider transactions, SEC filings, market indices, and analyst targets. Contains `compute_ttm_ratios()` which self-computes P/E, P/B, P/S, EV/EBITDA, margins, ROE/ROA/ROIC, leverage ratios, and dividend metrics from raw quarterly statements (avoiding FMP quota). Includes outlier safeguards (e.g. P/B and EV/Sales capped at 100). -- **`services/valuation_service.py`** — DCF engine (`run_dcf()`) using Gordon Growth Model: projects FCF with median historical growth (capped ±50%, skips sign flips), computes terminal value, bridges enterprise value to equity per share via net debt/preferred equity/minority interest. Also has `run_ev_ebitda()`, `run_ev_revenue()`, `run_price_to_book()`. -- **`services/fmp_service.py`** — FMP REST calls for peers, forward analyst estimates, historical ratios, and news. Falls back to yfinance when FMP is unavailable. -- **`services/news_service.py`** — Finnhub sentiment wrapper. -- **`components/overview.py`** — signal badge logic (6 signals), 52-week range bar, short interest rendering. -- **`utils/formatters.py`** — number formatting helpers (market cap abbreviations, percent display, etc.). +Read-only reference for finance logic — do not import at runtime. -**Caching strategy in v1:** `@st.cache_data` with staggered TTLs — financials at 1 h, indices at 5 min, search at 60 s. Mirror these TTLs when adding new cached endpoints in v2's `data_service.py`. +- `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. -**Data source hierarchy in v1:** yfinance primary → FMP fallback → Finnhub for news/sentiment. FMP free tier is 250 req/day; `FMP_API_KEY` and `FINNHUB_API_KEY` are set via `.env`. +v1 cache TTLs to mirror: financials 1 h, indices 5 min, search 60 s. Data hierarchy: yfinance → FMP fallback → Finnhub for sentiment. -## Environment Variables +## Environment -Copy `.env.example` → `.env`. The current feature set works without API keys; yfinance is the active data source. +`.env.example` → `.env`. Current feature set works without keys. -- `NEXT_PUBLIC_API_BASE_URL` — frontend points to backend (default `http://localhost:8000`) -- `FMP_API_KEY`, `FINNHUB_API_KEY` — reserved for future enrichment endpoints (see v1 `fmp_service.py` / `news_service.py`) +- `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 |
