infra/docs/plans/2026-06-01-wealth-dashboard-consolidation-design.md
Viktor Barzin 55ed50b932 docs(plans): wealth dashboard consolidation design
Consolidate the wealth Grafana dashboard 36 -> ~17 panels with zero metric
loss: merge the 3 NW/contribution/growth timeseries into 1, the 11 returns/Δ
stat cards into 1 returns table, the 2 yearly barcharts into 1 combo, and the
3 net-pay-vs-market-gain panels into 1 (grain dropdown); reorganize into
collapsed rows. Also rebuild the projection as a Trend panel (numeric
years-from-today x-axis) so it renders regardless of the dashboard time range
(fixes empty-by-default). Philosophy: merge duplicates, keep every metric.

[ci skip]

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 21:52:59 +00:00

6.5 KiB
Raw Blame History

Wealth Dashboard Consolidation — Design (2026-06-01)

Goal

The wealth Grafana dashboard (UID wealth) has grown to 36 panels with heavy duplication. Consolidate to ~17 panels with ZERO metric loss by merging redundant panels, and fix the projection's empty-by-default problem. Philosophy (user-locked): merge duplicates, keep every metric — no metric the user tracks today is removed.

Current state — 36 panels, duplication clusters

Cluster Panels today Issue
1. NW/contribution/growth over time "Net worth — total over time", "Net contribution vs market value", "Growth (market value contribution) over time" All restate NW = contribution + growth
2. Returns/deltas stat cards "12mo return/contrib/gain" (3) + "Δ 1d/7d/30d/90d" × (all/mkt) (8) = 11 cards Same idea, many windows
3. Net pay vs market gain "…cumulative", "…per year", "…per month" (3) Same comparison, 3 grains
4. Yearly bars "Yearly investment return %" + "Annual change decomposition" (2) Same yearly data, two encodings
Projection row (5) text + 3 stats + projection chart Stats duplicate Overview; chart empty by default (shared time-range)

Target layout — collapsed rows

Row: Overview (expanded by default)

  • Keep 4 snapshot stats: Net worth · Net contribution · Growth · ROI%.
  • NEW "Returns" table ← merges cluster 2 (11 cards). table panel: one row per window (1d / 7d / 30d / 90d / 12mo), columns Δ all £ · Δ market £ · return %. Reuses the existing per-window latest-vs-N-days-ago SQL, UNION'd into 5 rows. Preserves every value (12mo contrib = Δall Δmkt) and adds return-% for the short windows.

Row: Net worth over time

  • NEW merged timeseries ← cluster 1: two lines — net_contribution and total_value (market value) — with the growth gap shaded (fillBelowTo / area between). Optionally a 3rd faint "growth" line (= total_value net_contribution). Reuses the "Net contribution vs market value" query.
  • Keep "Per-account stacked — total value" · "Cash vs invested (stacked)".

Row: Returns & contributions

  • NEW yearly combo ← cluster 4: timeseries panel, contributions + market_gain as bars (drawStyle=bars via per-series override) + a return_pct line on a right Y-axis. One query returns year, contributions, market_gain, return_pct (merges the two existing yearly queries — both already share the yearly/ep CTEs).
  • Keep "Monthly contributions vs market gain" · "Per-account ROI %".

Row: Income vs market

  • NEW merged "Net pay vs market gain" ← cluster 3: one timeseries + a $grain custom variable (cumulative / yearly / monthly). The rawSql switches bucketing on $grain. Default cumulative.

Row: Holdings — Keep Positions · Activity log

Row: RSUs (META) — Keep vest cadence · realized PNL

Row: Projections (rebuilt)

  • Rebuild the projection chart as a Trend panel (type: trend): numeric x-axis = years from today (0…$horizon_years), y = Low / Base / High / Historical / "Base, no new contributions". The Trend panel renders smooth multi-series lines on a numeric x — independent of the dashboard time range — so it is ALWAYS visible (fixes empty-by-default). SQL: same FV math as today, but emit m.n/12.0 AS years_from_now instead of a timestamp; format table; panel xField = years_from_now. Carry over the dashed/dotted line overrides + GBP unit.
  • Drop the 3 projection-row stat cards (NW today / Historical return / Monthly contribution) — already in Overview (return table + snapshot). Keep the "How to view" text panel only if still useful (with Trend it's no longer needed — drop it too). Keep the 5 template vars (rate_low/base/high, monthly_contribution, horizon_years).

Panel count: 36 → ~17

4 snapshot + returns table + nw-over-time + per-account + cash-vs-invested + yearly-combo + monthly-contrib + per-account-ROI + net-pay(merged) + positions + activity-log + meta-cadence + meta-pnl + projection-trend = ~17.

Merge SQL notes (validate each against live wealth-pg before deploy)

  • Returns table: 5 SELECTs (one per window) UNION ALL, each computing Δall = nw_now nw_{ago}, Δmkt = Δall (contrib_now contrib_{ago}), ret% = Δmkt / (nw_{ago} + 0.5·Δcontrib)·100 (Modified Dietz, the existing formula). Window→interval: 1d/7d/30d/90d/12mo.
  • Yearly combo: extend the "Annual change decomposition" query (already has contributions, market_gain per year) to also emit return_pct (the "Yearly investment return %" formula) — same ep CTE.
  • Net-pay $grain: one query; cumulative = running sums, yearly/monthly = period-end deltas (reuse the month-end/year-end delta pattern shipped today).

Build / deploy / verify

  1. One-off Python builder (/tmp, outside repo) loads wealth.json: removes the merged-away panels by title, adds the new merged panels + $grain var, rebuilds the projection as a Trend panel, wraps everything in collapsed rows, assigns unique ids + clean gridPos. Clone existing panels for schema-39 fidelity where possible.
  2. Validate: json.load; unique ids; spot-run every new/merged target's SQL against live wealth-pg (the pg-sync sidecar) with default var values.
  3. Deploy: scripts/tg apply -target='module.monitoring.kubernetes_config_map.grafana_dashboards["wealth.json"]' (targeted — monitoring stack carries unrelated drift). git rebase --autostash forgejo/master before push (shared repo).
  4. Verify: ConfigMap == local file; user eyeballs each row in Grafana (esp. the Trend projection renders without touching the time picker, and the returns table + merged panels show the right numbers).

Risks

  • Trend panel is flagged experimental (since v10.0) but available in v11.2; confirm xField + query format=table at build time.
  • Bars + line on one timeseries (yearly combo) needs per-series drawStyle overrides + a second Y-axis override — verify rendering.
  • $grain net-pay SQL is the fiddliest merge; validate all 3 grains.
  • Reorganizing into rows reshuffles gridPos for the whole dashboard — the builder must lay out rows top-to-bottom without overlaps.
  • Keep the contribution-correctness fixes (LOCF view, month-end deltas) intact — the merged panels read the same dav_corrected view.

Out of scope

  • The dav_corrected view + the Fidelity growth-timing cosmetic (separate).
  • No new metrics — pure consolidation.