summaryrefslogtreecommitdiff
path: root/CLAUDE.md
blob: ea7a26f99c0ffcca7c79eba5b6429921aaaa35e0 (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
86
87
88
89
90
91
92
93
94
95
96
97
98
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## 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.

## Commands

**Backend — run from repo root or `backend/`:**
```bash
# Start API (ports 8001 to avoid conflicts)
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
backend/.venv/bin/python -m py_compile backend/app/main.py backend/app/schemas.py
```

**Frontend — run from `frontend/`:**
```bash
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)
```

## 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.

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

### 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`.

### 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.

## Design System (`design-system/`)

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.

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

**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`).

**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 (`▲ ▼ ◈ ✦ ↗ ·`).

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

**Skill:** `design-system/SKILL.md` is a Claude Code skill (`tyler-hoang-design`) — invoke it when doing design-heavy frontend work.

## Reference: Prism v1 (`../prism`)

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.

Key source files to consult:

- **`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.).

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

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

## Environment Variables

Copy `.env.example` → `.env`. The current feature set works without API keys; yfinance is the active data source.

- `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`)