From e110b40a4afb9dccc3bb4c8ac947a8fbdbba77cc Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Thu, 7 May 2026 22:41:46 +0000 Subject: [PATCH] monitoring(wealth): monthly contrib-vs-mkt as line chart, not bars User asked for two lines instead of side-by-side bars at monthly granularity. Converts panel 25 from barchart to timeseries: * type: barchart -> timeseries * format: table -> time_series, SELECT month::timestamp AS time * drawStyle line, lineWidth 2, fillOpacity 0, showPoints auto * Same blue (contributions) / green (market gain) colour overrides Where the green line rises above the blue line is the visual cue that the market out-earned new contributions for that month -- the trend the user wants to track. Diff is small (15 ins / 28 del) because the bar-chart-only fields (barRadius, barWidth, groupWidth, stacking, xField, xTickLabelRotation) are dropped. --- .../modules/monitoring/dashboards/wealth.json | 43 +++++++------------ 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/stacks/monitoring/modules/monitoring/dashboards/wealth.json b/stacks/monitoring/modules/monitoring/dashboards/wealth.json index a44f8037..65da044b 100644 --- a/stacks/monitoring/modules/monitoring/dashboards/wealth.json +++ b/stacks/monitoring/modules/monitoring/dashboards/wealth.json @@ -1705,8 +1705,8 @@ { "id": 25, "title": "Monthly contributions vs market gain", - "description": "Each month split into 'new money in' (contributions) and 'market gain' (everything else). Side-by-side bars, so months where market gain exceeds contributions are visually obvious — that's the inflection point where your investments out-earn your savings.", - "type": "barchart", + "description": "Each month's net contributions vs market gain as two lines. Where the green (market) line crosses above the blue (contributions) line is when investments out-earn savings for that month. Months below zero on the green line = market drawdowns.", + "type": "timeseries", "datasource": { "type": "grafana-postgresql-datasource", "uid": "wealth-pg" @@ -1725,26 +1725,20 @@ "unit": "currencyGBP", "decimals": 0, "custom": { + "drawStyle": "line", + "lineWidth": 2, + "fillOpacity": 0, + "pointSize": 5, + "showPoints": "auto", + "spanNulls": true, "axisPlacement": "auto", - "axisLabel": "", - "fillOpacity": 80, - "gradientMode": "none", - "lineWidth": 1 + "stacking": { + "group": "A", + "mode": "none" + } } }, "overrides": [ - { - "matcher": { - "id": "byName", - "options": "month" - }, - "properties": [ - { - "id": "unit", - "value": "string" - } - ] - }, { "matcher": { "id": "byName", @@ -1786,16 +1780,9 @@ ] }, "options": { - "barRadius": 0, - "barWidth": 0.7, - "groupWidth": 0.75, - "orientation": "auto", - "showValue": "auto", - "stacking": "none", - "xField": "month", - "xTickLabelRotation": -45, "legend": { "calcs": [ + "last", "sum" ], "displayMode": "table", @@ -1815,8 +1802,8 @@ }, "rawQuery": true, "editorMode": "code", - "format": "table", - "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), monthly AS (SELECT date_trunc('month', valuation_date)::date AS month, valuation_date, SUM(total_value) AS nw, SUM(net_contribution) AS contrib FROM daily_account_valuation WHERE valuation_date <= (SELECT d FROM max_complete) GROUP BY valuation_date), endpoints AS (SELECT month, (array_agg(nw ORDER BY valuation_date ASC))[1] AS nw_start, (array_agg(nw ORDER BY valuation_date DESC))[1] AS nw_end, (array_agg(contrib ORDER BY valuation_date ASC))[1] AS contrib_start, (array_agg(contrib ORDER BY valuation_date DESC))[1] AS contrib_end FROM monthly GROUP BY month) SELECT month::text AS month, ROUND((contrib_end - contrib_start)::numeric, 0) AS contributions, ROUND((nw_end - nw_start - (contrib_end - contrib_start))::numeric, 0) AS market_gain FROM endpoints ORDER BY month" + "format": "time_series", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), monthly AS (SELECT date_trunc('month', valuation_date)::date AS month, valuation_date, SUM(total_value) AS nw, SUM(net_contribution) AS contrib FROM daily_account_valuation WHERE valuation_date <= (SELECT d FROM max_complete) GROUP BY valuation_date), endpoints AS (SELECT month, (array_agg(nw ORDER BY valuation_date ASC))[1] AS nw_start, (array_agg(nw ORDER BY valuation_date DESC))[1] AS nw_end, (array_agg(contrib ORDER BY valuation_date ASC))[1] AS contrib_start, (array_agg(contrib ORDER BY valuation_date DESC))[1] AS contrib_end FROM monthly GROUP BY month) SELECT month::timestamp AS time, ROUND((contrib_end - contrib_start)::numeric, 0) AS contributions, ROUND((nw_end - nw_start - (contrib_end - contrib_start))::numeric, 0) AS market_gain FROM endpoints ORDER BY month" } ] },