- **High — `src/jarvis_finance/services/portfolio_analytics.py:284-286,328,389-407`** — Quotes and position details are keyed only by `instrument_id`, although canonical positions are scoped by `(account_id, instrument_id)`. If the same instrument is held in multiple accounts, the later holding overwrites the earlier one, producing incomplete valuation and incorrect coverage/risk. Additionally, snapshot versioning is calculated per account at lines 390-393, while the existing uniqueness constraint is `(scope_kind, scope_id, valuation_at, snapshot_version)`, so preserving both holdings without changing `scope_id`/version semantics would collide. Keep account identity throughout the quote/valuation pipeline and align it with snapshot uniqueness. - **High — `src/jarvis_finance/market_data/prices.py:92-111,154-170,177-195,214-227,234-249` and `src/jarvis_finance/services/portfolio_analytics.py:302,315-327`** — Every production equity provider ignores `price_date` and uses a live/previous-quote endpoint, usually stamping the result with the fetch time. Consequently, historical runs, persistent timer catch-ups, and weekend/business-date normalization can store a current quote as another day’s “unadjusted close”; a quote dated after `effective` also gets a negative age and is classified fresh. Use a provider endpoint that returns the requested daily unadjusted close and reject quotes outside the requested market date. - **High — `src/jarvis_finance/services/portfolio_analytics.py:102-140,258-264,526-533`** — The run’s effective business date is not applied to holdings or cash. `confirmed_canonical_positions()` includes confirmed transactions with any `trade_date`, and `_latest_cash_by_account()` chooses snapshots solely by latest `created_at`, without `balance_date <= effective`. A historical/backfill run can therefore include future trades and future cash balances. Pass the effective date into both projections and enforce as-of cutoffs. - **High — `src/jarvis_finance/services/portfolio_analytics.py:584-604`** — Volatility and drawdown are calculated from raw portfolio totals rather than cash-flow-adjusted returns. Deposits and withdrawals therefore appear as gains/losses. The query also includes every snapshot revision for a date, so 30 same-day/revised observations can satisfy the “30 daily observations” gate and are annualized as 30 trading days. Build one canonical observation per business date and derive risk from TWR/subperiod returns adjusted for external cash flows. - **Medium — `src/jarvis_finance/services/portfolio_analytics.py:210-219,265-270`** — Idempotency treats a `partial` run as permanently complete for the day, preventing retries after transient provider/FX failures. The fingerprint also excludes provider mappings and cash snapshots, so correcting a mapping or confirming a new cash snapshot still returns the stale prior result. Include all persisted valuation inputs in the fingerprint and/or permit safe continuation/retry of partial runs. - **Medium — `src/jarvis_finance/services/portfolio_analytics.py:397-407,415-424,460-470,492-499`** — Crash recovery is unsafe. A run left in `running` is retried, but its deterministic child IDs are written with `INSERT OR REPLACE`; the affected snapshot tables have immutable/no-delete triggers, so replacing a child already committed before the crash can abort every retry. Resume with conflict-aware inserts that verify/reuse identical children, or atomically roll back the entire run. - **Medium — `src/jarvis_finance/services/portfolio_analytics.py:518-533`** — Cash aggregation uses SQLite `SUM(CAST(... AS NUMERIC))`, which converts decimal text to SQLite integer/REAL arithmetic before wrapping the result in `Decimal`. Values such as `0.1 + 0.2` become `0.30000000000000004`, violating exact Decimal CHF valuation and contaminating totals, allocations, and risk percentages. Load the selected text amounts and sum them in Python with `Decimal`. - **Medium — `src/jarvis_finance/services/portfolio_analytics.py:458-469,619-658`** — ETF benchmark snapshots are always labeled `etf_proxy`, but their value silently switches between `adjusted_close` and `close` depending on availability. A series can therefore mix price-return and distribution-adjusted levels while reporting one return type from only the final row, creating artificial benchmark returns. Require one consistent value/return basis across the aligned period, or mark the comparison unavailable/partial when the basis changes. - **Medium — `src/jarvis_finance/services/portfolio_analytics.py:143-162,181-207`** — The mapping resolver requires exactly one mapped row, while the schema and existing market service support multiple provider mappings per instrument and use a deterministic provider priority. Instruments with two valid mappings are incorrectly reported as `mapping_required`, including possible benchmark instruments. Reuse the established deterministic mapping selection or store an explicit active/preferred mapping.