Skip to content

Commit 8faf516

Browse files
GiuseppeDM98claude
andcommitted
fix: hide baseline snapshot from Net Worth Evolution chart
For YTD/1Y/3Y/5Y periods, getSnapshotsForPeriod includes one extra month before the range as a baseline to enable first-month return calculations. This baseline was visible as a spurious data point in the Evoluzione Patrimonio AreaChart (e.g. Dec 2024 appearing in a YTD 2025 chart). Added optional skipBaseline param to preparePerformanceChartData; caller (getChartData in page.tsx) passes true for baseline periods. Consistent with the existing pattern in prepareMonthlyReturnsHeatmap (i=1 loop). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bf8ad1d commit 8faf516

File tree

5 files changed

+33
-4
lines changed

5 files changed

+33
-4
lines changed

AGENTS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ ALL fields in settings types must be handled in THREE places:
128128
- **Currency**: Use `currencyConversionService.ts` (Frankfurter API, 24h cache)
129129
- **Stamp Duty (Imposta di Bollo)**: `calculateStampDuty(assets, rate, checkingAccountSubCategory?)` in `assetService.ts`. Excluded: `quantity=0` + `stampDutyExempt=true`. Conti correnti (matching subcategory): apply only if value strictly > €5,000. Configured in Settings (`stampDutyEnabled`, `stampDutyRate`, `checkingAccountSubCategory`).
130130

131+
### Dividend Net Amount Storage
132+
- `netAmount` is computed at creation time (`grossAmount - taxAmount`) and **stored in Firestore** — metrics read the saved field, never recalculate at runtime
133+
- Auto-scraped dividends (Borsa Italiana): `taxAmount = grossAmount × 0.26` (flat 26%, hardcoded)
134+
- Bond coupons (auto-generated): `taxRate = asset.taxRate ?? 26` — correctly applies 12.5% for BTPs
135+
- Manual entries: `taxAmount` is whatever the user entered; `netAmount` auto-filled if not provided
136+
- YOC/Current Yield netto: use `div.netAmountEur ?? div.netAmount` (prefer EUR conversion)
137+
131138
### DividendStats Filter Coupling
132139
- `DividendStats` makes an **independent** API fetch to `/api/dividends/stats` — it does NOT read from parent filtered state
133140
- Any filter added to `DividendTrackingTab` **must be explicitly passed** as a prop to `DividendStats` and forwarded to the API
@@ -151,6 +158,9 @@ ALL fields in settings types must be handled in THREE places:
151158
- `getSnapshotsForPeriod` includes 1 extra month before the period as **baseline** for YTD/1Y/3Y/5Y
152159
- **`hasBaseline`** in `calculatePerformanceForPeriod`: period dates computed from `sortedSnapshots[1]` (not baseline). Active only for YTD/1Y/3Y/5Y with >= 3 snapshots
153160
- All metric functions that annualize **must use `calculateMonthsDifference(periodEnd, periodStart)`** — NOT `snapshots.length - 1`
161+
- **Chart baseline hiding** — each chart function handles it independently:
162+
- Heatmap (`prepareMonthlyReturnsHeatmap`): loop starts at `i = 1`
163+
- Evoluzione Patrimonio (`preparePerformanceChartData`): optional `skipBaseline=true``.slice(1)` after sort. Pass `true` for YTD/1Y/3Y/5Y in `getChartData()`. Baseline is always `sortedSnapshots[0]` after chronological sort → slice is safe.
154164

155165
---
156166

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Net Worth Tracker is a Next.js app for Italian investors to track net worth, ass
55

66
## Current Status
77
- Versione stack: Next.js 16, React 19, TypeScript 5, Tailwind v4, Firebase, Vitest, date-fns-tz, @nivo/sankey, @anthropic-ai/sdk, cheerio
8-
- Ultima implementazione: Colonna "Mese Prec. %" nelle tab Prezzi/Valori Anno Corrente (`AssetPriceHistoryTable`, `assetPriceHistoryUtils`, `types/assets`) (2026-03-05)
8+
- Ultima implementazione: Fix baseline nascosto nel grafico "Evoluzione Patrimonio" per periodi YTD/1Y/3Y/5Y (`preparePerformanceChartData` + `getChartData`) (2026-03-06)
99
- In corso ora: nessuna attività attiva
1010

1111
## Architecture Snapshot

Draft Release Temp.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@
157157

158158
## 🐛 Bug Fixes
159159

160+
- Fixed the Net Worth Evolution chart ("Evoluzione Patrimonio") showing an extra data point from outside the selected time period — for example, December 2024 appearing as the first point in a YTD 2025 chart. The chart now correctly starts at the first month of the selected period for YTD, 1Y, 3Y, and 5Y views.
161+
160162
- Fixed asset value history (Current Year Values / Historical Values tabs) showing "0,00€" instead of the correct amount for assets whose monthly snapshot was captured while their quantity was 0 — value is now correctly recalculated from the stored unit price × quantity. Future snapshots also no longer include zero-quantity assets in the per-asset breakdown to prevent recurrence.
161163

162164
- Fixed dividend page asset filter not showing bond assets — bonds are now listed alongside stocks and ETFs in the filter dropdown so you can filter coupon entries by bond

app/dashboard/performance/page.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,10 @@ export default function PerformancePage() {
330330
metrics.endDate
331331
);
332332

333-
return preparePerformanceChartData(periodSnapshots, metrics.cashFlows);
333+
// YTD/1Y/3Y/5Y periods include an extra baseline snapshot before the range;
334+
// skip it so the chart starts at the first actual month of the selected period.
335+
const hasBaseline = ['YTD', '1Y', '3Y', '5Y'].includes(metrics.timePeriod);
336+
return preparePerformanceChartData(periodSnapshots, metrics.cashFlows, hasBaseline);
334337
};
335338

336339
const [chartData, setChartData] = useState<any[]>([]);

lib/services/performanceService.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,16 +1499,30 @@ async function calculateRollingPeriods(
14991499

15001500
/**
15011501
* Prepare chart data for net worth evolution
1502+
*
1503+
* @param skipBaseline - When true, drops the first (baseline) snapshot.
1504+
* getSnapshotsForPeriod includes an extra month before YTD/1Y/3Y/5Y periods
1505+
* so the first month's return can be calculated, but that month falls outside
1506+
* the selected period and should not appear as a chart data point.
15021507
*/
15031508
export function preparePerformanceChartData(
15041509
snapshots: MonthlySnapshot[],
1505-
cashFlows: CashFlowData[]
1510+
cashFlows: CashFlowData[],
1511+
skipBaseline = false
15061512
): PerformanceChartData[] {
15071513
const sortedSnapshots = [...snapshots].sort((a, b) => {
15081514
if (a.year !== b.year) return a.year - b.year;
15091515
return a.month - b.month;
15101516
});
15111517

1518+
// Skip the first snapshot when it is a baseline month (e.g., Dec for YTD).
1519+
// getSnapshotsForPeriod includes it for return calculations but it falls
1520+
// outside the selected period and should not appear as a chart data point.
1521+
const chartSnapshots =
1522+
skipBaseline && sortedSnapshots.length > 1
1523+
? sortedSnapshots.slice(1)
1524+
: sortedSnapshots;
1525+
15121526
let cumulativeContributions = 0;
15131527
const cashFlowMap = new Map<string, number>();
15141528

@@ -1517,7 +1531,7 @@ export function preparePerformanceChartData(
15171531
cashFlowMap.set(key, cf.netCashFlow);
15181532
});
15191533

1520-
return sortedSnapshots.map(snapshot => {
1534+
return chartSnapshots.map(snapshot => {
15211535
const key = `${snapshot.year}-${String(snapshot.month).padStart(2, '0')}`;
15221536
const cashFlow = cashFlowMap.get(key) || 0;
15231537
cumulativeContributions += cashFlow;

0 commit comments

Comments
 (0)