# Market Quotes & Detail Charts v1

Use this reference for FinanceManager sprints that add current/delayed market quotes and short-term charts for existing Equity/ETF and Crypto positions.

## Scope and non-goals

- No trading/order execution and no broker connection.
- No provider calls on normal dashboard render. Render must read local DB/cache only.
- Provider calls are allowed only through explicit user action (`Kurse aktualisieren`, `Live-Stats aktualisieren`, `Chart laden`) or explicit CLI jobs.
- No API keys in frontend, no secrets in logs, no runtime DB/reports/build artifacts in Git, and no invented prices.
- Missing prices must render as missing; delayed/stale prices must render with a clear data-quality status.

## Backend pattern

- Prefer existing cache tables (`market_prices`, `crypto_prices`) for latest/EOD values.
- Add append-only point tables only when chart/intraday storage is needed:
  - `equity_price_points(point_id, instrument_id, provider, provider_symbol, timestamp, price, currency, interval, source_quality, fetched_at)`
  - `crypto_price_points(point_id, asset_id, provider, provider_symbol, timestamp, price, currency, interval, source_quality, fetched_at)`
- Store price/quantity/amount values as Decimal-compatible TEXT; format `Decimal` with `format(value, 'f')` and avoid Python float conversions.
- Cache helper functions should upsert deterministic point IDs and backfill chart points from existing latest/EOD tables when no point rows exist yet.
- If the repo has many tests asserting the current schema version, do not casually bump `MIGRATION_VERSION` for compatibility-only append operations; either coordinate all version tests or add idempotent table creation in the existing compatibility migration path.

## Provider strategy

Equity/ETF priority:
1. FMP when key exists and symbol is supported.
2. Finnhub when key exists and symbol is supported.
3. Twelve Data when key exists and symbol is supported.
4. Stooq/EOD-style no-key fallback only when suitable.
5. Manual price only as audited fallback.

Crypto priority:
1. CoinGecko for broad coverage / portfolio valuation.
2. Binance Spot API for ticker/24h stats/klines only when an unambiguous pair exists (e.g. `BTCUSDT`).
3. CoinGecko market chart fallback when Binance mapping is missing/unsupported.

Status taxonomy should be sanitized and user-facing: `fresh`, `delayed`, `stale`, `missing`, `unsupported_pair`, `provider_error`, `rate_limited`. Never expose raw provider responses or secret-bearing URLs.

## API shape

Useful endpoints:

- `GET /api/market/status` — local cache/status only; include `render_provider_calls=false`.
- `POST /api/market/equity/update-quotes` — explicit quote refresh. It must work both scoped (`?instrument_id=...`) and unscoped batch mode for the page-level `Kurse aktualisieren` button; unscoped mode should obey a bounded `limit` and return aggregate counts, not require the frontend to send every instrument ID.
- `POST /api/market/crypto/update-live-stats` — explicit live-stat refresh. It must work both scoped (`?asset_id=...`) and unscoped batch mode for the page-level `Live-Stats aktualisieren` button; unscoped mode should obey a bounded `limit` and return aggregate counts.
- `GET /api/equity/{instrument_id}/quote`
- `GET /api/equity/{instrument_id}/chart?range=1d&interval=5m`
- `GET /api/crypto/{asset_id}/live-stats`
- `GET /api/crypto/{asset_id}/chart?range=1d&interval=5m`

Detail responses should include: latest price, currency, absolute/percent change, OHLC/volume when available, provider, provider symbol, fetched timestamp, quality status, chart points and warnings.

## UI pattern

- Top-level Equity page: show `Kurse aktualisieren`, last local update/cache status, table data from cache/readmodel, and a row-click detail drawer.
- Top-level Crypto page: show `Live-Stats aktualisieren`, last local update/cache status, table data from cache/readmodel, and a row-click detail drawer.
- The page-level update buttons should call the unscoped batch endpoints; detail-drawer buttons should call the scoped single-asset endpoints. Add tests for both so the UI cannot drift into requiring hidden IDs for batch refresh.
- Detail drawers should show current price info, provider symbol/mapping quality, data-quality badge, and buttons for `Chart laden` and quote/live refresh.
- Use existing chart stack (PrimeVue Chart / Chart.js or existing Sparkline component). Do not add a new chart library for this sprint.
- If there are too few chart points, render a clear empty state like `Noch zu wenig Kursdaten` rather than a misleading zero chart.

## CLI pattern

Add/extend explicit jobs with dry-run support:

- `update-equity-quotes --provider auto --limit 10 --dry-run`
- `update-crypto-live-stats --provider binance --limit 20 --dry-run`
- `update-market-charts --asset-class equity|crypto --range 1d --interval 5m --dry-run`

CLI output should be aggregate-only: counts updated/cached/skipped/warnings/errors and whether it was dry-run. Do not print real portfolio values or secrets.

## Verification pattern

- Python compile and full pytest.
- Frontend tests and production build from `frontend/`.
- Secret scan changed source files and built frontend assets before deleting build output.
- Remove `.pytest_cache`, `__pycache__`, `frontend/dist`, `frontend/node_modules` as appropriate before Git-safety. If `npm install` was needed only to run tests in a fresh checkout, treat `node_modules` as a temporary artifact and remove it before final Git-safety.
- Run `git-safety-scan .` and `git diff --check`.
- Browser sanity must prove: Equity page, Crypto page, row-click detail, `Chart laden`, explicit update buttons, and `/api/market/status` with `render_provider_calls=false`.
- Route/navigation smoke tests should verify route resolution/component wiring rather than mounting every route and accidentally triggering real page API initialization; page-level tests own actual render behavior.
- Commit and push; verify remote hash with authenticated fetch/update-ref if normal `origin` fetch cannot prompt for credentials.

## Pitfalls from implementation

- Do not make the page-level refresh endpoints require `instrument_id`/`asset_id`. The UI has global buttons (`Kurse aktualisieren`, `Live-Stats aktualisieren`) that need bounded batch behavior; requiring IDs breaks the main user action even if detail refresh still works.
- Batch refresh responses should be aggregate-only and sanitized (`updated`, `skipped`, `warnings`, `errors`, `render_provider_calls=false`), never long logs, raw provider payloads, or real-value summaries in chat.
- When a frontend full-suite failure comes from a broad navigation smoke test mounting all pages, avoid masking API errors with huge mock bodies. Prefer narrowing the smoke test to route/component resolution and keep actual page API behavior in focused page tests.