# Crypto price refresh hardening and idempotent BTC runtime checks

Use when making FinanceManager crypto price updates production-safe, or when repeating an approved single-coin/BTC runtime import test after data may already exist.

## Crypto price refresh hardening pattern

- Prefer CoinGecko batch requests (`/simple/price` with comma-separated `ids`) over one request per coin.
- Keep dashboard/report rendering read-only over local cached `crypto_prices`; price refresh belongs to CLI/scheduled jobs only.
- CLI knobs should include:
  - `--currency`
  - `--sleep-seconds`
  - `--max-retries`
  - `--initial-backoff`
  - `--max-backoff`
  - `--only-missing`
  - `--only-stale`
  - `--only-symbol SYMBOL`
  - `--limit N`
  - `--dry-run`
- Treat HTTP 429 as non-fatal: retry with capped exponential backoff, then store/report stale/missing quality rather than crashing.
- Summary output must stay aggregate-only: total assets, updated, cached, skipped, stale, warnings, errors, assets still missing local price. Do not print real prices, holdings, market values, or quantities.
- For every asset without a fresh local price after refresh, create/update a deduplicated data-quality alert. Repeated refreshes should update occurrence metadata, not create alert floods.
- Dry-run must not mutate prices or alerts. If dry-run simulates successful quotes, remember that missing-local-price counts may reflect current DB state, not hypothetical post-write state, unless explicitly modelled.

## Tests to include

- Batch request updates multiple CoinGecko IDs in one provider call.
- `--only-missing` skips assets with any fresh local price.
- `--only-stale` refreshes only stale fresh prices.
- `--only-symbol` and `--limit` constrain scope.
- `--dry-run` writes neither `crypto_prices` nor alerts.
- 429/backoff uses configured retries and cap.
- Missing/stale/error quotes create deduplicated alerts.
- Dashboard/report contexts still read local prices only (`live_api_calls=False`).

## Idempotent BTC / single-coin runtime test

If the approved BTC/single-coin runtime import is re-run and the exact asset/holdings already exist:

1. Do not force a second import.
2. Verify aggregate-only that the existing asset matches the approved identity (`coin_name`, `symbol`, `coingecko_id`) and that the expected wallet holdings are present.
3. Verify no duplicate `asset_id + wallet_id` rows.
4. Verify stored quantities are SQLite `TEXT` / Decimal-parseable without printing them.
5. Verify snapshot date and legacy-value policy (for BTC test: snapshot date `2025-12-31`, legacy CHF values ignored/null if required).
6. Verify holding-level audit logs exist.
7. Report “asset/holding count increased: no” when no write was necessary. This is success, not failure, because duplicate prevention is part of the requirement.

## Verification discipline

- Run Git status and Git-safety before and after real-data runtime operations.
- Run targeted tests first, then full suite.
- Because `pytest` recreates `.pytest_cache`, remove caches after tests and before the final Git-safety scan.
- Final chat report must be aggregate-only: counts, booleans, warnings/errors, git status/safety. No real values.
