summaryrefslogtreecommitdiff
path: root/CLAUDE.md
blob: 39040f7d721887539f2b1066536963931f5ab6c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 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`.

**Deploy flow:** bare repo at `/srv/git/prism-v2.git`; pushing master triggers a post-receive hook that `git checkout -f`s into `/var/www/prism-v2/` and runs `scripts/deploy.sh`. `/var/www/prism-v2/` has NO `.git/` — never try to `git pull` there. Manual server-side deploy: `sudo ./scripts/deploy.sh` (`--install` also refreshes systemd units + nginx site). Sample hook lives at `scripts/post-receive.sample`.

**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