From ede587dfd9bf583a5274d6aeae7061d5fd4f805b Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Fri, 7 Aug 2026 13:46:52 +0200 Subject: Bessere Kapitalverlaufskurve --- app/README.md | 97 ++++++++++++++++++++++- app/index.html | 213 ++++++++++++++++++++++++++++++++++++++------------ app/service-worker.js | 2 +- 3 files changed, 258 insertions(+), 54 deletions(-) (limited to 'app') diff --git a/app/README.md b/app/README.md index dac29cf..c0437c0 100644 --- a/app/README.md +++ b/app/README.md @@ -188,7 +188,96 @@ auf den Segment-Umschaltern), und `prefers-reduced-motion` schaltet Übergänge Der Rechenweg baut 98 Tabellenzeilen. Solange kein Abschnitt aufgeklappt ist, wird der Neuaufbau jetzt bis 300 ms nach der letzten Eingabe verschoben und beim Aufklappen nachgeholt. -## 7. Eine Lesefalle in der Sensitivitätskurve +## 7. Zwei Zinssätze und der Kapitalverlauf + +In der Auszahlungsphase arbeiten **zwei verschiedene Zinssätze**, und das ist kein Versehen: + +| Geld … | verzinst sich mit | versteuert | +|---|---|---| +| … im Produkt (AV-Depot) | `rnet_av` = 6,50 % | erst bei Entnahme | +| … 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: + +| | Einmalbetrag | Rest | +|---|---|---| +| bAV | 0 % oder 100 % (`auszform`) | — | +| Altersvorsorgedepot | 0 – 30 % (`teilkap`) | bleibt im Produkt | + +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 +Basisfall: + +| Einmalbetrag | steuerpflichtig Jahr 0 | Grenzbelastung | Monatsleistung | +|---|---|---|---| +| 0 % | 17.292 € | 27,2 % | 850,83 € | +| 15 % | 42.543 € | 29,7 % | 840,09 € | +| 30 % | 67.793 € | 32,3 % | 823,37 € | + +Zwei Kontrollen dazu: das Restkapital am Horizont ist über alle geprüften Renditen, Horizonte +und Einmalbeträge **exakt 0,00 €** — der Annuitätenfaktor passt zur Rekursion, es wird nichts +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. + +### Der Kapitalverlauf + +**Eine** Größe über die ganze Zeitachse: das **Vermögen, ohne Konsumannahme**. + +| | | +|---|---| +| Ansparphase | Kontostand im Produkt | +| Auszahlungsphase | Kontostand im Produkt **+** was ausgezahlt und netto wieder angelegt wurde | + +``` +Wiederanlage(0) = 0, Wiederanlage(t+1) = (Wiederanlage(t) + netto(t))·(1+w) +Vermögen(t) = Produktkonto(t) + Wiederanlage(t) +``` + +Drei Eigenschaften, alle numerisch geprüft: + +| | | +|---|---| +| `Vermögen(0) = Produktkapital` | 0,00 € — die Wiederanlage ist am Rentenbeginn null, die Kurve läuft **knickfrei** durch | +| `Vermögen(n) = V(0)·(1+w)ⁿ` | 8,9 · 10⁻¹⁶ relativ — der Endwert ist der Endwert aller Netto-Leistungen, also **dieselbe Rangfolge wie die Kennzahl oben** | +| Pfade **nicht** proportional | Verhältnis AV-Depot zu Privatdepot wandert von 1,3152 auf 1,1296 | + +Die dritte ist der eigentliche Gewinn. Geld **im** Produkt wächst mit der Produktrendite vor +Steuern, ausgezahltes nur noch mit dem Satz danach — und genau dieser Unterschied war die +Ausgangsfrage. Jeder sichtbare Abfall ist eine tatsächlich gezahlte Abgabe; bei der bAV mit +Kapitalauszahlung 144.791 € auf einen Schlag, weil die gesamte Leistung in einem einzigen Jahr +besteuert wird. + +Zwei Einschränkungen stehen in der Diagrammnotiz: der Produktanteil enthält unterwegs noch +latente Steuer (streng vergleichbar sind Anfang und Ende), und eine lebenslange Rente hat kein +Konto — dort geht das Kapital an den Versicherer, die Kurve fällt steil, obwohl nichts verloren +ist. Dieser Punkt ist im Diagramm eigens beschriftet („Kapital → Rentenanspruch“). + +### Vier verworfene Fassungen + +Der Weg dorthin lohnt die Notiz, weil jede Zwischenstufe an einem anderen Denkfehler scheiterte: + +1. **Nur das Produktkonto.** Die bAV fällt bei Kapitalwahl auf null und schweigt über den Rest. + Verschweigt, dass die Kennzahl sehr wohl eine Wiederanlage unterstellt. +2. **Die prospektive Reserve `V(t)`.** Mathematisch tadellos und exakt die Größe hinter der + Monatsleistung — aber nach einer Kapitalauszahlung **negativ** (−54.555 € ab Alter 68), weil + dann nur noch Lasten ausstehen. Wahr, wichtig, und als Kurve unlesbar. +3. **Der Entsparpfad** `G(t+1) = G(t)(1+w) − M`. Läuft für alle drei sauber auf null, aber die + Pfade sind dann zwangsläufig **proportional** (`G_x/G_y = M_x/M_y`, geprüft auf 4 · 10⁻¹⁴): + wer den Verbrauch fixiert, hat das Vermögen determiniert. Die Kurve trägt dann keine + Information über die Headline-Zahl hinaus. +4. **Der beitragsfreie Wert** in der Ansparphase, um den Einheitenwechsel am Rentenbeginn zu + vermeiden. Stetig und sauber, aber es blieb bei einer Nettogröße, die für das Privatdepot + eine Bewegung suggeriert, wo keine ist. + +Die jetzige Fassung braucht keine Konsumannahme, wechselt nirgends die Einheit und ist als +einzige nicht proportional. + +## 8. Eine Lesefalle in der Sensitivitätskurve Reglern wie *Jahresbrutto* oder *Monatsbeitrag* verändern nicht nur die Förderarchitektur, sondern auch den **Nettoaufwand** — und damit den Betrag, der in allen drei Optionen investiert @@ -199,7 +288,7 @@ KV/PV-Beiträge mehr, oberhalb von 101.400 € auch keine RV/AV-Beiträge. Der N springt dort um bis zu 15 %, die investierte Summe mit ihm. Die App blendet einen Hinweis mit der tatsächlichen Spanne ein, sobald der Effekt 2 % übersteigt. -## 8. Zwei Befunde, die beim Portieren aufgefallen sind +## 9. Zwei Befunde, die beim Portieren aufgefallen sind **Die Vorabpauschale ist im Basisfall ein Vorteil für das Privatdepot.** Über 35 Jahre fallen 121.774 € Vorabpauschale an, davon werden aber nur 11.942 € tatsächlich versteuert — der Rest @@ -222,7 +311,7 @@ Die Sensitivitätskurve zeigt die Sättigung als waagerechten Ast. --- -## 9. Das Aussehen: Windows 3.1 / NT 3.5 +## 10. Das Aussehen: Windows 3.1 / NT 3.5 Silber `#C0C0C0` auf Petrol `#008080`, Navy-Titelleisten, harte Ein-Pixel-Kanten, alles aus der 16-Farben-VGA-Palette. **Es gibt nur ein Farbschema** — `prefers-color-scheme` ist entfernt, @@ -268,7 +357,7 @@ Grau ist unter Windows die Farbe des *Deaktivierten*. Lesbarer Text bleibt desha schwarz und wird über Größe und Kursivstellung abgestuft, nicht über Aufhellung. Das ist zugleich authentisch und der bessere Kontrast: Schwarz auf `#C0C0C0` sind 9,9 : 1. -## 10. Bewusst nicht gebaut +## 11. Bewusst nicht gebaut - **Onboarding-Assistent.** Elf Kernfelder tragen keinen Wizard; er verstellte nur den Blick auf die Kopplung der Größen. diff --git a/app/index.html b/app/index.html index 22b14ee..4573e52 100644 --- a/app/index.html +++ b/app/index.html @@ -675,6 +675,62 @@ function rechne(input, param, flags) { var ertrag = { bav: bw.bav - bwAufw, av: bw.av - bwAufw, priv: bw.priv - bwAufw }; var ANNW = D.wiederanl === 0 ? 1 / D.n_ausz : D.wiederanl / (1 - pow(1 + D.wiederanl, -D.n_ausz)); var mnom = ANNW / 12, mfak = mnom * Z[0].df; + + /* Prospektive Reserve: Barwert der noch AUSSTEHENDEN Netto-Leistungen, + bezogen auf den jeweiligen Zeitpunkt, mit dem Wiederanlagezins nach + Steuern. Rekursiv V(t) = cf(t) + V(t+1)/(1+w), also exakt und ohne + Summationsfehler; V(0) ist definitionsgemäß dieselbe Größe, aus der + die äquivalente Monatsleistung annuitisiert wird. + + Das ist die Größe, die der Kapitalverlauf in der Auszahlungsphase + braucht: das Produktkonto der bAV ist nach einer Kapitalauszahlung + null, der Wert der Option aber nicht — er ist NEGATIV, solange noch + KV/PV auf den Versorgungsbezug und der Verlust an gesetzlicher Rente + ausstehen. */ + function reserve(key){ + var V = new Array(N_AUSZ + 1); V[N_AUSZ] = 0; + for (var t = N_AUSZ - 1; t >= 0; t--) V[t] = Z[t][key] + V[t+1] / (1 + D.wiederanl); + return V; + } + var res = { bav: reserve('bav_net'), av: reserve('av_net'), priv: reserve('pr_net') }; + + /* Vermögen ohne Konsum: was noch im Produkt liegt PLUS was ausgezahlt und + netto wieder angelegt wurde. + + Wiederanlage(0) = 0, Wiederanlage(t+1) = (Wiederanlage(t) + netto(t))·(1+w) + Vermögen(t) = Produktkonto(t) + Wiederanlage(t) + + Das ist die einzige Darstellung, die NICHT proportional verläuft: Geld im + Produkt wächst mit der Produktrendite vor Steuern, ausgezahltes nur noch + mit dem Satz danach. Genau dieser Unterschied ist die Frage. + + Am Rentenbeginn ist die Wiederanlage null, also geht die Ansparkurve + (Produktkonto) knickfrei über. Am Horizont ist das Produkt leer, also + ist der Endwert für jede Option der Endwert aller Netto-Leistungen — + dieselbe Rangfolge wie die Kennzahl oben. + + Ehrlich dazugesagt: unterwegs enthält der Produktanteil noch latente + Steuer, der Wiederanlageanteil nicht. Streng vergleichbar sind deshalb + der Anfangs- und der Endpunkt, nicht jeder Zwischenwert. Und wo es gar + kein Konto gibt — bAV nach Kapitalauszahlung, bAV als lebenslange Rente — + steht dort null; eine Rente ist ein Anspruch, kein Vermögen. */ + function vermoegen(key, produkt){ + var v = [], acc = 0; + for (var t = 0; t <= N_AUSZ; t++){ + v.push(produkt(t) + acc); + if (t < N_AUSZ) acc = (acc + Z[t][key]) * (1 + D.wiederanl); + } + return v; + } + var verm = { + /* t = 0 ist der Beginn des ersten Auszahlungsjahres — da liegt das Kapital + in beiden Auszahlungsformen noch vor. Erst im Lauf dieses Jahres wird es + entweder ausgezahlt oder gegen einen Rentenanspruch eingetauscht. Damit + gilt Vermögen(0) = Produktkapital und die Kurve läuft knickfrei durch. */ + bav: vermoegen('bav_net', function(t){ return t === 0 ? KB : 0; }), + av: vermoegen('av_net', function(t){ return t < N_AUSZ ? Z[t].gef_a + Z[t].ngef_a + Z[t].sl_a : 0; }), + priv: vermoegen('pr_net', function(t){ return t < N_AUSZ ? Z[t].pr_a : 0; }) + }; return { I: I, P: P, D: D, A: A, Z: Z, cf: cf, beitragMtl: BEITRAG, bisektion: bis, nettoaufwandMtl: A[0].netto_a / 12, @@ -685,7 +741,7 @@ function rechne(input, param, flags) { kvpvAusz: { bav: ss('bav_kvpv'), av: 0, priv: 0 }, rentenverlust: { bav: ss('mind_br'), av: 0, priv: 0 }, nettoAusz: { bav: ss('bav_net'), av: ss('av_net'), priv: ss('pr_net') }, - bwAuszahlung: bw, bwAufwand: bwAufw, ertrag: ertrag, + bwAuszahlung: bw, bwAufwand: bwAufw, ertrag: ertrag, reserve: res, vermoegen: verm, irr: { bav: irrOf(cf.bav), av: irrOf(cf.av), priv: irrOf(cf.priv) }, monat: { bav: sp('bav_net')*mfak, av: sp('av_net')*mfak, priv: sp('pr_net')*mfak }, monatBrutto: { bav: sp('bav_br')*mfak, av: (sp('gef_e')+sp('ngef_e')+sp('sl_e'))*mfak, priv: sp('pr_e')*mfak }, @@ -881,7 +937,23 @@ var FIELDS = [ { k:'kv_ruhe', src:'I', g:'kern', kind:'seg', label:'Krankenversicherung im Ruhestand', opts:[[1,'gesetzlich'],[2,'privat']], hint:'Der entscheidende Hebel. In der GKV sind Betriebsrenten Versorgungsbezüge und tragen den vollen Beitragssatz (§§ 229, 250 SGB V); private Altersvorsorge nicht. Allein diese Einstellung dreht das Ergebnis.' }, { k:'auszform', src:'I', g:'kern', kind:'seg', label:'bAV-Auszahlung', opts:[[1,'Kapital'],[2,'lebenslange Rente']], - hint:'Kapital: im Zuflussjahr voll steuerpflichtig, Beiträge zur KV auf 120 Monate verteilt (§ 229 Abs. 1 S. 3 SGB V). Die Rente streckt den Bezug auf die Restlebenszeit und schirmt deshalb ein Vielfaches an Kapital unter demselben Freibetrag ab.' }, + hint:'Kapital: im Zuflussjahr voll steuerpflichtig, Beiträge zur KV auf 120 Monate verteilt (§ 229 Abs. 1 S. 3 SGB V). Die Rente streckt den Bezug auf die Restlebenszeit und schirmt deshalb ein Vielfaches an Kapital unter demselben Freibetrag ab. Das Modell kennt nur ganz oder gar nicht; eine Teilkapitalisierung der bAV ist nicht abgebildet.' }, + { k:'teilkap', src:'I', g:'kern', kind:'pct', label:'Altersvorsorgedepot: Einmalbetrag zu Beginn', min:0, max:0.3, step:0.05, + /* Dieselbe Entscheidung wie eine Zeile darüber, nur für das andere Produkt — + deshalb steht sie hier und nicht im Abschnitt „Ruhestand“. */ + liveNote:function(R){ + if (!(S.I.teilkap > 0)) return null; + var M = METRICS[S.metric]; + var ohne = AVModell.rechne(Object.assign({}, S.I, { teilkap: 0 }), S.P); + var d = M.get(R,'av') - M.get(ohne,'av'); + var z = R.Z[0], gr = z.av_stpfl > 0 ? (z.stg_av - z.stg_b) / z.av_stpfl : 0; + return 'Kostet ' + (d >= 0 ? 'nichts, im Gegenteil: ' + M.dfmt(d) + : M.dfmt(-d).replace(/^[+−]/,'') + ' ' + M.unit) + + ' gegenüber reiner Verrentung. Grund: der Einmalbetrag ist im Zuflussjahr voll ' + + '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 €', @@ -936,8 +1008,7 @@ var FIELDS = [ hint:'Vor allem der steuerpflichtige Teil der gesetzlichen Rente, ohne die hier verglichenen Produkte. Bestimmt den Grenzsteuersatz in der Auszahlungsphase — und damit die Tarifarbitrage t₀ − t₁.' }, { k:'rente_br', src:'I', g:'ruhe', kind:'eur', label:'gesetzliche Bruttorente p.a.', min:0, max:120000, step:1000, hint:'Nur für den Deckel in der Krankenversicherung: gesetzliche Rente und Versorgungsbezüge teilen sich EINE Beitragsbemessungsgrenze (§ 223 Abs. 3 SGB V).' }, - { k:'teilkap', src:'I', g:'ruhe', kind:'pct', label:'AV-Depot: Teilkapital zu Beginn', min:0, max:0.3, step:0.05, - hint:'Bis 30 % sind zu Beginn der Auszahlungsphase als Einmalbetrag möglich.' }, + /* ---------------- Rechengrößen 2026 ---------------- */ { k:'bbg_rv', src:'P', g:'p_sv', kind:'eur', label:'BBG Rentenversicherung (jährlich)', min:50000, max:200000, step:100, hint:'SVBezGrV 2026 (BGBl. 2025 I Nr. 116), bundeseinheitlich.' }, @@ -1764,58 +1835,100 @@ function drawWaterfall(){ + 'gegeben die darüberliegenden. Tippen Sie einen Balken an für die Herleitung.'; } -/* ---------- 3) Kapitalverlauf ---------- */ +/* ---------- 3) Kapitalverlauf ---------- * + Eine Größe über die ganze Zeitachse: das VERMÖGEN, ohne Konsumannahme. + + Ansparphase Kontostand im Produkt + Auszahlung Kontostand im Produkt + was ausgezahlt und netto + wieder angelegt wurde + + Am Rentenbeginn ist die Wiederanlage null — die Kurve läuft knickfrei + durch. Kein Einheitenwechsel, kein Sprung, dem nichts entspricht: beim + Privatdepot passiert dort tatsächlich nichts. + + Was man jetzt sieht und vorher nicht sah: Geld im Produkt wächst mit der + Produktrendite vor Steuern, ausgezahltes nur noch mit dem Satz danach. + Deshalb verlaufen die Kurven NICHT proportional — im Basisfall wandert + das Verhältnis AV-Depot zu Privatdepot von 1,32 auf 1,13. Jeder sichtbare + Abfall ist eine tatsächlich gezahlte Abgabe. */ function drawKapital(){ - var cv = $('#chart'), P = prep(cv, 270), g = P.g, w = P.w, h = P.h; - var pad = { l:58, r:12, t:12, b:30 }; - var a0 = S.I.alter, series = { bav:[], av:[], priv:[] }; + var cv = $('#chart'), P = prep(cv, 285), g = P.g, w = P.w, h = P.h; + var pad = { l:64, r:14, t:22, b:30 }; + var a0 = S.I.alter, rb = S.I.rentbeg, pe = Math.max(S.I.planende, rb); + var nA = Math.min(R.D.n_ausz, R.Z.length); // bis eine Zahlung nach dem Horizont + var xhi = rb + nA; + + var kurve = {}; + OPTS.forEach(function(o){ kurve[o.k] = []; }); R.A.forEach(function(a){ - if (a.alter > S.I.rentbeg) return; - series.bav.push([a.alter, a.k_bav_g + a.k_bav_n]); - series.av.push([a.alter, a.k_gef + a.k_ngef + a.sl_w]); - series.priv.push([a.alter, a.pr_w]); + if (a.alter > rb) return; + kurve.bav.push([a.alter, a.k_bav_g + a.k_bav_n]); + kurve.av.push([a.alter, a.k_gef + a.k_ngef + a.sl_w]); + kurve.priv.push([a.alter, a.pr_w]); }); - R.Z.forEach(function(z){ - if (z.alter > S.I.planende) return; - series.av.push([z.alter, z.gef_a + z.ngef_a + z.sl_a]); - series.priv.push([z.alter, z.pr_a]); + OPTS.forEach(function(o){ + for (var t = 1; t <= nA; t++) kurve[o.k].push([rb + t, R.vermoegen[o.k][t]]); }); - /* Die bAV führt in der Auszahlungsphase kein Depot mehr: bei Kapitalauszahlung - fließt alles im ersten Jahr ab, bei Verrentung liegt das Kapital beim - Versicherer und ist kein Vermögen des Versicherten mehr. Deshalb endet die - bAV-Kurve am Rentenbeginn — mit einem Abfall auf null im Kapitalfall. */ - if (S.I.auszform === 1) series.bav.push([S.I.rentbeg + 0.001, 0]); - - var all = [].concat(series.bav, series.av, series.priv).map(function(p){return p[1];}).filter(isFinite); - if (!all.length) all = [0]; - var hi = Math.max.apply(null, all) * 1.06, lo = 0; - if (!(hi > 0)) hi = 1000; // alles null: leere, aber gültige Achse - var xhi = Math.max(S.I.planende, a0 + 1); // entarteter Zeitraum - var xr = [a0, xhi], yr = [lo, hi]; + + var alle = [].concat(kurve.bav, kurve.av, kurve.priv) + .map(function(p){ return p[1]; }).filter(isFinite); + if (!alle.length) alle = [0, 1000]; + var hi = Math.max.apply(null, alle); + hi = hi > 0 ? hi * 1.10 : 1000; + var xr = [a0, Math.max(xhi, a0 + 1)], yr = [0, hi]; var X = function(v){ return pad.l + (v-xr[0])/(xr[1]-xr[0])*(w-pad.l-pad.r); }; var Y = function(v){ return pad.t + (1-(v-yr[0])/(yr[1]-yr[0]))*(h-pad.t-pad.b); }; - axes(g,w,h,pad,xr,yr,function(v){return num(v/1000,0)+'k';}, function(v){return num(v,0);}, - niceTicks(a0, xhi, 6).filter(function(v){return v>=a0&&v<=xhi;})); + axes(g,w,h,pad,xr,yr, function(v){ return num(v/1000,0)+'k'; }, function(v){ return num(v,0); }, + niceTicks(a0, xr[1], 6).filter(function(v){ return v>=a0 && v<=xr[1]; })); - g.strokeStyle = css('--ink'); g.setLineDash([3,3]); - g.beginPath(); g.moveTo(X(S.I.rentbeg), pad.t); g.lineTo(X(S.I.rentbeg), h-pad.b); g.stroke(); g.setLineDash([]); - g.fillStyle = css('--ink'); g.font=UIFONT; g.textAlign='left'; g.textBaseline='top'; - g.fillText('Rentenbeginn', X(S.I.rentbeg)+4, pad.t+2); + g.strokeStyle = css('--ink'); g.setLineDash([3,3]); g.lineWidth = 1; + g.beginPath(); g.moveTo(X(rb), pad.t); g.lineTo(X(rb), h-pad.b); g.stroke(); g.setLineDash([]); + g.fillStyle = css('--ink'); g.font = UIFONT; g.textAlign='right'; g.textBaseline='top'; + g.fillText('Rentenbeginn', X(rb)-5, pad.t+2); OPTS.forEach(function(o){ - g.strokeStyle=o.raw; g.lineWidth=2; g.lineJoin='miter'; g.beginPath(); - series[o.k].forEach(function(p,i){ var x=X(p[0]), y=Y(p[1]); if(i) g.lineTo(x,y); else g.moveTo(x,y); }); + var pts = kurve[o.k]; + if (pts.length < 2) return; + g.strokeStyle = o.raw; g.lineWidth = 2; g.lineJoin='miter'; g.beginPath(); + pts.forEach(function(p,i){ var x=X(p[0]), y=Y(p[1]); if(i) g.lineTo(x,y); else g.moveTo(x,y); }); g.stroke(); }); + + /* Bei lebenslanger Rente stürzt die bAV-Kurve ab, weil das Kapital an den + Versicherer geht. Das ist kein Verlust, sondern ein Tausch — ohne + Beschriftung liest es aber jeder als Verlust. */ + 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.strokeStyle = OPTS[0].raw; g.lineWidth = 1; + g.strokeRect ? g.strokeRect(X(rb)+5, (yA+yB)/2-7, 118, 14) : 0; + g.fillStyle = OPTS[0].raw; g.font='bold '+UIFONT; + g.textAlign='left'; g.textBaseline='middle'; + g.fillText('Kapital \u2192 Rentenanspruch', X(rb)+8, (yA+yB)/2); + } + $('#legend').innerHTML = OPTS.map(function(o){ return ''+o.name+''; - }).join('') + 'y: Kapital, nominal (Tausend €)'; - $('#readout').textContent = 'Bei Rentenbeginn: bAV ' + eur(R.kapital.bav) + ' · AV-Depot ' + eur(R.kapital.av) - + ' · privat ' + eur(R.kapital.priv) + '.'; - $('#chartnote').textContent = 'Nominal, vor Steuern. Beim Privatdepot ist die Vorabpauschale bereits jährlich ' - + 'abgezogen. Die bAV-Kurve endet am Rentenbeginn: danach führt sie kein Depot mehr — bei Kapitalauszahlung ' - + 'fließt alles im ersten Jahr ab, bei Verrentung liegt das Kapital beim Versicherer. Dass die bAV hier führt ' - + 'und trotzdem verlieren kann, ist der ganze Punkt: entschieden wird erst in der Auszahlung.'; + }).join('') + 'y: Vermögen, nominal (Tausend €)'; + + var e = { bav: R.vermoegen.bav[nA], av: R.vermoegen.av[nA], priv: R.vermoegen.priv[nA] }; + $('#readout').textContent = 'Am Rentenbeginn: ' + eur(R.kapital.bav) + ' / ' + eur(R.kapital.av) + + ' / ' + eur(R.kapital.priv) + '. Am Horizont: ' + eur(e.bav) + ' / ' + eur(e.av) + + ' / ' + eur(e.priv) + ' — dieselbe Rangfolge wie die Kennzahl oben.'; + + $('#chartnote').textContent = 'Vermögen ohne Konsumannahme: was noch im Produkt liegt, plus alles, ' + + 'was ausgezahlt und netto wieder angelegt wurde (' + pct(R.diagnose.wiederanl,2) + ' nach ' + + 'Steuern). Am Rentenbeginn ist die Wiederanlage null, deshalb läuft die Kurve knickfrei durch — ' + + 'beim Privatdepot passiert dort ja auch nichts. Jeder Abfall ist eine tatsächlich gezahlte ' + + 'Abgabe; bei der bAV mit Kapitalauszahlung ' + eur(R.kapital.bav - R.vermoegen.bav[1]) + + ' auf einen Schlag, weil die gesamte Leistung in einem einzigen Jahr besteuert wird. ' + + 'Die Kurven verlaufen bewusst nicht proportional: Geld im Produkt wächst mit der ' + + 'Produktrendite vor Steuern, ausgezahltes nur noch mit dem Satz danach. ' + + 'Zwei Einschränkungen: der Produktanteil enthält unterwegs noch latente Steuer, streng ' + + 'vergleichbar sind daher Anfang und Ende. Und eine lebenslange Rente hat kein Konto — das ' + + 'Kapital geht an den Versicherer, die Kurve fällt dort steil, obwohl nichts verloren ist. ' + + 'Was die Rente nach dem Vergleichshorizont noch zahlt, fehlt in dieser Darstellung ebenfalls; ' + + 'der Betrag steht als Memo unter „Diagnose“.'; } /* ---------- 4) Monatszerlegung ---------- */ @@ -1887,16 +2000,18 @@ function buildTabs(){ b.onclick = function(){ S.tab=T.k; hoverX=null; buildTabs(); drawChart(); saveState(); }; host.appendChild(b); }); - if (S.tab==='wf'){ - var sw = el('div','seg btnseg'); sw.style.marginLeft='auto'; - [['bav','bAV'],['av','AV-Depot']].forEach(function(o){ + var umschalter = function(werte, aktuell, setzen){ + var sw = el('div','seg btnseg'); + werte.forEach(function(o){ var b = el('button', null, o[1]); b.type='button'; - b.setAttribute('aria-pressed', S.wfZiel===o[0]); - b.onclick = function(){ S.wfZiel=o[0]; buildTabs(); drawChart(); saveState(); }; + b.setAttribute('aria-pressed', aktuell===o[0]); + b.onclick = function(){ setzen(o[0]); buildTabs(); drawChart(); saveState(); }; sw.appendChild(b); }); host.appendChild(sw); - } + }; + if (S.tab==='wf') + umschalter([['bav','bAV'],['av','AV-Depot']], S.wfZiel, function(v){ S.wfZiel=v; }); } function drawChart(){ var T = TABS.filter(function(x){return x.k===S.tab;})[0] || TABS[0]; diff --git a/app/service-worker.js b/app/service-worker.js index 103ea88..f4958f1 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-v4'; +const VERSION = 'av-v8'; const ASSETS = [ './', './index.html', -- cgit v1.2.1