summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md32
-rw-r--r--app/README.md48
-rw-r--r--app/index.html95
-rw-r--r--app/service-worker.js2
-rw-r--r--bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsxbin151973 -> 118375 bytes
-rw-r--r--build_bav.py47
6 files changed, 178 insertions, 46 deletions
diff --git a/README.md b/README.md
index 110ba87..3ce61cf 100644
--- a/README.md
+++ b/README.md
@@ -8,12 +8,14 @@ Vollständig formelbasierte Excel-Arbeitsmappe (6.359 Formeln), erzeugt aus eine
| `bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx` | Das Modell. Nur die gelben Zellen im Blatt `Eingaben` ändern. |
| `build_bav.py` | Generator. Erzeugt die Mappe vollständig neu. **Einzige Quelle der Wahrheit** — Änderungen gehören hierhin, nicht in die xlsx. |
| `app/` | **Web-App zum Weitergeben.** Derselbe Rechenkern in JavaScript, mobilfreundlich, offlinefähig, mit Sensitivitätskurven. Siehe `app/README.md`. |
+| `tests/` (in den Sitzungsausgaben) | Regressionslauf gegen einen frischen LibreOffice-Recalc dieser Mappe. |
| `README.md` | Diese Datei. |
-> **Zwei Quellen der Wahrheit.** Der Rechenkern liegt jetzt zweimal vor: als Formelgenerator in
+> **Zwei Quellen der Wahrheit.** Der Rechenkern liegt zweimal vor: als Formelgenerator in
> `build_bav.py` und als JavaScript in Teil 1 von `app/index.html`. Sie stimmen auf 1,3·10⁻¹⁴
-> relativ überein (drei Testfälle, 22 Aggregate). Bei jeder Rechtsänderung **beide** anfassen —
-> und danach den Abgleich wiederholen.
+> relativ überein — **sechs** Testfälle, jeweils rund 20 Aggregate, gegen einen frischen
+> LibreOffice-Recalc dieser Mappe. Bei jeder Rechtsänderung **beide** anfassen und danach den
+> Abgleich wiederholen; er ist der einzige Schutz davor, dass sie auseinanderlaufen.
---
@@ -59,7 +61,7 @@ Punkt 4 ist der dominante Nachteil der bAV; Punkt 2 ihr einziger struktureller V
| `Parameter` | Rechengrößen 2026 mit Fundstelle, abgeleitete Größen |
| `Umrechnung` | Bisektion Nettoaufwand → Bruttobeitrag |
| `Ansparphase` | 62 Spalten × 50 Jahre (Zeilen 8–57) |
-| `Auszahlung` | 85 Spalten × 34 Jahre (Zeilen 8–41) |
+| `Auszahlung` | 58 Spalten × 34 Jahre (Zeilen 8–41) |
| `Cashflow` | Zahlungsströme für IRR und Barwerte (Zeilen 5–69) |
| `Vergleich` | Ergebnis, Diagnose, monatliche Leistung, Diagramm |
@@ -70,7 +72,7 @@ Diese werden von anderen Blättern referenziert — beim Einfügen von Zeilen **
| Zeile | Inhalt |
|---|---|
| 5–19 | Haupttabelle (13 = Barwert Netto-Auszahlungen, 15 = Barwert Netto-Ertrag, 17 = IRR) |
-| 22–36 | Diagnose (31–35 = Freibetragsanalyse) |
+| 22–37 | Diagnose (31–35 = Freibetragsanalyse, 36/37 = Memo-Zeilen für Rentenzahlungen nach dem Horizont) |
| 40–48 | Monatliche Leistung (47 = äquivalente Monatsleistung real) |
| 50–52 | Diagrammdaten |
| 55 | Diagramm |
@@ -99,13 +101,17 @@ zweimal besteuert würde:
| | gefördert | nicht gefördert |
|---|---|---|
| **bAV** | bis 8 % BBG-RV (§ 3 Nr. 63 EStG) → 100 % nachgelagert steuerpflichtig | Rest, aus versteuertem Entgelt → halber Unterschiedsbetrag (Kapital) bzw. Ertragsanteil (Rente) |
-| **AV-Depot** | Eigenbeitrag bis 1.800 € + Zulagen → 100 % nachgelagert | Beiträge darüber + reinvestierte Erstattung → halber Unterschiedsbetrag |
+| **AV-Depot** | Eigenbeitrag bis 1.800 € + Zulagen → 100 % nachgelagert | Beiträge darüber + reinvestierte Erstattung → halber Unterschiedsbetrag (Plan bzw. Einmalbetrag) oder Ertragsanteil (Rente) |
Der Überschuss über den Jahreshöchstbetrag von 6.840 € läuft in eine eigene Schiene
(„Sleeve"), die wie das Privatdepot besteuert wird — so bleibt der Nettoaufwand identisch.
### Auszahlungsphase
+- **Auszahlungsform je Produkt getrennt**: `auszform` für die bAV (Kapital oder Rente),
+ `av_auszform` für das Depot (Auszahlungsplan oder Rente), dazu `teilkap` für den Einmalbetrag
+ von bis zu 30 % beim Depot, der in beiden Formen wirkt. Bei Verrentung geht das Restkapital an
+ den Anbieter, ein Depotkonto gibt es danach nicht mehr
- **KV/PV nur bei der bAV**: voller Satz allein vom Rentner (§ 250 SGB V), Freibetrag
1/20 Bezugsgröße für KV, Freigrenze für PV, Kapitalleistung auf 120 Monate verteilt,
gemeinsame BBG mit der gesetzlichen Rente (§ 223 Abs. 3 SGB V)
@@ -133,9 +139,10 @@ Werte im Cache ab — vorher liest jedes Werkzeug `None`.
2. **Recalc auf dem gemounteten Ordner hängt.** Nach `/tmp` kopieren, dort rechnen,
zurückkopieren.
3. **Notiztexte dürfen nicht mit `=` beginnen** — openpyxl schreibt sie sonst als Formel.
-4. **Hilfszellen kollidieren mit Datenspalten.** Die Auszahlung reicht bis Spalte CG;
- Hilfszellen liegen bei CE/CG **unterhalb** von Zeile 8, was funktioniert, aber fragil
- ist. Beim Hinzufügen von Spalten prüfen.
+4. **Hilfszellen kollidieren mit Datenspalten.** Die Auszahlung reicht inzwischen bis Spalte
+ **BF** (58 Spalten), die Hilfszellen liegen bei CE/CG **unterhalb** von Zeile 8. Das
+ funktioniert, ist aber fragil: beim Hinzufügen von Spalten jedes Mal nachrechnen. Die
+ Erweiterung um die Depotverrentung hat drei Spalten gekostet.
---
@@ -238,6 +245,13 @@ an einer politisch gesetzten Zahl.
(entscheidet 116 Basispunkte)
- [ ] Prüfen, ob bereits andere Versorgungsbezüge bestehen — der Freibetrag gilt pro
Person, nicht pro Vertrag
+- [x] ~~Verrentung des Altersvorsorgedepots~~ — eingebaut: eigene Eingabe `av_auszform`,
+ unabhängig von der bAV-Auszahlungsform, mit eigenem Rentenfaktor und eigener Dynamik.
+ Nicht geförderte Schicht dann mit dem Ertragsanteil statt dem halben Unterschiedsbetrag,
+ zweite Memo-Zeile für Zahlungen nach dem Vergleichshorizont.
+- [ ] Teilkapitalisierung der **bAV** (0–30 % einmalig, Rest verrentet). Beim Depot ist das seit
+ der Erweiterung möglich, bei der bAV noch nicht — das Modell kennt dort nur ganz oder gar
+ nicht. Die einzige verbliebene Asymmetrie zwischen den beiden geförderten Produkten.
- [ ] Optional: jahresweise Inversion des Nettoaufwands statt nur im ersten Jahr
- [ ] Optional: Sensitivitätstabelle über Rendite × Option (Excel-Datentabellen werden von
openpyxl nicht geschrieben; Alternative wäre eine vorgerechnete Matrix)
diff --git a/app/README.md b/app/README.md
index 4f1dd1f..5f35f7c 100644
--- a/app/README.md
+++ b/app/README.md
@@ -50,7 +50,7 @@ selbst zu aktualisieren.
| Teil | Inhalt |
|---|---|
| 1 | **Rechenkern** — Portierung von `build_bav.py`. Reine Funktionen, kein DOM |
-| 2 | Felddefinitionen: 77 Eingaben mit Bereich, Einheit, Erklärung, Fundstelle |
+| 2 | Felddefinitionen: 80 Eingaben mit Bereich, Einheit, Erklärung, Fundstelle |
| 3 | Zustand, `localStorage`, Kodierung für den Teilen-Link |
| 4 | Formatierung (de-DE, Komma als Dezimaltrenner in beiden Richtungen) |
| 5 | Kennzahlen |
@@ -100,13 +100,22 @@ existiert die Umkehrung nicht. Die App sagt das statt still nichts zu tun.
## 4. Prüfung
-Der Kern ist gegen die Excel-Mappe geprüft, drei unabhängige Fälle, jeweils 22 Aggregate:
+Der Kern ist gegen die Excel-Mappe geprüft, sechs unabhängige Fälle, jeweils rund 20 Aggregate:
| Testfall | größte relative Abweichung |
|---|---|
| Basisfall (40 J., 75.000 €, 300 €/Monat, GKV, Kapital, Ausscheiden mit 63) | 3,6 · 10⁻¹⁵ |
| Splitting, KiSt 9 %, kinderlos, PKV, Rente, Fünftelregelung, Modus 2, Teilkapital | 1,3 · 10⁻¹⁴ |
| GKV + lebenslange Rente, Zuschussmodus 0, KiSt 8 %, Teilkapital 30 % | 6,2 · 10⁻¹⁵ |
+| **AVD verrentet** | 5,2 · 10⁻¹⁵ |
+| **AVD verrentet + 30 % Einmalbetrag** | 3,6 · 10⁻¹⁵ |
+| **beide Produkte verrentet, Splitting, PKV, andere Zeitachse, 2 Kinder** | 4,4 · 10⁻¹⁵ |
+
+> Der Prüfanker ist **erhalten geblieben**: `build_bav.py` wurde für die Depotverrentung
+> mitgezogen (neue Eingaben, drei neue Spalten im Blatt `Auszahlung`, zweite Memo-Zeile im
+> Blatt `Vergleich`), die Mappe neu erzeugt und mit LibreOffice durchgerechnet — 0 Formelfehler,
+> und der Basisfall ist auf den Cent unverändert. Der JS-Kern wird gegen diesen Recalc geprüft,
+> nicht gegen sich selbst.
Das sind wenige Dutzend ULP — akkumulierter Gleitkommafehler, nichts Systematisches.
Die Referenzwerte stammen aus einem LibreOffice-Recalc der Mappe mit denselben Eingaben.
@@ -128,7 +137,9 @@ Die Testskripte liegen unter `outputs/tests/`, `./tests/alle.sh` fährt alles du
nach dem Stilwechsel zwei echte Fehler gefunden — den unformatierten Wasserfall-Umschalter
und sechs verwaiste Hilfsklassen. Zusätzlich: jede per `var()` referenzierte Farbvariable
muss definiert sein.
-- **DOM-Test:** 34 Zusicherungen über jsdom — Karten, Sichtbarkeitsregeln, Regler, Segment-
+- **Excel-Abgleich der Depotverrentung** (`7-avrente.js`): die drei neuen Fälle gegen einen
+ frischen LibreOffice-Recalc, einschließlich der Memo-Zeile für Zahlungen nach dem Horizont.
+- **DOM-Test:** 57 Zusicherungen über jsdom — Karten, Sichtbarkeitsregeln, Regler, Segment-
umschalter, Brutto-Netto-Kopplung in beide Richtungen, Fazittexte, mitlaufende Leiste,
Rundung, Tabs, Kennzahlwechsel, Referenz merken, Zurücksetzen, `localStorage`.
Die beiden seltenen Zweige des Fazits — „zulasten“ und „die Reihenfolge kehrt sich um“ —
@@ -198,13 +209,31 @@ In der Auszahlungsphase arbeiten **zwei verschiedene Zinssätze**, und das ist k
| … im Produkt (Privatdepot) | `rnet_priv` = 6,80 % | erst bei Entnahme |
| … ausgezahlt, noch nicht verbraucht | `wiederanl` = **5,54 %** | bereits versteuert |
-Wieviel davon betroffen ist, entscheidet die **Auszahlungsform** — und die gibt es bei *beiden*
-geförderten Produkten:
+Wieviel davon betroffen ist, entscheidet die **Auszahlungsform**. Beide geförderten Produkte
+haben eine, und sie sind **unabhängig voneinander** einstellbar — wer die bAV als Kapital nimmt,
+kann das Depot trotzdem verrenten:
| | Einmalbetrag | Rest |
|---|---|---|
-| bAV | 0 % oder 100 % (`auszform`) | — |
-| Altersvorsorgedepot | 0 – 30 % (`teilkap`) | bleibt im Produkt |
+| bAV | 0 % oder 100 % (`auszform`) | lebenslange Rente |
+| Altersvorsorgedepot | 0 – 30 % (`teilkap`) | Auszahlungsplan **oder** lebenslange Rente (`av_auszform`) |
+
+### Die Verrentung des Altersvorsorgedepots
+
+Beim Depot hängen an der Auszahlungsform **drei** Wirkungen — nicht vier wie bei der bAV, weil
+KV/PV entfällt (keine Versorgungsbezüge):
+
+1. **Ertragsanteil statt halbem Unterschiedsbetrag** für die nicht geförderte Schicht, im
+ Basisfall 30,7 % des Kapitals. Steuerpflichtig sind dann 17,0 % der Rente statt 26,2 % der
+ Planentnahme (§ 22 Nr. 5 S. 2 Buchst. a EStG). Ein echter Vorteil der Verrentung.
+2. **Rentenfaktor statt Marktrendite** auf das Restkapital — eigenes Eingabefeld `rentfak_av`,
+ weil ein Depotvertrag ohne Versicherungsmantel auskommt und günstiger sein kann.
+3. **Zahlungen nach dem Horizont**, die der Vergleich nicht mitzählt. Eigene Memo-Zeile in der
+ Diagnose und eine Live-Notiz am Schalter; im Basisfall 145.618 €.
+
+Die geförderte Schicht ist in beiden Formen voll nachgelagert steuerpflichtig
+(§ 22 Nr. 5 S. 1 EStG) — daran ändert die Auszahlungsform nichts. Der Einmalbetrag wirkt in
+beiden Formen und wird als Kapital besteuert, auch wenn der Rest verrentet wird.
Beim Altersvorsorgedepot kostet der Einmalbetrag aus denselben zwei Gründen wie bei der bAV:
volle Progression im Zuflussjahr und danach nur noch der Satz nach Abgeltungsteuer. Im
@@ -221,8 +250,9 @@ und Einmalbeträge **exakt 0,00 €** — der Annuitätenfaktor passt zur Rekurs
stillschweigend einbehalten. Und der Überschuss über den Jahreshöchstbetrag („Sleeve“) wird
ohne Einmalbetrag verrentet; das ist eine Modellkonvention, kein Rechtssatz.
-**Modelllücke, ausdrücklich:** die bAV kennt hier nur ganz oder gar nicht. Eine
-Teilkapitalisierung der Betriebsrente, die es in der Praxis gibt, ist nicht abgebildet.
+**Verbleibende Modelllücke, ausdrücklich:** die bAV kennt weiterhin nur ganz oder gar nicht.
+Eine Teilkapitalisierung der Betriebsrente, die es in der Praxis gibt, ist nicht abgebildet —
+anders als beim Depot, wo Einmalbetrag und Verrentung seit dieser Fassung kombinierbar sind.
### Der Kapitalverlauf
diff --git a/app/index.html b/app/index.html
index 9b27758..408cc8b 100644
--- a/app/index.html
+++ b/app/index.html
@@ -429,7 +429,8 @@ var DEFAULT_INPUT = {
kennt keinen Umschalter mehr, sondern koppelt Brutto und Netto über bruttoVonNetto() */
agzus: 0.15, agzus_sv: 2, auszform: 1, rentfak: 26, rentdyn: 0.01, fuenftel: 0,
rendite: 0.07, k_bav: 0.006, k_av: 0.005, k_priv: 0.002, infl: 0.02,
- kv_ruhe: 1, sonst_zve: 22000, rente_br: 24000, teilkap: 0.0
+ kv_ruhe: 1, sonst_zve: 22000, rente_br: 24000, teilkap: 0.0,
+ av_auszform: 1, rentfak_av: 26, rentdyn_av: 0.01
};
var DEFAULT_PARAM = {
bbg_rv: 101400, bbg_kv: 69750, bezug: 3955, de_rv: 51944, rw: 42.52,
@@ -636,18 +637,38 @@ function rechne(input, param, flags) {
z.bav_net = z.bav_br - z.bav_kvpv - z.st_bav - z.mind_br + z.mind_kv;
if (u === 0) { z.gef_a = KG; z.ngef_a = KN; z.sl_a = KS; z.pr_a = KP; z.pr_ba = KPB; }
else {
- z.gef_a = max(0, (q.gef_a - q.gef_e) * (1 + D.rnet_av));
- z.ngef_a = max(0, (q.ngef_a - q.ngef_e) * (1 + D.rnet_av));
+ /* Bei Verrentung geht das Restkapital an den Versicherer — danach gibt
+ es kein Depotkonto mehr, genau wie bei der bAV. */
+ z.gef_a = I.av_auszform === 2 ? 0 : max(0, (q.gef_a - q.gef_e) * (1 + D.rnet_av));
+ z.ngef_a = I.av_auszform === 2 ? 0 : max(0, (q.ngef_a - q.ngef_e) * (1 + D.rnet_av));
z.sl_a = max(0, (q.sl_a - q.sl_e) * (1 + D.rnet_priv));
z.pr_a = max(0, (q.pr_a - q.pr_e) * (1 + D.rnet_priv));
z.pr_ba = max(0, q.pr_ba - q.pr_ba * (q.pr_a === 0 ? 0 : q.pr_e / q.pr_a));
}
var tk = u === 0 ? I.teilkap : 0;
- z.gef_e = z.plan === 0 ? 0 : min(z.gef_a, KG * tk + KG * (1 - I.teilkap) * ANN_AV);
- z.ngef_e = z.plan === 0 ? 0 : min(z.ngef_a, KN * tk + KN * (1 - I.teilkap) * ANN_AV);
+ /* Verrentetes Restkapital je Schicht; die laufende Depotrente wird nach
+ ihrem Anteil daran aufgeteilt — dieselbe Konstruktion wie bei der bAV. */
+ var KGR = KG * (1 - I.teilkap), KNR = KN * (1 - I.teilkap), KRR = KGR + KNR;
+ var renteAv = KRR * I.rentfak_av / 1e4 * 12 * pow(1 + I.rentdyn_av, u) * z.plan;
+ z.av_rest = (I.av_auszform === 2 && z.plan === 0)
+ ? KRR * I.rentfak_av / 1e4 * 12 * pow(1 + I.rentdyn_av, u) : 0;
+ if (I.av_auszform === 1){
+ z.gef_e = z.plan === 0 ? 0 : min(z.gef_a, KG * tk + KGR * ANN_AV);
+ z.ngef_ek = z.plan === 0 ? 0 : min(z.ngef_a, KN * tk + KNR * ANN_AV);
+ z.ngef_er = 0;
+ } else {
+ z.gef_e = KG * tk + renteAv * (KRR === 0 ? 0 : KGR / KRR);
+ z.ngef_ek = KN * tk;
+ z.ngef_er = renteAv * (KRR === 0 ? 0 : KNR / KRR);
+ }
+ z.ngef_e = z.ngef_ek + z.ngef_er;
z.sl_e = z.plan === 0 ? 0 : min(z.sl_a, KS * ANN_PR);
z.sl_st = max(0, z.sl_e * (1 - (KS === 0 ? 1 : min(1, KSB / KS))) * (1 - P.tfs)) * D.kapst_eff;
- z.av_stpfl = z.gef_e + z.ngef_e * (1 - (KN === 0 ? 1 : min(1, S_AVN / KN))) * P.halb;
+ /* Kapitalteil der nicht geförderten Schicht: halber Unterschiedsbetrag.
+ Rententeil: Ertragsanteil (§ 22 Nr. 5 S. 2 Buchst. a EStG). */
+ z.av_stpfl = z.gef_e
+ + z.ngef_ek * (1 - (KN === 0 ? 1 : min(1, S_AVN / KN))) * P.halb
+ + z.ngef_er * P.ertragsq;
z.zve_av = max(0, z.sonst + z.av_stpfl);
z.stg_av = steuer(z.zve_av, z.lam);
z.av_net = z.gef_e + z.ngef_e - (z.stg_av - z.stg_b) + z.sl_e - z.sl_st;
@@ -788,7 +809,7 @@ function rechne(input, param, flags) {
vbQuote: (D.frei_vb * Z[0].lohn) === 0 ? 0 : Z[0].bav_vb / (D.frei_vb * Z[0].lohn),
kapFreiKapital: 120 * D.frei_vb * Z[0].lohn,
kapFreiRente: I.rentfak === 0 ? 0 : D.frei_vb * Z[0].lohn / I.rentfak * 1e4,
- memoRest: ss('bav_rest'),
+ memoRest: ss('bav_rest'), memoRestAv: ss('av_rest'),
wiederanl: D.wiederanl, kapstEff: D.kapst_eff, bestAnteil: D.best_anteil
}
};
@@ -983,12 +1004,33 @@ var FIELDS = [
+ 'steuerpflichtig (Grenzbelastung dort ' + pct(gr,1) + ') und verzinst sich danach nur '
+ 'noch mit dem Satz nach Abgeltungsteuer statt mit der Produktrendite.';
},
- hint:'Bis 30 % des angesparten Kapitals sind zu Beginn der Auszahlungsphase als Einmalbetrag möglich. Das ist beim Altersvorsorgedepot dieselbe Entscheidung wie „Kapital oder Rente“ bei der bAV, nur gedeckelt — und sie kostet aus denselben zwei Gründen: volle Progression im Zuflussjahr und danach nur noch der Zinssatz nach Abgeltungsteuer. Der Überschuss über den Jahreshöchstbetrag („Sleeve“) wird ohne Einmalbetrag verrentet; das ist eine Modellkonvention, kein Rechtssatz.' },
-
- /* ---------------- bAV im Detail ---------------- */
- { k:'rentfak', src:'I', g:'bav', kind:'num', label:'Rentenfaktor', min:0, max:60, step:0.5, unit:'€ je 10.000 €',
+ hint:'Bis 30 % des angesparten Kapitals sind zu Beginn der Auszahlungsphase als Einmalbetrag möglich — in beiden Auszahlungsformen. Er kostet aus zwei Gründen: volle Progression im Zuflussjahr und danach nur noch der Zinssatz nach Abgeltungsteuer. Der Überschuss über den Jahreshöchstbetrag („Sleeve“) wird ohne Einmalbetrag verrentet; das ist eine Modellkonvention, kein Rechtssatz.' },
+ { k:'av_auszform', src:'I', g:'kern', kind:'seg', label:'Altersvorsorgedepot: Rest',
+ opts:[[1,'Auszahlungsplan'],[2,'lebenslange Rente']],
+ /* Bewusst ein eigener Schalter: wer die bAV als Kapital nimmt, kann das
+ Depot trotzdem verrenten. Eine gemeinsame Umschaltung erzwänge eine
+ Kopplung, die es rechtlich nicht gibt. */
+ hint:'Was mit dem Kapital jenseits des Einmalbetrags geschieht — unabhängig von der bAV-Entscheidung darüber. Auszahlungsplan: das Geld bleibt investiert und wird verzinst entnommen; die nicht geförderte Schicht wird mit dem halben Unterschiedsbetrag besteuert. Lebenslange Rente: das Kapital geht an den Anbieter, dafür wird die nicht geförderte Schicht nur mit dem Ertragsanteil besteuert (§ 22 Nr. 5 S. 2 Buchst. a EStG, 17 % ab Alter 67 statt hier rund 26 %). Die geförderte Schicht ist in beiden Fällen voll nachgelagert steuerpflichtig, das ändert sich nicht.',
+ liveNote:function(R){
+ if (S.I.av_auszform !== 2) return null;
+ if (!(R.diagnose.memoRestAv > 1)) return null;
+ return 'Über den Vergleichshorizont hinaus zahlt diese Rente noch ' + eur(R.diagnose.memoRestAv)
+ + ', die hier NICHT mitgezählt werden. Ein fester Horizont unterschätzt jede lebenslange '
+ + 'Rente systematisch — wer alt wird, gewinnt hier.';
+ } },
+
+ /* ---------------- Auszahlungsformen im Detail ---------------- */
+ { k:'rentfak_av', src:'I', g:'bav', kind:'num', label:'Rentenfaktor Altersvorsorgedepot', min:0, max:60, step:0.5, unit:'€ je 10.000 €',
+ showIf:function(I){return I.av_auszform===2;},
+ hint:'Nur bei Verrentung des Depots. Voreingestellt wie bei der bAV; ein Depotvertrag kommt ohne Versicherungsmantel aus und kann deshalb günstiger sein — den Wert aus dem konkreten Angebot eintragen. Bei 0 gibt es keine Rente.' },
+ { k:'rentdyn_av', src:'I', g:'bav', kind:'pct', label:'Dynamik der Depotrente p.a.', min:0, max:0.05, step:0.0025,
+ showIf:function(I){return I.av_auszform===2;},
+ hint:'Überschussbeteiligung bzw. Fondsentwicklung in der Rentenphase des Depotvertrags.' },
+ { k:'rentfak', src:'I', g:'bav', kind:'num', label:'Rentenfaktor bAV', min:0, max:60, step:0.5, unit:'€ je 10.000 €',
+ showIf:function(I){return I.auszform===2;},
hint:'Nur bei Auszahlungsform „Rente“. 26 € Monatsrente je 10.000 € entspricht etwa 3,1 % Entnahmequote. Steht im Produktinformationsblatt.' },
{ k:'rentdyn', src:'I', g:'bav', kind:'pct', label:'Dynamik der bAV-Rente p.a.', min:0, max:0.05, step:0.0025,
+ showIf:function(I){return I.auszform===2;},
hint:'Überschussbeteiligung bzw. Fondsentwicklung in der Rentenphase.' },
{ k:'fuenftel', src:'I', g:'bav', kind:'seg', label:'Fünftelregelung prüfen', opts:[[0,'nein (konservativ)'],[1,'ja']],
hint:'§ 34 EStG ist bei planmäßiger Kapitalabfindung aus § 3 Nr. 63-Verträgen nach BFH X R 23/15 nur ausnahmsweise anwendbar. Konservativ: nein.' },
@@ -1092,7 +1134,7 @@ var FIELDS = [
];
var GROUPS = [
- { g:'bav', t:'Betriebliche Altersvorsorge im Detail' },
+ { g:'bav', t:'Auszahlungsformen im Detail' },
{ g:'person',t:'Zeitachse, Ausscheiden, Teilzeit' },
{ g:'steuer',t:'Steuer, Veranlagung, Kinder' },
{ g:'markt', t:'Kosten und Inflation' },
@@ -1926,12 +1968,24 @@ function drawKapital(){
OPTS.forEach(function(o){ zeichne(frei[o.k], o.raw, 1, [3,2]); });
OPTS.forEach(function(o){ zeichne(ges[o.k], o.raw, 2, null); });
- if (S.I.auszform === 2 && R.kapital.bav > 1){
- var yA = Y(R.kapital.bav), yB = Y(R.vermoegen.bav[1]);
- g.fillStyle = css('--white'); g.fillRect(X(rb)+5, (yA+yB)/2-7, 118, 14);
- g.fillStyle = OPTS[0].raw; g.font='bold '+UIFONT; g.textAlign='left'; g.textBaseline='middle';
- g.fillText('Kapital → Rentenanspruch', X(rb)+8, (yA+yB)/2);
- }
+ /* Verrentung heisst: Kapital gegen Anspruch getauscht. Ohne Beschriftung
+ liest man den Absturz als Verlust — und das betrifft beide Produkte. */
+ var tausch = [];
+ if (S.I.auszform === 2 && R.kapital.bav > 1)
+ tausch.push({ o: OPTS[0], a: R.kapital.bav, b: R.vermoegen.bav[1] });
+ if (S.I.av_auszform === 2 && R.vermoegenLiquid.av[1] < R.vermoegen.av[1] - 1e-9 ||
+ (S.I.av_auszform === 2 && R.kapital.av > 1))
+ tausch.push({ o: OPTS[1], a: R.kapital.av, b: R.vermoegen.av[1] });
+ var marken = tausch.map(function(x){ return { y:(Y(x.a)+Y(x.b))/2, c:x.o.raw }; });
+ marken.sort(function(p,q){ return p.y - q.y; });
+ for (var mi = 1; mi < marken.length; mi++)
+ if (marken[mi].y - marken[mi-1].y < 16) marken[mi].y = marken[mi-1].y + 16;
+ g.font='bold '+UIFONT; g.textAlign='left'; g.textBaseline='middle';
+ marken.forEach(function(m){
+ var t = 'Kapital → Rentenanspruch', tw = g.measureText(t).width;
+ g.fillStyle = css('--white'); g.fillRect(X(rb)+5, m.y-7, tw+6, 14);
+ g.fillStyle = m.c; g.fillText(t, X(rb)+8, m.y);
+ });
$('#legend').innerHTML = OPTS.map(function(o){
return '<span><i style="background:'+o.raw+'"></i>'+o.name+'</span>';
@@ -1953,6 +2007,10 @@ function drawKapital(){
+ 'Unverfallbarkeit, nicht Verfügbarkeit), beim Altersvorsorgedepot ist eine Entnahme vor 65 '
+ 'förderschädlich und frei nur der Überschuss über den Jahreshöchstbetrag, das Privatdepot ist '
+ 'jederzeit frei. Das ist die einzige Achse, auf der eine Kapitalauszahlung gut aussieht. '
+ + (S.I.av_auszform === 2
+ ? 'Weil Sie das Depot verrenten, geht auch dort das Kapital an den Anbieter — die Kurve '
+ + 'fällt entsprechend, obwohl nichts verloren ist. '
+ : '')
+ 'Und noch einmal ausdrücklich: die Kurven zeigen das Vermögen aus DIESER einen Entscheidung, '
+ 'nicht Ihr Gesamtvermögen.';
}
@@ -2127,6 +2185,7 @@ function renderDetails(){
{ c:['beitragsfrei mögliches Kapital (Kapitalauszahlung)', eur(d.kapFreiKapital)] },
{ c:['beitragsfrei mögliches Kapital (lebenslange Rente)', eur(d.kapFreiRente)] },
{ c:['MEMO: bAV-Rentenzahlungen nach dem Horizont', eur(d.memoRest)] },
+ { c:['MEMO: Depotrente nach dem Horizont', eur(d.memoRestAv)] },
{ c:['Wiederanlagezins nach Steuern', pct(d.wiederanl)] },
{ c:['effektive Abgeltungsteuer inkl. Soli/KiSt', pct(d.kapstEff,3)] },
{ c:['Besteuerungsanteil der gesetzlichen Rente', pct(d.bestAnteil,1)] }
diff --git a/app/service-worker.js b/app/service-worker.js
index e83c9b1..b68ba71 100644
--- a/app/service-worker.js
+++ b/app/service-worker.js
@@ -9,7 +9,7 @@
* Beim Ändern der App die Version hochzählen — sonst sieht ein bereits
* installierter Client die Änderung erst nach zwei Aufrufen.
*/
-const VERSION = 'av-v9';
+const VERSION = 'av-v10';
const ASSETS = [
'./',
'./index.html',
diff --git a/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx b/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx
index b7e62bf..a04ea94 100644
--- a/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx
+++ b/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx
Binary files differ
diff --git a/build_bav.py b/build_bav.py
index add91a9..61d7fc9 100644
--- a/build_bav.py
+++ b/build_bav.py
@@ -8,7 +8,9 @@ from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter as gcl
from openpyxl.chart import BarChart, Reference
-OUT = "/sessions/determined-nice-faraday/mnt/outputs/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx"
+import os
+OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx")
FONT = "Arial"
BLUE = Font(name=FONT, size=10, color="0000FF")
@@ -88,7 +90,10 @@ inp = [
("kv_ruhe", "Krankenversicherung im Ruhestand: 1 = GKV, 2 = PKV", 1, "1/2", "Entscheidender Hebel: in der GKV sind Versorgungsbezuege mit dem vollen Beitragssatz belegt, private Altersvorsorge nicht."),
("sonst_zve", "sonstiges zu versteuerndes Einkommen im Ruhestand", 22000, "EUR", "Vor allem der steuerpflichtige Teil der gesetzlichen Rente, ohne die hier verglichenen Produkte. Bestimmt den Grenzsteuersatz in der Auszahlungsphase."),
("rente_br", "gesetzliche Bruttorente p.a. (heutige Werte)", 24000, "EUR", "Nur fuer den Beitragsbemessungsgrenzen-Deckel in der KV: Rente und Versorgungsbezuege teilen sich EINE Obergrenze ( 223 Abs. 3 SGB V)."),
- ("teilkap", "Altersvorsorgedepot: Teilkapital zu Beginn", 0.0, "", "Bis 30 % sind zu Beginn der Auszahlungsphase als Einmalbetrag moeglich."),
+ ("teilkap", "Altersvorsorgedepot: Teilkapital zu Beginn", 0.0, "", "Bis 30 % sind zu Beginn der Auszahlungsphase als Einmalbetrag moeglich. Gilt in beiden Auszahlungsformen."),
+ ("av_auszform","Altersvorsorgedepot: 1 = Auszahlungsplan, 2 = lebenslange Rente", 1, "1/2", "Unabhaengig von der bAV-Auszahlungsform: wer die bAV als Kapital nimmt, kann das Depot trotzdem verrenten. Plan: Entnahme bleibt investiert, Besteuerung der nicht gefoerderten Schicht mit dem halben Unterschiedsbetrag. Rente: Kapital geht an den Versicherer, nicht gefoerderte Schicht nur mit dem Ertragsanteil ( 22 Nr. 5 S. 2 Buchst. a EStG)."),
+ ("rentfak_av", "Rentenfaktor Altersvorsorgedepot (EUR je 10.000 EUR)", 26, "EUR", "Nur bei Auszahlungsform 2. Voreinstellung wie bei der bAV; ein Depotvertrag kann guenstiger sein als ein Versicherungsmantel - Wert aus dem Angebot eintragen."),
+ ("rentdyn_av", "Dynamik der Altersvorsorgedepot-Rente p.a.", 0.01, "", "Ueberschussbeteiligung bzw. Fondsentwicklung in der Rentenphase des Depotvertrags."),
]
r = 4
@@ -746,6 +751,9 @@ Z_COLS = [
("sl_a", "Sleeve\nStand", 11, EUR),
("sl_e", "Sleeve\nEntnahme", 11, EUR),
("sl_st", "Sleeve\nSteuer", 11, EUR),
+ ("ngef_ek", "davon Kapital\n(nicht gef.)", 12, EUR),
+ ("ngef_er", "davon Rente\n(nicht gef.)", 12, EUR),
+ ("av_rest", "MEMO AV-Rente\nnach dem Horizont", 14, EUR),
("av_stpfl", "steuerpfl.\nAV-Entnahme", 12, EUR),
("zve_av", "zvE Option\nAV-Depot", 11, EUR),
("arg_av", "Tarifarg.\nAV", 11, EUR),
@@ -881,19 +889,36 @@ for t in range(NZ):
g("pr_a", f"={KP}")
g("pr_ba", f"={KPB}")
else:
- g("gef_a", f"=MAX(0,({Cp['gef_a']}-{Cp['gef_e']})*(1+{P['rnet_av']}))")
- g("ngef_a", f"=MAX(0,({Cp['ngef_a']}-{Cp['ngef_e']})*(1+{P['rnet_av']}))")
+ # Bei Verrentung geht das Restkapital an den Versicherer; ein Depotkonto
+ # gibt es danach nicht mehr.
+ g("gef_a", f"=IF({IN['av_auszform']}=2,0,MAX(0,({Cp['gef_a']}-{Cp['gef_e']})*(1+{P['rnet_av']})))")
+ g("ngef_a", f"=IF({IN['av_auszform']}=2,0,MAX(0,({Cp['ngef_a']}-{Cp['ngef_e']})*(1+{P['rnet_av']})))")
g("sl_a", f"=MAX(0,({Cp['sl_a']}-{Cp['sl_e']})*(1+{P['rnet_priv']}))")
g("pr_a", f"=MAX(0,({Cp['pr_a']}-{Cp['pr_e']})*(1+{P['rnet_priv']}))")
g("pr_ba", f"=MAX(0,{Cp['pr_ba']}-{Cp['pr_ba']}*IF({Cp['pr_a']}=0,0,{Cp['pr_e']}/{Cp['pr_a']}))")
tk = f"IF({t}=0,{IN['teilkap']},0)"
- g("gef_e", f"=IF({C['plan']}=0,0,MIN({C['gef_a']},{KG}*{tk}+({KG}*(1-{IN['teilkap']}))*{ANN_AV}))")
- g("ngef_e", f"=IF({C['plan']}=0,0,MIN({C['ngef_a']},{KN}*{tk}+({KN}*(1-{IN['teilkap']}))*{ANN_AV}))")
+ # Verrentetes Restkapital je Schicht und insgesamt
+ KGR = f"({KG}*(1-{IN['teilkap']}))"
+ KNR = f"({KN}*(1-{IN['teilkap']}))"
+ KRR = f"({KGR}+{KNR})"
+ # Laufende Depotrente; die Aufteilung auf die Schichten folgt ihrem Anteil
+ # am verrenteten Kapital, genau wie bei der bAV.
+ rente_av = f"{KRR}*{IN['rentfak_av']}/10000*12*(1+{IN['rentdyn_av']})^{t}*{C['plan']}"
+ g("av_rest", f"=IF(AND({IN['av_auszform']}=2,{C['plan']}=0),"
+ f"{KRR}*{IN['rentfak_av']}/10000*12*(1+{IN['rentdyn_av']})^{t},0)")
+ g("gef_e", f"=IF({IN['av_auszform']}=1,"
+ f"IF({C['plan']}=0,0,MIN({C['gef_a']},{KG}*{tk}+{KGR}*{ANN_AV})),"
+ f"{KG}*{tk}+({rente_av})*IF({KRR}=0,0,{KGR}/{KRR}))")
+ g("ngef_ek", f"=IF({IN['av_auszform']}=1,"
+ f"IF({C['plan']}=0,0,MIN({C['ngef_a']},{KN}*{tk}+{KNR}*{ANN_AV})),"
+ f"{KN}*{tk})")
+ g("ngef_er", f"=IF({IN['av_auszform']}=1,0,({rente_av})*IF({KRR}=0,0,{KNR}/{KRR}))")
+ g("ngef_e", f"={C['ngef_ek']}+{C['ngef_er']}")
g("sl_e", f"=IF({C['plan']}=0,0,MIN({C['sl_a']},{KS}*{ANN_PR}))")
g("sl_st", f"=MAX(0,{C['sl_e']}*(1-IF({KS}=0,1,MIN(1,{KSB}/{KS})))*(1-{P['tfs']}))*{P['kapst_eff']}")
- g("av_stpfl", f"={C['gef_e']}+{C['ngef_e']}*(1-IF({KN}=0,1,MIN(1,"
- f"Ansparphase!SUMPRODUCT_PLACEHOLDER)))*{P['halb']}")
+ g("av_stpfl", f"={C['gef_e']}+{C['ngef_ek']}*(1-IF({KN}=0,1,MIN(1,"
+ f"Ansparphase!SUMPRODUCT_PLACEHOLDER)))*{P['halb']}+{C['ngef_er']}*{P['ertragsq']}")
g("zve_av", f"=MAX(0,{C['sonst']}+{C['av_stpfl']})")
g("arg_av", arg_formula(C['zve_av'], C['lam']))
g("est_av", est_formula(C['arg_av'], C['lam']))
@@ -924,7 +949,8 @@ zw['CG8'].number_format = EUR
for t in range(NZ):
r = Z0 + t
zw.cell(r, ZI['av_stpfl']).value = (
- f"={ZC['gef_e']}{r}+{ZC['ngef_e']}{r}*(1-IF({KN}=0,1,MIN(1,$CG$7/{KN})))*{P['halb']}")
+ f"={ZC['gef_e']}{r}+{ZC['ngef_ek']}{r}*(1-IF({KN}=0,1,MIN(1,$CG$7/{KN})))*{P['halb']}"
+ f"+{ZC['ngef_er']}{r}*{P['ertragsq']}")
# =====================================================================
# 5) CASHFLOW / IRR
@@ -1112,6 +1138,9 @@ diag = [
("MEMO: bAV-Rentenzahlungen nach dem Vergleichshorizont",
f"=SUM(Auszahlung!${ZC['bav_rest']}${Z0}:${ZC['bav_rest']}${ZLAST})", EUR,
"Nur bei Auszahlungsform 2. Eine lebenslange Rente zahlt ueber den Horizont hinaus - diesen Betrag beruecksichtigt der Vergleich NICHT. Wer alt wird, gewinnt hier."),
+ ("MEMO: Altersvorsorgedepot-Rente nach dem Vergleichshorizont",
+ f"=SUM(Auszahlung!${ZC['av_rest']}${Z0}:${ZC['av_rest']}${ZLAST})", EUR,
+ "Dasselbe fuer das Altersvorsorgedepot, wenn es verrentet wird. Beide Memo-Zeilen sind der Preis eines festen Vergleichshorizonts: eine lebenslange Rente wird darin systematisch unterschaetzt."),
]
r = 22
for lab, fml, fmt, note in diag: