summaryrefslogtreecommitdiff
path: root/app/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'app/index.html')
-rw-r--r--app/index.html213
1 files changed, 164 insertions, 49 deletions
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(){
+ '<i>gegeben</i> 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 '<span><i style="background:'+o.raw+'"></i>'+o.name+'</span>';
- }).join('') + '<span style="margin-left:auto">y: Kapital, nominal (Tausend €)</span>';
- $('#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('') + '<span style="margin-left:auto">y: Vermögen, nominal (Tausend €)</span>';
+
+ 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];