feat(investments): asset classes with target allocation, charts and rebalance

- Asset classes (name, optional target %) managed under Management > Investments,
  chosen per account
- Charts tab: allocation by asset class and currency, value against invested,
  return by investment; off-target classes flagged by the 5/25 rule
- Rebalance tab: what to buy or sell per class to reach the targets
- Investment page: price against average cost with trades, monthly earnings
  and income
This commit is contained in:
Herculino Trotta committed 2026-11-08 02:26:40 -03:00
1 parent 7902c4fc4e
commit 7038ce8e18
18 files changed
+1085 -63

No files matched your search

+20
View File
@@ -4,3 +4,23 @@ import zoomPlugin from 'chartjs-plugin-zoom';
Chart.register(SankeyController, Flow, zoomPlugin);
window.Chart = Chart;
// The theme's text color at a given strength, resolved to a value canvas can draw
// with. shortcut: read once per chart, so a theme switch shows on the next render.
window.chartInk = (percent) => {
const probe = document.createElement('span');
probe.style.color = `color-mix(in oklab, var(--color-base-content) ${percent}%, transparent)`;
document.body.appendChild(probe);
const color = getComputedStyle(probe).color;
probe.remove();
return color;
};
// Formats amounts with the user's separators, as the amounts elsewhere in the app.
// currency: {prefix, suffix, decimals, decimal, thousand}
window.chartMoney = (currency) => (v, decimals = currency.decimals) => {
const [whole, fraction] = Math.abs(v).toFixed(decimals).split('.');
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, currency.thousand);
const number = fraction ? `${grouped}${currency.decimal}${fraction}` : grouped;
return `${v < 0 ? '-' : ''}${currency.prefix}${number}${currency.suffix}`;
};