diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-07 18:29:02 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-22 19:18:04 +0200 |
| commit | 03ed88c55eace54b88ec71cbfb1da483dbccbf60 (patch) | |
| tree | 349392acc28536fbdc00c2b798518dfef8f2408d | |
| parent | 513454a4a3a7af12c2c15a19738d8e46fd991220 (diff) | |
Altersvorsorgedepot mit Verrentung
| -rw-r--r-- | README.md | 32 | ||||
| -rw-r--r-- | app/README.md | 48 | ||||
| -rw-r--r-- | app/index.html | 95 | ||||
| -rw-r--r-- | app/service-worker.js | 2 | ||||
| -rw-r--r-- | bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx | bin | 151973 -> 118375 bytes | |||
| -rw-r--r-- | build_bav.py | 47 |
6 files changed, 178 insertions, 46 deletions
@@ -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 Binary files differindex b7e62bf..a04ea94 100644 --- a/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx +++ b/bAV-DYNO_vs_Altersvorsorgedepot_vs_ETF.xlsx 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: |
