wealth dashboard: replace returns table with per-window stat panels [ci skip]

Swap the single "Returns over time windows" table (panel 9201) for 5 stat
panels (1d/7d/30d/90d/12mo), each showing Return % (Modified-Dietz) as the
headline value + Δ market (£, net of contributions) as a second value,
colored red/green by sign.

Same per-window Modified-Dietz math as the old table, just scoped to one
interval per panel — verified against live wealthfolio_sync PG and reproduced
through Grafana's datasource API (e.g. 30d = 8.15% / £86,875, 12mo = 38.68% /
£297,846, matching the prior table exactly). Kept the same 24×8 grid footprint
so nothing else on the dashboard reflows.

Already applied via targeted `tg apply` of the wealth.json configmap; [ci skip]
because a full monitoring-stack CI apply would pull in unrelated pre-existing
drift.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-04 19:54:33 +00:00
parent 6cec27f8dc
commit fc1486c3dd

View file

@ -295,79 +295,44 @@
]
},
{
"id": 9201,
"title": "Returns over time windows",
"description": "Replaces the 12mo + \u0394 stat cards. \u0394 all = total change; \u0394 market = change net of contributions; Return % = Modified-Dietz.",
"type": "table",
"id": 9211,
"title": "1d",
"description": "Return % (Modified-Dietz) and \u0394 market = \u00a3 change net of contributions, over the trailing 1d.",
"type": "stat",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"gridPos": {
"h": 8,
"w": 24,
"w": 5,
"x": 0,
"y": 5
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "right"
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "\u0394 all (\u00a3)"
},
"properties": [
{
"id": "unit",
"value": "currencyGBP"
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
},
{
"id": "color",
"value": {
"mode": "continuous-RdYlGr"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "\u0394 market (\u00a3)"
},
"properties": [
{
"id": "unit",
"value": "currencyGBP"
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
},
{
"id": "color",
"value": {
"mode": "continuous-RdYlGr"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Return %"
"options": "Return"
},
"properties": [
{
@ -375,26 +340,38 @@
"value": "percent"
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
},
"id": "decimals",
"value": 2
}
]
},
{
"matcher": {
"id": "byName",
"options": "\u0394 market"
},
"properties": [
{
"id": "color",
"value": {
"mode": "continuous-RdYlGr"
}
"id": "unit",
"value": "currencyGBP"
}
]
}
]
},
"options": {
"cellHeight": "sm",
"footer": {
"show": false
}
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value_and_name"
},
"targets": [
{
@ -403,10 +380,382 @@
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"format": "table",
"editorMode": "code",
"rawQuery": true,
"rawSql": "WITH active_count AS (SELECT COUNT(*) n FROM accounts), mc AS (SELECT MAX(valuation_date) d FROM (SELECT valuation_date, COUNT(*) c FROM dav_corrected GROUP BY valuation_date) x WHERE c>=(SELECT n FROM active_count)), latest AS (SELECT DISTINCT ON (account_id) account_id, total_value nw_now, net_contribution c_now FROM dav_corrected WHERE valuation_date<=(SELECT d FROM mc) ORDER BY account_id, valuation_date DESC), an AS (SELECT SUM(nw_now) nw_now, SUM(c_now) c_now FROM latest), w(label,ord,iv) AS (VALUES ('1d',1,interval '1 day'),('7d',2,interval '7 days'),('30d',3,interval '30 days'),('90d',4,interval '90 days'),('12mo',5,interval '12 months')), ago AS (SELECT wl.label, wl.ord, SUM(x.nw) nw_ago, SUM(x.c) c_ago FROM w wl CROSS JOIN latest l LEFT JOIN LATERAL (SELECT total_value nw, net_contribution c FROM dav_corrected dd WHERE dd.account_id=l.account_id AND dd.valuation_date<=(SELECT d FROM mc)-wl.iv ORDER BY dd.valuation_date DESC LIMIT 1) x ON true GROUP BY wl.label, wl.ord) SELECT a.label AS \"Window\", round((an.nw_now-a.nw_ago)::numeric,0) AS \"\u0394 all (\u00a3)\", round(((an.nw_now-a.nw_ago)-(an.c_now-a.c_ago))::numeric,0) AS \"\u0394 market (\u00a3)\", round((((an.nw_now-a.nw_ago)-(an.c_now-a.c_ago))/NULLIF(a.nw_ago+0.5*(an.c_now-a.c_ago),0)*100)::numeric,2) AS \"Return %\" FROM ago a CROSS JOIN an ORDER BY a.ord"
"editorMode": "code",
"format": "table",
"rawSql": "WITH active_count AS (SELECT COUNT(*) n FROM accounts), mc AS (SELECT MAX(valuation_date) d FROM (SELECT valuation_date, COUNT(*) c FROM dav_corrected GROUP BY valuation_date) x WHERE c>=(SELECT n FROM active_count)), latest AS (SELECT DISTINCT ON (account_id) account_id, total_value nw_now, net_contribution c_now FROM dav_corrected WHERE valuation_date<=(SELECT d FROM mc) ORDER BY account_id, valuation_date DESC), an AS (SELECT SUM(nw_now) nw_now, SUM(c_now) c_now FROM latest), ago AS (SELECT SUM(x.nw) nw_ago, SUM(x.c) c_ago FROM latest l LEFT JOIN LATERAL (SELECT total_value nw, net_contribution c FROM dav_corrected dd WHERE dd.account_id=l.account_id AND dd.valuation_date<=(SELECT d FROM mc)-interval '1 day' ORDER BY dd.valuation_date DESC LIMIT 1) x ON true) SELECT round((((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))/NULLIF(ago.nw_ago+0.5*(an.c_now-ago.c_ago),0)*100)::numeric,2) AS \"Return\", round(((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))::numeric,0) AS \"\u0394 market\" FROM an CROSS JOIN ago"
}
]
},
{
"id": 9212,
"title": "7d",
"description": "Return % (Modified-Dietz) and \u0394 market = \u00a3 change net of contributions, over the trailing 7d.",
"type": "stat",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"gridPos": {
"h": 8,
"w": 5,
"x": 5,
"y": 5
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Return"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
},
{
"matcher": {
"id": "byName",
"options": "\u0394 market"
},
"properties": [
{
"id": "unit",
"value": "currencyGBP"
}
]
}
]
},
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value_and_name"
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"rawQuery": true,
"editorMode": "code",
"format": "table",
"rawSql": "WITH active_count AS (SELECT COUNT(*) n FROM accounts), mc AS (SELECT MAX(valuation_date) d FROM (SELECT valuation_date, COUNT(*) c FROM dav_corrected GROUP BY valuation_date) x WHERE c>=(SELECT n FROM active_count)), latest AS (SELECT DISTINCT ON (account_id) account_id, total_value nw_now, net_contribution c_now FROM dav_corrected WHERE valuation_date<=(SELECT d FROM mc) ORDER BY account_id, valuation_date DESC), an AS (SELECT SUM(nw_now) nw_now, SUM(c_now) c_now FROM latest), ago AS (SELECT SUM(x.nw) nw_ago, SUM(x.c) c_ago FROM latest l LEFT JOIN LATERAL (SELECT total_value nw, net_contribution c FROM dav_corrected dd WHERE dd.account_id=l.account_id AND dd.valuation_date<=(SELECT d FROM mc)-interval '7 days' ORDER BY dd.valuation_date DESC LIMIT 1) x ON true) SELECT round((((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))/NULLIF(ago.nw_ago+0.5*(an.c_now-ago.c_ago),0)*100)::numeric,2) AS \"Return\", round(((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))::numeric,0) AS \"\u0394 market\" FROM an CROSS JOIN ago"
}
]
},
{
"id": 9213,
"title": "30d",
"description": "Return % (Modified-Dietz) and \u0394 market = \u00a3 change net of contributions, over the trailing 30d.",
"type": "stat",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"gridPos": {
"h": 8,
"w": 5,
"x": 10,
"y": 5
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Return"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
},
{
"matcher": {
"id": "byName",
"options": "\u0394 market"
},
"properties": [
{
"id": "unit",
"value": "currencyGBP"
}
]
}
]
},
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value_and_name"
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"rawQuery": true,
"editorMode": "code",
"format": "table",
"rawSql": "WITH active_count AS (SELECT COUNT(*) n FROM accounts), mc AS (SELECT MAX(valuation_date) d FROM (SELECT valuation_date, COUNT(*) c FROM dav_corrected GROUP BY valuation_date) x WHERE c>=(SELECT n FROM active_count)), latest AS (SELECT DISTINCT ON (account_id) account_id, total_value nw_now, net_contribution c_now FROM dav_corrected WHERE valuation_date<=(SELECT d FROM mc) ORDER BY account_id, valuation_date DESC), an AS (SELECT SUM(nw_now) nw_now, SUM(c_now) c_now FROM latest), ago AS (SELECT SUM(x.nw) nw_ago, SUM(x.c) c_ago FROM latest l LEFT JOIN LATERAL (SELECT total_value nw, net_contribution c FROM dav_corrected dd WHERE dd.account_id=l.account_id AND dd.valuation_date<=(SELECT d FROM mc)-interval '30 days' ORDER BY dd.valuation_date DESC LIMIT 1) x ON true) SELECT round((((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))/NULLIF(ago.nw_ago+0.5*(an.c_now-ago.c_ago),0)*100)::numeric,2) AS \"Return\", round(((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))::numeric,0) AS \"\u0394 market\" FROM an CROSS JOIN ago"
}
]
},
{
"id": 9214,
"title": "90d",
"description": "Return % (Modified-Dietz) and \u0394 market = \u00a3 change net of contributions, over the trailing 90d.",
"type": "stat",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"gridPos": {
"h": 8,
"w": 5,
"x": 15,
"y": 5
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Return"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
},
{
"matcher": {
"id": "byName",
"options": "\u0394 market"
},
"properties": [
{
"id": "unit",
"value": "currencyGBP"
}
]
}
]
},
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value_and_name"
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"rawQuery": true,
"editorMode": "code",
"format": "table",
"rawSql": "WITH active_count AS (SELECT COUNT(*) n FROM accounts), mc AS (SELECT MAX(valuation_date) d FROM (SELECT valuation_date, COUNT(*) c FROM dav_corrected GROUP BY valuation_date) x WHERE c>=(SELECT n FROM active_count)), latest AS (SELECT DISTINCT ON (account_id) account_id, total_value nw_now, net_contribution c_now FROM dav_corrected WHERE valuation_date<=(SELECT d FROM mc) ORDER BY account_id, valuation_date DESC), an AS (SELECT SUM(nw_now) nw_now, SUM(c_now) c_now FROM latest), ago AS (SELECT SUM(x.nw) nw_ago, SUM(x.c) c_ago FROM latest l LEFT JOIN LATERAL (SELECT total_value nw, net_contribution c FROM dav_corrected dd WHERE dd.account_id=l.account_id AND dd.valuation_date<=(SELECT d FROM mc)-interval '90 days' ORDER BY dd.valuation_date DESC LIMIT 1) x ON true) SELECT round((((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))/NULLIF(ago.nw_ago+0.5*(an.c_now-ago.c_ago),0)*100)::numeric,2) AS \"Return\", round(((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))::numeric,0) AS \"\u0394 market\" FROM an CROSS JOIN ago"
}
]
},
{
"id": 9215,
"title": "12mo",
"description": "Return % (Modified-Dietz) and \u0394 market = \u00a3 change net of contributions, over the trailing 12mo.",
"type": "stat",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"gridPos": {
"h": 8,
"w": 4,
"x": 20,
"y": 5
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "green",
"value": 0
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Return"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
},
{
"matcher": {
"id": "byName",
"options": "\u0394 market"
},
"properties": [
{
"id": "unit",
"value": "currencyGBP"
}
]
}
]
},
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "center",
"orientation": "vertical",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "value_and_name"
},
"targets": [
{
"refId": "A",
"datasource": {
"type": "grafana-postgresql-datasource",
"uid": "wealth-pg"
},
"rawQuery": true,
"editorMode": "code",
"format": "table",
"rawSql": "WITH active_count AS (SELECT COUNT(*) n FROM accounts), mc AS (SELECT MAX(valuation_date) d FROM (SELECT valuation_date, COUNT(*) c FROM dav_corrected GROUP BY valuation_date) x WHERE c>=(SELECT n FROM active_count)), latest AS (SELECT DISTINCT ON (account_id) account_id, total_value nw_now, net_contribution c_now FROM dav_corrected WHERE valuation_date<=(SELECT d FROM mc) ORDER BY account_id, valuation_date DESC), an AS (SELECT SUM(nw_now) nw_now, SUM(c_now) c_now FROM latest), ago AS (SELECT SUM(x.nw) nw_ago, SUM(x.c) c_ago FROM latest l LEFT JOIN LATERAL (SELECT total_value nw, net_contribution c FROM dav_corrected dd WHERE dd.account_id=l.account_id AND dd.valuation_date<=(SELECT d FROM mc)-interval '12 months' ORDER BY dd.valuation_date DESC LIMIT 1) x ON true) SELECT round((((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))/NULLIF(ago.nw_ago+0.5*(an.c_now-ago.c_ago),0)*100)::numeric,2) AS \"Return\", round(((an.nw_now-ago.nw_ago)-(an.c_now-ago.c_ago))::numeric,0) AS \"\u0394 market\" FROM an CROSS JOIN ago"
}
]
},