Button in der Planansicht -> Dialog: Erklaerung, Eingaben, Lauf, Ergebnis + Faecher. Statt einer festen Rendite/Inflation werden tausende Zufallspfade gerechnet und die Erfolgs-/Ruinwahrscheinlichkeit des Plans ausgewiesen. Kern (computePlan-Nahtstelle): - computePlan(plan, sample?) nimmt optional pro Jahr Inflation und pro Element/Jahr eine Rendite. Ohne sample bitgenau wie bisher (durch Golden Tests abgesichert). - Dazu Inflation auf ein kumulatives Deflator-Array umgestellt (statt (1+i)^t), damit sie pro Jahr variieren kann. Deterministisch identisch. - Reine Funktion ohne Server-Deps -> Simulation laeuft komplett im Browser, null Serverlast. ~10'000 Laeufe in ~1 s, Fortschritt alle 500 Laeufe (kein Freeze). Statistik (montecarlo.ts): - Fettschwaenzig (standardisierte Student-t, nu=5): Extremcrashs realistisch haeufig, eine Normalverteilung wuerde sie stark unterschaetzen. - Gemeinsamer Marktschock (rho=0.7): riskante Anlagen fallen im Crash zusammen, nicht gegeneinander. - Boeden: 0 % fuer PK/3a, -100 % sonst. Seedbar (reproduzierbar). - Zwei Renditezahlen: geplante (Zielbalken) vs. historische (Streu-Mittelpunkt) -- sonst waere P(>= geplantes Endvermoegen) immer ~50 %. UI: Erklaerung, pro Element/Inflation historischer Oe (Pflicht, kein Default) + Streuungsstufe (recherchierte sigma-Werte) + Anzahl Laeufe. Ergebnis: Ruin-/ Erfolgswahrscheinlichkeit, Faecher (10/50/90) + deterministische Linie. 7 MC-Tests inkl. deterministischer Aequivalenz, Vol-Drag, Reproduzierbarkeit, Boden, Ruin (41 -> 48). Keine DB-Aenderung. Spezifikation auf v0.7 (Kap. 4.12, 9.15). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+129
-7
@@ -4,10 +4,10 @@
|
||||
| | |
|
||||
|---|---|
|
||||
| **Dokument** | Funktionale und Technische Spezifikation FPT |
|
||||
| **Version** | 0.6 |
|
||||
| **Version** | 0.7 |
|
||||
| **Datum** | 2026-07-17 |
|
||||
| **Status** | Lebendes Dokument |
|
||||
| **Codestand** | Arbeitsstand nach `a97de5b` inkl. Teilverkauf (Sonstiges Vermögen) und Sonderamortisation (Immobilie) (Branch `main`) |
|
||||
| **Codestand** | Arbeitsstand nach `5bf35bd` inkl. Monte-Carlo-Simulation (Stufe A) (Branch `main`) |
|
||||
| **Ersetzt** | `FDD_TDD_FPT.docx` (v1–v5) im Ordner `Info Dateien` – diese sind ab Version 0.1 dieses Dokuments obsolet |
|
||||
| **Geltungsbereich** | Gesamter Code im Verzeichnis `FPT` |
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
| Version | Datum | Autor | Änderung |
|
||||
|---|---|---|---|
|
||||
| 0.7 | 2026-07-17 | Claude (Opus 4.8) | **Monte-Carlo-Simulation** (Roadmap Nr. 19, Stufe A). Button in der Planansicht → Dialog mit Erklärung, Eingaben und Ergebnis. Statt einer festen Rendite/Inflation werden tausende Zufallspfade gerechnet; ausgewiesen werden **Ruinwahrscheinlichkeit**, **Erfolgswahrscheinlichkeit** (P(Endvermögen ≥ Zielbetrag)) und ein **Fächer** (10 %/Median/90 %) plus die deterministische Linie. Läuft komplett im Browser (`computePlan` ist rein). Modell: fettschwänzige Verteilung (Student-t, ν=5), gemeinsamer Marktschock (ρ=0.7), Böden 0 % für PK/3a und −100 % sonst. Pro renditetragendem Element und für die Inflation je: historischer Ø (Pflicht), Streuungsstufe, σ. Neue Datei `montecarlo.ts` + optionaler `sample`-Parameter in `computePlan` (Inflation neu als kumulatives Array; deterministisch identisch). Neues Kapitel 4.12; 7 MC-Tests + Refactor-Absicherung (41 → 48). Keine Verhaltensänderung, keine DB-Änderung. |
|
||||
| 0.6 | 2026-07-17 | Claude (Opus 4.8) | **Teilverkauf** von Sonstigem Vermögen (Roadmap Nr. 42) und **Sonderamortisation** der Hypothek (Roadmap Nr. 15). `OTHER_ASSET` am Übergang neu: Halten / Verkaufen / **Teilverkauf** – ein Betrag fliesst ins Cash (erscheint als „Kapitalzufluss" im Phasenkopf), der Rest bleibt investiert. `REAL_ESTATE` im Halten-Fall neu mit **Einmaltilgung** aus dem Cash (analog zur Sofort-Tilgung bei Schulden; erscheint als „Kapitalinvestition"). Damit lässt sich die indirekte Amortisation via 3a mechanisch nachbilden. Die eigentliche Roadmap Nr. 15 (Steuerwirkung der indirekten Amortisation) bleibt mit dem Steuer-Bündel 13/14/23 zurückgestellt – siehe 9.14. Kapitel 4.9.3/4.9.4 ergänzt. Fünf Regressionstests (36 → 41). Keine Verhaltensänderung für bestehende Pläne. |
|
||||
| 0.5 | 2026-07-17 | Claude (Opus 4.8) | **Netto/Brutto geklärt** (Roadmap Nr. 9, reduziert) und **Immobilien-Modul erweitert** (Roadmap Nr. 8). Einkommen ist neu explizit als **Nettolohn** definiert (Label und Hilfetext); für die AHV rechnet das Tool intern mit `AHV_GROSS_FROM_NET_FACTOR = 1.12` auf den Bruttolohn hoch – die AHV bemisst sich am Brutto, die bisherige Netto-Basis unterschätzte die Rente um bis zu ~1'900/Jahr. Immobilie neu mit **Hypothekarzins** (% der Restschuld, sinkt mit der Amortisation, mit Doppelzählungs-Schalter) und **Wertsteigerung** (auf die **Liegenschaft**, nicht auf das Eigenkapital – Hebeleffekt). Grundstückgewinnsteuer bemisst sich neu explizit am ursprünglichen Kaufpreis. Keine Aufschlüsselung bei Einkommen oder Ausgaben, keine Steuerschätzung (Begründung: 9.14). Neues Kapitel 4.4.5; 4.6.5 und 4.9.4 überarbeitet; Abschnitt 9 um zwei Punkte ergänzt. Sechs Regressionstests (30 → 36). **Verhaltensänderung:** siehe 9.13. |
|
||||
| 0.4 | 2026-07-17 | Claude (Opus 4.8) | **AHV-Rente einkommensabhängig** (Roadmap Nr. 3) und **Fortschreibungs-Warnhinweis** (Roadmap Nr. 4). Die AHV-Rente folgt neu der amtlichen Rentenformel (Skala 44) über das massgebende durchschnittliche Jahreseinkommen statt pauschal der Maximalrente; verifiziert gegen die amtliche Tabelle 318.117.1 (51/51 Zeilen). Prüfung der Beitragskarriere am Pensions-Übergang, mit Zusatzfeldern für die Jahre vor Planbeginn (ab Alter 21). Alles **real** gerechnet. Neue Konstanten `AHV_MIN_MONTHLY_FULL`, `AHV_PENSION_MONTHS`, `AHV_CONTRIBUTION_START_AGE`; `AHV_MAX_ANNUAL_SINGLE` neu abgeleitet. Warnhinweis in Phasenzellen und Phasen-Detail, wenn Folgephasen existieren. Neue Kapitel 3.5.6, 4.4; Abschnitt 9 um zwei Punkte ergänzt. Zwölf Regressionstests (18 → 30). **Verhaltensänderung:** siehe 9.10. |
|
||||
@@ -1418,6 +1419,106 @@ Die drei Default-Sätze werden **sowohl als UI-Vorschlag als auch in der Berechn
|
||||
verwendet (`num(td.capitalTaxRate, DEFAULT_CAPITAL_TAX_RATE)`). Grund laut Code-Kommentar: Damit
|
||||
ein nicht angetippter Wert nicht fälschlich als 0 gerechnet wird.
|
||||
|
||||
## 4.12 Monte-Carlo-Simulation
|
||||
|
||||
Die deterministische Berechnung nimmt pro Anlage *eine* feste Rendite und *eine* feste Inflation
|
||||
an. Real schwanken beide. Die Monte-Carlo-Simulation (Roadmap Nr. 19, Stufe A, `montecarlo.ts`)
|
||||
würfelt viele tausend mögliche Verläufe und weist die **Erfolgswahrscheinlichkeit** des Plans aus.
|
||||
|
||||
### 4.12.1 Die Nahtstelle in `computePlan`
|
||||
|
||||
`computePlan(plan, sample?)` nimmt optional ein `PlanSample`:
|
||||
|
||||
```ts
|
||||
interface PlanSample {
|
||||
inflation: number[]; // Inflation %/Jahr (Index 0 = Jahr 1)
|
||||
assetReturn: (elementId: string, year: number) => number; // Rendite %/Jahr (1-basiert)
|
||||
}
|
||||
```
|
||||
|
||||
Ohne `sample` rechnet die Funktion **exakt wie bisher** (die geplanten Annahmen). Mit `sample`
|
||||
liefert sie einen einzelnen simulierten Pfad. Voraussetzung war eine Umstellung der Inflation
|
||||
auf ein **kumulatives Deflator-Array** `cumInfl[]` (statt der geschlossenen `(1+i)^t`-Formel),
|
||||
damit die Inflation pro Jahr variieren kann – deterministisch bitgenau identisch, durch die
|
||||
Golden Tests abgesichert. Die AHV-Karriere bleibt bewusst auf der festen Plan-Inflation (sie
|
||||
ist eine Real-Grösse auf Planungsbasis, sie wird nicht mitgewürfelt).
|
||||
|
||||
**Architektur-Vorteil:** `computePlan` ist eine reine Funktion ohne Server-Abhängigkeiten und
|
||||
läuft damit **im Browser**. Die gesamte Simulation rechnet client-seitig – null Serverlast.
|
||||
~10'000 Läufe in rund 1 Sekunde; die Ausführung gibt alle 500 Läufe die Kontrolle ab
|
||||
(Fortschrittsbalken, keine eingefrorene Oberfläche).
|
||||
|
||||
### 4.12.2 Das statistische Modell
|
||||
|
||||
Pro Jahr ein **gemeinsamer Marktschock** `z_markt`; je Element und Jahr:
|
||||
|
||||
```
|
||||
rendite = mittelwert + σ × (ρ × z_markt + √(1−ρ²) × z_eigen)
|
||||
```
|
||||
|
||||
- `z_markt`, `z_eigen`: **standardisierte Student-t** (ν = 5) – „fette Ränder", damit
|
||||
Extremcrashs realistisch häufig auftreten. Eine Normalverteilung macht ein −40%-Jahr zu einem
|
||||
1-in-250-Ereignis; real ist es ~1-in-15. Standardisiert auf Einheitsvarianz → die eingegebene
|
||||
Standardabweichung σ bleibt die tatsächliche.
|
||||
- `ρ = 0.7` → Korrelation zweier riskanter Anlagen ≈ 0.5: **alle riskanten Anlagen fallen im
|
||||
Crash gemeinsam.** Unabhängiges Würfeln würde das Absturzrisiko systematisch unterschätzen.
|
||||
- **Böden:** `max(0, rendite)` für PK/3a (schreiben keine negative Rendite gut),
|
||||
`max(−100, rendite)` sonst.
|
||||
|
||||
Die Inflation wird analog gezogen (eigener Student-t-Schock, Mittelwert + σ), unabhängig vom
|
||||
Marktschock. Der Zufallsgenerator ist **seedbar** (reproduzierbare Läufe).
|
||||
|
||||
### 4.12.3 Zwei Renditezahlen — und warum
|
||||
|
||||
Pro renditetragendem Element (PK, 3a, Sonstiges Vermögen, Immobilie) gibt es im Simulations-Dialog
|
||||
**zwei** Renditen mit verschiedenen Rollen:
|
||||
|
||||
| Zahl | Rolle |
|
||||
|---|---|
|
||||
| **Geplante Rendite** (im Plan) | zeichnet die deterministische Linie = der *Zielbalken* |
|
||||
| **Historische Ø-Rendite** (im Dialog, Pflicht) | der Mittelpunkt, um den die Simulation streut |
|
||||
|
||||
Ohne diese Trennung wäre die Kennzahl „P(erreiche mein geplantes Endvermögen)" **immer ~50 %**,
|
||||
egal welche Rendite man annimmt (der Zielbetrag wüchse ja mit). Erst weil die Simulation um die
|
||||
*historische* Rendite streut, während der Zielbalken auf der *geplanten* steht, wird ein
|
||||
konservativer Plan (tiefe Planannahme) korrekt mit einer höheren Erfolgsquote belohnt als ein
|
||||
optimistischer. Analog auf Plan-Ebene für die Inflation.
|
||||
|
||||
**Ehrliche Grenze (im Dialog ausgewiesen):** Die Simulation misst das Risiko *um deine Annahmen
|
||||
herum* – sie beurteilt **nicht**, ob deine Mittelwerte realistisch sind. Ein zu optimistischer
|
||||
Mittelwert liefert eine beruhigende, aber unrealistische Erfolgsquote (siehe 9.15).
|
||||
|
||||
### 4.12.4 Streuungsstufen
|
||||
|
||||
| Stufe (Rendite) | σ | Beispiele | | Stufe (Inflation) | σ |
|
||||
|---|---|---|---|---|---|
|
||||
| Sehr niedrig | 3 % | Staatsanleihen, Geldmarkt | | Sehr niedrig | 1 % |
|
||||
| Niedrig | 6 % | Immobilien, defensive Mischportfolios | | Niedrig | 2 % |
|
||||
| Moderat | 15 % | breit diversifizierte Aktien-ETFs/Fonds | | Manuell | frei |
|
||||
| Hoch | 25 % | Einzelaktien, Branchen-/Schwellenländerfonds | | | |
|
||||
| Sehr hoch | 55 % | Kryptowährungen, hochspekulative Anlagen | | | |
|
||||
| Manuell | frei | eigene Eingabe | | | |
|
||||
|
||||
Default-Stufe je Typ: PK → sehr niedrig · 3a/Immobilie → niedrig · Sonstiges Vermögen → moderat.
|
||||
Bei Inflation gibt es bewusst nur zwei Stufen (höhere wären Hyperinflations-Annahmen).
|
||||
|
||||
Quellen der σ-Werte: Anleihen ~6 %, globale Aktien ~15–18 %, Schweizer Immobilien(fonds) ~2 %,
|
||||
Bitcoin ~54 %, Schweizer Inflation SD der letzten 20 J. ~1 %. Belege: BSV/Weltbank sowie
|
||||
Markt-/Volatilitätsstatistiken (recherchiert 2026-07-17).
|
||||
|
||||
### 4.12.5 Ergebnis
|
||||
|
||||
| Kennzahl | Bedeutung |
|
||||
|---|---|
|
||||
| **Ruinwahrscheinlichkeit** | Anteil der Läufe mit `ruinAge !== null` (Vermögen fällt vor Planende unter 0) |
|
||||
| **Erfolgswahrscheinlichkeit** | Anteil der Läufe mit Endvermögen ≥ Zielbetrag (nominal, vorbelegt mit dem geplanten Nachlass) |
|
||||
| **Fächer** | je Alterspunkt (Phasengrenzen) das 10-/50-/90-Perzentil des Vermögens; dazu die deterministische Planungslinie |
|
||||
|
||||
Der Median liegt typischerweise **unter** der deterministischen Linie – der „Volatilitäts-Drag"
|
||||
(`geometrisch ≈ arithmetisch − σ²/2`) macht sichtbar, dass die glatte Ein-Zahl-Planung schon
|
||||
leicht zu optimistisch ist. Pflichtfelder: ohne die historischen Ø-Werte startet die Simulation
|
||||
nicht.
|
||||
|
||||
---
|
||||
|
||||
# 5. Technische Spezifikation
|
||||
@@ -1489,6 +1590,7 @@ PlanComputed ← an den Client geliefert
|
||||
| `elements.ts` | Kategorien, Labels, Reihenfolge, JSON-Payload-Typen, Zod-Schemas, `num()` |
|
||||
| `types.ts` | Domänentypen für API und Berechnung |
|
||||
| `constants.ts` | Schweizer Systemparameter |
|
||||
| `montecarlo.ts` | Monte-Carlo-Simulation (Sampler + Treiber). Keine I/O, läuft im Browser. |
|
||||
| `queries.ts` | Prisma-Includes, `toPlanInput()`, Ownership-Abfragen |
|
||||
| `db.ts` | Prisma-Singleton (Global-Cache im Dev, verhindert Connection-Leak bei HMR) |
|
||||
| `auth.ts` | JWT erzeugen/prüfen (Edge-kompatibel via `jose`) |
|
||||
@@ -1712,6 +1814,7 @@ wird der Plan neu geladen; die Berechnung kommt immer vom Server.
|
||||
| `PlanProfileFields` | 101 | Wiederverwendete Grundprofil-Felder |
|
||||
| `PhaseDetail` | 95 | Phase bearbeiten/löschen |
|
||||
| `SparquoteChart` | 64 | Einkommen/Ausgaben pro Jahr mit Spar-/Verzehrbändern |
|
||||
| `MonteCarloDialog` | ~430 | Monte-Carlo-Dialog: Erklärung, Eingaben, Lauf, Ergebnis + Fächer |
|
||||
| `InfoBubble` | 28 | Hilfe-Tooltip |
|
||||
|
||||
### 5.5.3 Wiederverwendungsmuster
|
||||
@@ -1919,10 +2022,11 @@ Zielumgebung: Hetzner CX23, Traefik als Reverse Proxy, Domain `fpt.aicds.ch`, Gi
|
||||
## 8.1 Teststrategie
|
||||
|
||||
Getestet wird ausschliesslich der Berechnungskern – bewusst, da dort die Fachlogik und das
|
||||
Regressionsrisiko liegen. `src/lib/calculations.test.ts` enthält 41 Tests (AHV-Rentenformel,
|
||||
Immobilie, Teilverkauf, Sonderamortisation, AHV einkommensabhängig, „V5 Golden Tests"),
|
||||
ausgeführt mit Vitest in der Node-Umgebung (`vitest.config.ts`, Include `src/**/*.test.ts`).
|
||||
Es gibt **keine** Komponenten-, API- oder E2E-Tests.
|
||||
Regressionsrisiko liegen. `src/lib/calculations.test.ts` (41 Tests: AHV-Rentenformel, Immobilie,
|
||||
Teilverkauf, Sonderamortisation, AHV einkommensabhängig, „V5 Golden Tests") und
|
||||
`src/lib/montecarlo.test.ts` (7 Tests) ergeben zusammen **48 Tests**, ausgeführt mit Vitest in
|
||||
der Node-Umgebung (`vitest.config.ts`, Include `src/**/*.test.ts`). Es gibt **keine** Komponenten-,
|
||||
API- oder E2E-Tests.
|
||||
|
||||
## 8.2 Testfälle
|
||||
|
||||
@@ -1945,6 +2049,10 @@ Es gibt **keine** Komponenten-, API- oder E2E-Tests.
|
||||
| **Immobilie: Verkauf** | Grundstückgewinnsteuer auf `Verkaufspreis − Kaufpreis`, nicht auf den Verkehrswert |
|
||||
| **Teilverkauf (42)** | Betrag ins Cash (→ `capitalInflow`), Rest bleibt aktiv; am Endwert gekappt; Halten/Vollverkauf unverändert |
|
||||
| **Sonderamortisation (15)** | Einmaltilgung senkt Restschuld, belastet Cash (→ `capitalInvest`), am Restsaldo gekappt |
|
||||
| **MC: Determinismus** | Streuung 0 reproduziert exakt das deterministische Ergebnis (Bänder kollabieren) |
|
||||
| **MC: Volatilität / Vol-Drag** | σ > 0 spreizt p10<median<p90; Median unter dem deterministischen Wert |
|
||||
| **MC: Erfolg / Reproduzierbarkeit** | P(≥ Ziel) fällt mit steigendem Ziel; gleicher Seed → identisches Ergebnis |
|
||||
| **MC: Boden / Ruin** | 0%-Boden hält PK/3a ≥ Startwert; sicherer Verzehr → Ruinwahrscheinlichkeit 100 % |
|
||||
| Test 1 – Ansparen | Einkommen +2 % nominal, Ausgaben real flach: Endvermögen 761'654 nominal / 565'928 real (±1 %), kein negatives Cash |
|
||||
| Test 2 – Verzehr/Ruin | Rente nominal fix 60k, Ausgaben real 100k, Vermögen 900k @3 %: `ruinAge === 94` |
|
||||
| Test 3 – Cash-Ausgleich | Sparrate 6'364: `cashEnd === 5472`, nie negativ |
|
||||
@@ -2125,7 +2233,21 @@ hinterfragt. Der Benutzer kennt seine Steuerrechnung dagegen exakt aus der letzt
|
||||
**Heutiger Weg:** ein normales Ausgaben-Element „Steuern" – dafür braucht es kein neues Feld.
|
||||
Echte Steuerlogik ist Roadmap Nr. 23, mit Kanton und Gemeinde als Eingabe.
|
||||
|
||||
## 9.15 Kleinere Beobachtungen
|
||||
## 9.15 Monte-Carlo misst Risiko um die Annahmen, nicht deren Richtigkeit
|
||||
|
||||
Die Simulation streut um die eingegebenen Mittelwerte (historische Rendite/Inflation). Sie
|
||||
beurteilt **nicht**, ob diese Mittelwerte realistisch sind. Ein zu optimistischer Mittelwert
|
||||
liefert eine beruhigende, aber unrealistische Erfolgsquote – die präzise Prozentzahl täuscht
|
||||
dann Sicherheit vor. Der Dialog weist das explizit aus; die Mittelwerte sind Pflichtfelder ohne
|
||||
Default, damit sie bewusst gesetzt werden.
|
||||
|
||||
Weitere bewusste Vereinfachungen der Stufe A: Inflation und Renditen werden **unabhängig**
|
||||
gezogen (real sind sie negativ korreliert); die Normalverteilungs-Alternative wird gar nicht
|
||||
angeboten (fette Ränder fest eingebaut); die Simulationsparameter werden **nicht persistiert**
|
||||
(ephemer im Dialog). Ein historischer Backtest (Stufe B) und korrelierte/vollständigere Modelle
|
||||
(Stufe C) sind offen.
|
||||
|
||||
## 9.16 Kleinere Beobachtungen
|
||||
|
||||
- `planToCsv(plan, computed)` erhält den `plan`-Parameter, verwendet ihn aber nicht.
|
||||
- Der Typ `Selection` in `PlanView` hat nur eine Variante (`{ type: "phase" }`) – ein Rest der
|
||||
|
||||
Reference in New Issue
Block a user