Technical Notes#
Status: Draft, 2026-08-29 (access date for every citation: 2026-08-29).
Retrieval conditions. These notes were drafted with nothing retrieved — egress from the
build environment was blocked and the session’s WebSearch budget was exhausted before this product
was reached — and their citations have since been re-verified against the primary documents: the
statutes are read as canonical XML with their Stand recorded, the carrier wordings and
Produktinformationsblätter as PDFs. Where an entry in sources.md says Retrieved: yes the
document was opened and the passage the entry rests on was read; where it says Retrieved: no —
thirteen of the forty entries — the citation is still a pointer, not a certificate, naming the
instrument a claim must be checked against rather than a document anyone read, and the number it
carries keeps its unverified or std tag. See product-spec.md for the full statement.
Scope note. These notes specify a reference liability cash-flow projection model — model name
Basis_DE_S, monthly grid over an annual contract — for the standardized composite German Basisrente defined in
product-spec.md (same directory). This is not any single insurer’s product. [S#]/[R#] tags refer to
the source list in sources.md (numbering carried from _research/basisrente.md; frozen); [REG-R#]
tags refer to the cross-product reference library
references/regulatory-and-actuarial-references.md (its own frozen numbering). std marks a
standardization introduced for the reference implementation; unverified a claim no search result
confirmed. Parameter values are identical to those in product-spec.md. Cells names, model-point
columns and CSV headers are English lower_snake_case; German terms of art keep their German form in
prose, where they are the name of the thing.
Model scope and conventions#
Purpose. Project gross best-estimate liability cash flows, undiscounted — laufende Beiträge and Zuzahlungen in; death benefits, annuity payments, survivor benefits, insurer expenses and commission out — for a single model point on an expected (probability-weighted) basis, with the state variables that make the product what it is: the Deckungskapital of the premium-paying and premium-free cohorts, and the annuity secured at Rentenbeginn.
Out of scope, and said so. No discounting; no Deckungsrückstellung, Zinszusatzreserve REG-R17, Solvency II technical provision, risk margin or SCR — all cited, none computed. No tax: the Sonderausgabenabzug R2 R7 and the Besteuerungsanteil R4 REG-R41 shape the product’s economics, belong to
product-spec.md, and are not cash flows of the contract. No BUZ cash flows: a BUZ written inside the contract is represented only by its premium share, its disability mechanics belonging toBU_DE_S(delib product 9). No Versorgungsausgleich, no provider transfer, no Wiederinkraftsetzung, no unit-linked or hybrid asset form.The absences are the product. There is no surrender-value cells, no
claims_lapsecolumn, nocv_pp, noloan_pp, no commutation and no lump sum anywhere in this model, because the entitlement is nicht kapitalisierbar, nicht veräußerbar and nicht beleihbar R1 R14 REG-R39 REG-R40 — structural absences, not switched-off options, andcheck_no_capital()asserts them in code rather than in prose.Projection grid: monthly, over a contract that is annual. The model runs on two clocks and the argument of a cells says which.
tcounts projection months from the valuation date and the frame is 0-based:t = 0is the first projected month and the frame runst = 0 … proj_len() − 1withproj_len() = 12 × proj_len_y().k = proj_year(t) = t // 12counts projection years. Policy duration at the start of projection yearkisduration_y(k) = duration_init + kcompleted policy years, so the policy year — the contractual, 1-based label — ispolicy_year(t) = duration(t) + 1; attained age isage_y(k) = entry_age + duration_init + kand steps on the anniversary; calendar year iscal_year_y(k) = conclusion_year + duration_init + kand steps with it.duration_mth(t) = 12 duration_init + tis the completed policy months andis_anniv(t)—t % 12 == 11— the last month of a projection year. A new-business model point hasduration_init = 0, sok = 0is its first policy year; an in-force point opens at whatever duration it has already run, and the frame still starts att = 0.Which clock, and why the contract keeps the annual one. The in force, the decrements, the claims, the expenses, the commission and the Rente instalments take a month. Everything the contract states per Versicherungsjahr takes a year: the Beitragsdynamik step, the Zuzahlung, the four account charges, the annual declaration of Überschussbeteiligung, the Deckungskapital and its interest credit, the Beitragsfreistellung effective at the end of the current premium period R14, and the conversion at Rentenbeginn. The decrements carry the library’s two speeds —
mort_rate(t)is the annual rate of the year andmort_rate_mth(t) = 1 − (1 − mort_rate(t))^(1/12)is what the recursion applies — so twelve months compound back to the annual rate exactly,pols_if(12k)is the annual-step model’spols_if(k)to the last bit, and the whole Aufschubphase is unchanged.What the monthly grid buys, and it is the Rente. The annuity is paid monthly R1 and the Rentenfaktor is quoted in euro a month; the annual-step model these notes were first written for compressed the payment into twelve instalments booked at the start of the payout year on the opening in-force count, which was its pitfall 12 and a stated std convention generous to the year of death by up to a full year’s annuity.
ann_mth_pp(t) = ann_pp(k) / 12is now paid in advance to whoever is alive at the start of each month, which takes 4 290,52 € — 1,6 % — off the anchor’s annuity outgo. The Rentengarantiezeit becomes12mguaranteed instalments beginning in the month after the death that triggered them (+116,29 € on model point 4, +562,91 € on model point 12), and a policy that dies in the third month of a year bears three twelfths of that year’s maintenance expense rather than all of it (−35,45 € on the anchor).Projection horizon. The annuity is lifelong, so the projection runs to the end of the mortality table:
proj_len_y() = omega_age() − age(0) + 1, whereomega_age()is the last age inmort_table.csv, whereqx = 1.0. The terminal age is absorbing:mort_rate(t) = 1.0whereverage(t) ≥ omega_age(), whatevermort_be_factorsays, andmort_rate_mthputs that certainty in the terminal year’s last month, sopols_if(proj_len()) = 0exactly and the decrement closure identity holds to the last euro. Without that rule the generational trend would carry the table’s own terminal rate below 1 and leave a residue in force after the end of the table.omega_age = 121std, the terminal age German annuity tables are conventionally carried to. For the anchor cellproj_len_y() = 121 − 45 + 1 = 77andproj_len() = 924.proj_len()is the number of projected periods, the exclusive end of the frame:len(result_cf()) == proj_len()andresult_cf().index[-1] == proj_len() − 1. This is the library-wide 0-based reading, and the conventions suite asserts it. Where the frame starts is a product fact and is not asserted; contiguity is.Timing conventions std. The laufender Beitrag and the Zuzahlung are taken in the first month of the projection year (annual in advance; a fractionated mode changes the amount through the Ratenzahlungszuschlag, not the grid — which is why the monthly frame did not split them), and the charges on them are struck at the same moment. Interest is credited at the end of the year; deaths fall at the end of each month, so a dying policy carries the whole of its last year’s interest and has paid the whole of that year’s premium; the Beitragsfreistellung transition falls after the death decrement of the year’s last month, § 165 VVG taking effect for the end of the current Versicherungsperiode R14 and § 12 Abs. 1 VVG making that period the Versicherungsjahr. Annuity instalments are booked monthly in advance on the count in force at the start of the month. Acquisition expense and initial commission fall at inception; a twelfth of the annual maintenance expense or annuity administration falls in each month, and the renewal commission in the month the contribution it is a percentage of does.
Charges are not cash flows. The Zillmerung amortisation, the premium charge β, the reserve charge γ and the Stückkosten are deductions from the policyholder’s Deckungskapital, hence insurer income; the insurer’s own outgo is the acquisition expense, the commission, the maintenance expense and the annuity administration. A charge affects
net_cfonly through the benefit it shrinks; booking one as both is pitfall 4.Two cohorts, one model point. A Beitragsfreistellung does not remove a policy; it moves it to the premium-free cohort, where its Deckungskapital is still credited and still converts at Rentenbeginn R14. The two cohorts’ account values diverge from the first freeze, so the model carries the paying cohort per policy and the premium-free cohort at fund level — exact, because every paying policy in one model point is identical.
Currency, sign and rounding. EUR throughout.
net_cf(t)is income-positive (premiums +, benefits and expenses −), with the outgo-positive orientation published asliability_cf(t) = −net_cf(t). Intermediate values at full precision; displayed cash flows to euro cents andpols_ifto six decimals std. Totals are summed at full precision and then rounded.Age basis. Age last birthday at conclusion (Eintrittsalter), stepping on the policy anniversary std: no German convention was established, and here mortality drives the annuity’s duration rather than any benefit amount, so a half-year offset is second order.
mort_rateis generational and depends on the calendar year as well as the age R17 REG-R49, which is whycal_year_y(k)is carried. Both the age and the calendar year step on the anniversary, so the twelve months of a projection year share one annual death rate andmort_rate_mthis its geometric twelfth.
External input files#
Inputs are external CSVs in the model folder’s parent, read once per model by unparameterized
reader cells in the Data Space (the annuallife/TradLife_A layout). Every file but
model_point_table.csv carries a final provenance column, one tag per row — delib’s second
ruling, asserted by the conventions suite.
File |
Index columns |
Value columns |
What it is |
|---|---|---|---|
|
|
the 25 attributes below |
The policies. Exempt from the provenance rule: a model point is a configuration, not an assumption |
|
|
|
The std DAV 2004 R-shaped first-order proxy: |
|
|
|
The declared laufende Verzinsung in the Aufschubphase and the Überschussrente uplift in the Rentenphase, by scenario and projection year |
|
|
|
The insurer’s aktueller Rentenfaktor at each conversion age, by scenario |
|
|
|
One row per tariff: the charge scale and the insurer’s own expense and commission scale |
|
|
|
The two behavioural assumptions that vary by policy duration: the Beitragsfreistellung rate and the Zuzahlung take-up. |
|
|
|
One multiplicative factor per contractual option: |
Scalar assumptions that are single numbers rather than tables are Projection References and are
tagged in Assumption inputs below: mort_be_factor, elig_surv_prob, mort_base_year,
zill_spread_y, rf_unit, ann_freq, roll_fwd_tol.
Model point attributes#
Attribute |
Type |
Meaning |
Exercised by |
|---|---|---|---|
|
int |
Index into |
all |
|
str |
The carrier’s own reference, carried for reporting |
all |
|
enum {M, F} |
Reporting only. Must not enter pricing: unisex is mandatory for contracts concluded from 21 December 2012 REG-R34 |
1–13 |
|
int |
Age last birthday at conclusion (Eintrittsalter) |
all |
|
int |
Calendar year the contract was concluded. Fixes the age-floor cohort (60 before 2012, 62 after) R1 R8, the guarantee vintage REG-R15 and the generational mortality cohort R17 |
6, 7, 8 (pre-2012 / pre-2015 / pre-2010) |
|
int |
Completed policy years at the valuation date; 0 = new business |
6, 7, 8 |
|
int |
Attained age at Rentenbeginn |
6 (60, the pre-2012 floor), 12 (70) |
|
float |
Policies the model point represents |
all |
|
enum {regular, single} |
laufender Beitrag against Einmalbeitrag |
5 (single) |
|
EUR p.a. |
The contractual laufender Beitrag at inception, before the Ratenzahlungszuschlag and before any Dynamik; for |
all but 7, 8 |
|
enum {annual, half_yearly, quarterly, monthly} |
Payment frequency; keys |
1, 5, 7, 8, 9, 13 annual · 4 half-yearly · 3, 11 quarterly · 2, 6, 10, 12 monthly |
|
rate p.a. |
Beitragsdynamik, the contractual annual escalation |
1, 2, 3, 4, 6, 9, 11, 12 |
|
EUR p.a. |
The nominal annual Zuzahlung before take-up |
1, 3, 11 |
|
int |
Policy duration at which the Zuzahlung stops: it is paid while |
1, 3, 11 |
|
bool |
The model point is already beitragsfrei at the valuation date |
7 |
|
EUR |
Deckungskapital per policy at the valuation date |
6, 7 |
|
EUR p.a. |
Annual annuity already in payment, for a point that opens in the Rentenphase |
8 |
|
rate p.a. |
The contract’s Rechnungszins, the cohort’s guarantee vintage REG-R15 |
6 (2,25 %), 7 (1,75 %), 8 (2,75 %), rest 1,00 % |
|
EUR per month per 10 000 € |
Garantierter Rentenfaktor, fixed at inception R17 [S1] |
6, 13 (both set above the current-factor scenario at their conversion age, so the guarantee binds) |
|
int |
Rentengarantiezeit in years from Rentenbeginn; 0 = none |
4 (10), 12 (20) |
|
rate |
Survivor’s annuity as a fraction of the main annuity; 0 = rider off, which is the base design |
3 (0.60), 12 (0.60) |
|
rate |
Share of the total contribution attributable to a BUZ. Must satisfy |
11 (0.49, the boundary) |
|
str |
Key into |
all |
|
str |
Key into |
all |
|
str |
Key into |
all |
|
str |
Key into |
13 ( |
There is no surr_rate, no lapse_rate and no kapitalwahl column, and their absence is a
statutory fact rather than a modelling simplification R1 R14 REG-R39. lapse_rate is the name a
modeller reusing the endowment or Schicht-3 chassis reaches for first; the decrement it names does not
exist here, and bf_rate — which is not a lapse — takes its place.
An in-force paid-up point is represented wholly, not partly. A model point opens either entirely
premium-paying (paidup_at_init = 0) or entirely premium-free (paidup_at_init = 1, the whole of
pols_if_init opening in the premium-free cohort with
av_pu_at(0, "BEF_PREM") = av_pp_init × pols_if_init). A part-paid-up book is two model points,
which is the honest arrangement: averaging the two cohorts’ reserves is pitfall 3.
The shipped model point table#
# |
What it is for |
Key settings |
|---|---|---|
1 |
Anchor — the worked example. A self-employed buyer at the product’s typical entry age |
45 → 67, 2026, 6 000 € annual + 4 000 € Zuzahlung, 2 % Dynamik, no riders |
2 |
Monthly premium, long deferment |
35 → 67, 3 000 € p.a. monthly, 3 % Dynamik, no Zuzahlung |
3 |
Quarterly premium with the survivor’s annuity switched on |
48 → 67, quarterly, |
4 |
Half-yearly premium with a 10-year Rentengarantiezeit |
52 → 67, half-yearly, |
5 |
Einmalbeitrag — the late-career deferral of a high-income year |
58 → 67, |
6 |
In-force, pre-2012 cohort, at the 60 age floor |
concluded 2009, |
7 |
In-force and already beitragsfrei |
concluded 2014, |
8 |
In-force and already in payment — opens in the Rentenphase, |
concluded 2006, |
9 |
Boundary — the whole Höchstbetrag |
50 → 67, 30 826 € p.a. annual R2 R20 — 124 800 € × 24,7 %, rounded up under § 10 Abs. 3 Satz 1 EStG |
10 |
Boundary — a Kleinbetragsrente this model annuitises rather than commutes |
30 → 67, 300 € p.a. monthly (25 €/month) |
11 |
Boundary — the 50 % rule |
42 → 67, quarterly, |
12 |
Deferral to 70 with both options on |
55 → 70, monthly, |
13 |
Boundary — the guaranteed Rentenfaktor binds |
46 → 67, |
Between them the thirteen exercise both premium forms, all four payment frequencies, all three in-force shapes (accumulating, paid-up, in payment), both option modules separately and together, both age-floor cohorts, four guarantee vintages and four boundary cases.
State variables#
Variable |
Description |
Updated |
|---|---|---|
|
Number of projected years, |
once per model point |
|
Number of projected months, |
once per model point |
|
The projection year in which Rentenbeginn falls, |
once per model point |
|
Attained age, completed policy years and calendar year in projection year |
annual, stepping on the anniversary |
|
Policies in force at the start of month |
monthly recursion |
|
The premium-paying subset at the start of month |
monthly recursion |
|
The premium-free subset, |
monthly |
|
End-of-period state: |
within month |
|
Expected deaths in month |
monthly |
|
Policies going premium-free at the end of month |
annual, in the year’s last month |
|
Rentengarantiezeit continuations running at the start of month |
monthly recursion |
|
Deckungskapital per premium-paying policy: |
within year |
|
|
annual |
|
Deckungskapital of the premium-free cohort, at fund level, same three timings |
within year |
|
The whole Deckungskapital at fund level, |
within year |
|
|
annual |
|
The rate credited to the Deckungskapital, |
annual |
|
Annual annuity per surviving annuitant in year |
annual recursion |
|
The monthly instalment actually paid, |
monthly |
|
The contract’s Beitragssumme at inception, the base of the 25 ‰ Zillmerung cap |
once per model point |
There is no account value in the Rentenphase: the whole fund converts at ret_y() into an
annuity obligation, and the reserve that stands behind that obligation is a
Deckungsrückstellung, which delib cites and does not compute.
Assumption inputs#
Three classes are distinguished and every entry is tagged. Class (a) is contractual, statutory or tariff-fixed and is cited; class (b) is the insurer’s current discretionary scale, revisable annually; class (c) is the modeller’s view of experience.
(a) Contractual / guaranteed elements (cited)#
Input |
Value |
Basis |
|---|---|---|
Annuity form |
Monthly, lifelong, on the taxpayer’s own life; no lump sum of any kind at any date |
|
Earliest Rentenbeginn |
Completion of the 62nd year for contracts concluded after 31 December 2011; the 60th for earlier ones |
R1 R8 REG-R39 — § 10 Abs. 1 Nr. 2 Buchst. b aa and § 10 Abs. 6 EStG, both read |
Surrender value |
None at any duration. § 169 VVG inoperative; no Stornoabzug |
|
Beitragsfreistellung |
Exercisable at any time for the end of the current premium period; converts to a premium-free entitlement to a reduced annuity |
|
Death benefit, base design |
Nothing before Rentenbeginn; the annuity simply ends after it |
|
Death benefit, rider on |
The Deckungskapital, payable only where an eligible survivor exists, and applied as the single premium of a survivor’s annuity |
|
Permitted survivors |
Spouse or registered partner; children while Kindergeld or the Kinderfreibetrag runs |
|
Rentengarantiezeit |
Remaining instalments to the end of the guaranteed period, only to an eligible survivor, never commutable |
|
BUZ premium share |
Supplementary covers strictly below 50 % of the total contribution |
not R1 — the statute requires only that the cover be ergänzend. The 50 % test is BMF-Schreiben v. 24.05.2017 Rz. 38, measured on the actual total premium payable R18, and is a contract term in the GDV BUZ model conditions § 9 Abs. 2 [S12] |
Conversion rule |
|
R17 [S1] |
Rechnungszins ( |
The cohort’s Höchstrechnungszins: 1,00 % from 1 January 2025; 0,25 % 2022–2024; 0,90 % 2017–2021; 1,25 % 2015–2016; 1,75 % 2012–2014; 2,25 % 2007–2011; 2,75 % 2004–2006. Fixed at conclusion for the whole term |
|
Höchstzillmersatz ( |
25 ‰ of the Beitragssumme, from 1 January 2015 (40 ‰ before) |
|
Überschussbeteiligung entitlement |
Statutory, on the same terms as any German life contract; MindZV floor 90 % / 90 % / 50 % |
|
Mortality basis |
DAV 2004 R, generational; first order for pricing and the guaranteed Rentenfaktor, second order for the best estimate. Not public, not redistributed |
|
Unisex |
Mandatory from 21 December 2012; |
(b) Insurer-discretionary current elements (snapshot; revisable annually)#
Input |
Base-run value |
Basis |
|---|---|---|
Declared laufende Verzinsung |
2,60 % p.a. for |
std (i) |
Credited rate |
|
std (ii) |
Schlussüberschussanteil |
4,0 % of the fund, allocated only at Rentenbeginn |
|
Überschussrente |
1,0 % p.a., compounding — a teildynamische Rente |
std (iv) |
Aktueller Rentenfaktor |
31,50 € at age 67 in scenario |
std (v) |
Verwaltungskosten β on premium |
7,5 % of each laufender Beitrag and Zuzahlung |
std; band 5 %–10 %, product-spec |
Verwaltungskosten γ on the reserve |
0,35 % p.a. of the Deckungskapital |
std; band 0,2 %–0,6 % |
Stückkosten |
36,00 € per policy p.a., inflating at |
|
Acquisition charge on a Zuzahlung |
2,5 % of each Zuzahlung, charged in the year it is paid |
std; gap 8 |
Ratenzahlungszuschlag |
1,000 annual · 1,020 half-yearly · 1,030 quarterly · 1,050 monthly, on the laufender Beitrag only |
std; market convention |
Option cost on the Rentenfaktor |
|
std (vi) |
(i) No declared rate specific to a Basisrente was established anywhere in the delib corpus, and
the market-average rates in sibling delib files are Schicht-3 and endowment figures that must not be
relabelled. The path is a scenario, not a forecast: it starts above the 1,00 % Höchstrechnungszins
by a plausible surplus margin and grades down, so the guarantee does not bind on the base run.
(ii) German declared rates are quoted as the total credited rate including the Rechnungszins, so
cred_rate is a max, not a sum. Adding the declared rate on top of the guarantee is pitfall 6.
(iii) The single-date allocation is a contract fact R15 — no surrender means no early-exit
trigger — while the 4,0 % level is std with nothing behind it.
(iv) A volldynamische Rente would consume the whole first-order margin released in the payout phase
and a konstante Rente none; 1,0 % is deliberately in between, and it decides how much of the
conversion-basis wedge (see Key sensitivities) is given back.
(v) One guaranteed Rentenfaktor level now exists in the corpus and no range or time series does
(gap 4). NÜRNBERGER’s Muster-PIB gives a guaranteed 24,94 € per month per 10 000 € at 67 on a
2025 fund-linked contract [S13]; the shipped guaranteed factors are 28,00 € at the anchor and 34,00 €
at model point 13, i.e. above it, and were not re-calibrated in this pass — see model.md. The
base current-factor scenario is set above the guaranteed factor so max(gtd, curr) is visibly
operative, and the low scenario below it so model point 13 exercises the other branch.
(vi) Anchored on the sibling corpus’s Schicht-3 illustration — a 10-year Rentengarantiezeit at about
0,5 % of the annuity, 20 years at 2,6 %, 30 years at 8,0 % — unverified and explicitly not
transferable to Schicht 1. The survivor factor has no anchor at all.
(c) Behavioural / experience assumptions (the modeller’s view)#
Every input in this class is std and none of it has a source. No German insurer publishes a Beitragsfreistellung rate, a Zuzahlung take-up, an eligible-survivor probability or a best-estimate factor for a Basisrente, and the research file records the absence as gap 3.
Input |
Base-run value |
Rationale |
|---|---|---|
Mortality best-estimate factor |
0.85 of the shipped first-order table |
The first-order table carries the DAV’s prudential margins R17 REG-R47; 0.85 is a round std step to a best estimate and is the single largest unanchored number in the payout phase |
Mortality improvement |
1,5 % p.a. at every age, applied from |
Keeps the table generational, which is what a replacement must preserve REG-R49. A flat trend across ages is a simplification; DAV 2004 R’s own trends are age-dependent |
Beitragsfreistellung rate |
4,0 % at durations 1–5, 3,0 % at 6–10, 2,0 % at 11+ |
Higher than a Schicht-3 lapse rate early — the buyer’s income is volatile by construction and going premium-free is free of penalty and reversible — and lower late, because there is no realisable value to tempt anyone out. That shape is argued in |
Zuzahlung take-up |
0.70 at durations 1–5, 0.85 at 6–15, 0.90 at 16+ |
The Zuzahlung is paid out of a profit not known until the year end, so it is behavioural, not contractual. Rising with duration because the contract and the habit bed in |
Eligible-survivor probability |
0.55 |
The probability that a spouse or registered partner, or a Kindergeld-eligible child, exists at the moment of death R1. On a contract taken at 45 and running to 67 the child channel has usually closed, so this is in substance a marriage-survival probability. One of the most consequential std numbers in the whole delib library |
Acquisition expense |
250,00 € at inception |
Round-number placeholder |
Initial commission |
2,5 % of |
Sized to the Zillmerung cap R16, the German design in which what the insurer pays out is what it may write into the reserve. A carrier’s published figure now matches it exactly: NÜRNBERGER’s Muster-PIB puts Abschluss- und Vertriebskosten at “2,50 % der vereinbarten Beiträge”, 900,00 € on a 36 000 € Beitragssumme [S13] — that is the cost, not the commission, but on the German design they are the same lever. [S2]’s 1 575 € specimen Abschlussprovision stays unverified |
Renewal commission |
1,5 % of premiums plus Zuzahlungen from year 2 |
Market convention; no level established |
Maintenance expense |
60,00 € per in-force policy p.a., inflating |
Placeholder |
Annuity administration |
36,00 € per annuitant p.a., inflating |
Placeholder; the payout phase is administratively cheaper than the accumulation phase |
Expense inflation |
1,5 % p.a. |
Placeholder |
Zillmerung spread |
5 years |
Cited, not std — gap 8 is closed. The AltZertG’s own five-year rule (§ 1 Abs. 1 Satz 1 Nr. 8) sits in the Altersvorsorgevertrag definition and § 5a does not import it R10; but VVG § 165 Abs. 2 computes the premium-free benefit on the Rückkaufswert of § 169 Abs. 3, whose floor is the reserve “bei gleichmäßiger Verteilung der angesetzten Abschluss- und Vertriebskosten auf die ersten fünf Vertragsjahre” R14 — and a Beitragsfreistellung is the only exit this product has. Two retrieved wordings state it directly: GDV model conditions § 10 Abs. 1 (with the shorter premium term as the alternative) and CosmosDirekt LA 1079 A § 8 Abs. 1 [S12] [S1] |
No decrement other than death and the Beitragsfreistellung transition exists in this model. No surrender, no assignment, no provider transfer, no commutation. That is the product R1 REG-R39 REG-R40, and pitfall 1 is what happens when a modeller carries one across by habit.
Cash flow components and recursions#
Notation (defined once, used throughout)#
Symbol |
Cells |
Meaning |
|---|---|---|
|
— |
projection month, 0-based: |
|
|
projection year, |
|
|
attained age, completed policy years, calendar year in year |
|
|
the projection year in which Rentenbeginn falls; |
|
|
in force, premium-paying and premium-free at the start of month |
|
|
best-estimate annual death rate of the year month |
|
|
its geometric twelfth, |
|
|
the first-order table rate at age |
|
|
Beitragsfreistellung rate, annual, applied after the death decrement of the year’s last month |
|
|
Rentengarantiezeit continuations running at the start of month |
|
|
Deckungskapital: fund level, per paying policy, and premium-free block at fund level |
|
|
the laufender Beitrag charged and the Zuzahlung paid, per paying policy, per year |
|
|
base premium at inception, Beitragsdynamik, Ratenzahlungszuschlag |
|
|
the Beitragssumme at inception |
|
|
the four charges struck against the account, all annual |
|
|
premium credited to the account after all four charges |
|
|
credited rate, |
|
|
Schlussüberschussanteil rate at Rentenbeginn |
|
|
the fund converted at Rentenbeginn, including the terminal bonus |
|
|
|
|
|
annual annuity per surviving annuitant in year |
|
|
the monthly instalment actually paid, in advance, in month |
|
|
the Überschussrente uplift applied at the end of payout year |
|
|
probability an eligible survivor exists at the moment of death |
|
|
the survivor’s annuity as a fraction of the main annuity; |
|
|
Rentengarantiezeit in years from Rentenbeginn |
|
|
insurer expense and commission outgo, fund level, per month |
q, f, i, b are per-annum rates and q_mth is the only monthly one; A, P, Z, F, a,
E, C are EUR; R is euro of monthly annuity per 10 000 € of capital.
The Deckungskapital recursion#
The account is annual and takes k: one contribution, one set of charges and one interest credit
per Versicherungsjahr, so it has nothing to say about a month. Per premium-paying policy, in the
Aufschubphase (k < T):
Aᵖ(k, "BEF_PREM") = av_pp(k)
Aᵖ(k, "AFT_PREM") = Aᵖ(k, "BEF_PREM") + N(k)
Aᵖ(k, "AFT_INT") = Aᵖ(k, "AFT_PREM") × ( 1 + i(k) − γ )
av_pp(k + 1) = Aᵖ(k, "AFT_INT")
The premium-free block is carried at fund level, because a policy that froze at duration 5 and one that froze at duration 15 hold different reserves and only the aggregate is meaningful:
Aᶠ(k, "BEF_PREM") = av_pu_at(k, "BEF_PREM")
Aᶠ(k, "AFT_PREM") = Aᶠ(k, "BEF_PREM") − u(k) × lᶠ(12k)
Aᶠ(k, "AFT_INT") = Aᶠ(k, "AFT_PREM") × ( 1 + i(k) − γ )
Aᶠ(k + 1) = Aᶠ(k, "AFT_INT") × ( 1 − q(12k) ) + pols_freeze(12k + 11) × Aᵖ(k, "AFT_INT")
The freeze and the year’s interest credit fall at the same instant — the end of the
Versicherungsjahr — which is why the transfer term reads the last month of year k.
A premium-free policy keeps paying the Stückkosten and the reserve charge and stops paying β and α, which is the whole economic content of Beitragsfreistellung. The fund level closes:
A(k, timing) = Aᵖ(k, timing) × lᵖ(12k) + Aᶠ(k, timing)
A(k + 1) = A(k, "AFT_INT") × ( 1 − q(12k) )
and that last line — at the year’s annual death rate, which is exactly what its twelve monthly
rates compound to — is check_av_roll_fwd(). It holds whether or not the survivor rider is on,
because the reserve of a policy terminated by death leaves the fund either way: as a claim where an
eligible survivor exists, as a mortality profit where none does. That single identity is the
arithmetic content of nicht vererblich R1.
For k > T the Deckungskapital is zero: the fund has become an annuity obligation.
Decrements and the two policy ledgers#
The ledgers are monthly and the rate they apply is the geometric twelfth. Death first, in every month; then the Beitragsfreistellung on the survivors of the year’s last month std, because § 165 VVG takes effect for the end of the current Versicherungsperiode R14 and § 12 Abs. 1 VVG makes that period the Versicherungsjahr:
q_mth(t) = 1 − ( 1 − q(t) )^(1/12)
pols_death_paying(t) = lᵖ(t) × q_mth(t)
pols_death_paidup(t) = lᶠ(t) × q_mth(t)
pols_death(t) = pols_death_paying(t) + pols_death_paidup(t)
pols_freeze(t) = lᵖ(t) × ( 1 − q_mth(t) ) × f(t) if is_anniv(t) and t < 12T, else 0
lᵖ(t + 1) = lᵖ(t) × ( 1 − q_mth(t) ) − pols_freeze(t)
lᶠ(t + 1) = lᶠ(t) × ( 1 − q_mth(t) ) + pols_freeze(t)
l(t + 1) = l(t) × ( 1 − q_mth(t) )
Twelve geometric twelfths compound back to the annual rate exactly, so l(12k) is the annual-step
model’s l(k) to the last bit and lᵖ(12(k+1)) = lᵖ(12k) × (1 − q(12k)) × (1 − f(12k)) still holds
across the year. The arithmetic twelfth q(t)/12 would not close: it leaves a residue that grows
with the rate and is largest exactly where this product’s cash flows are, in the tail of a lifelong
annuity.
The last line is the one to stare at: f(t) does not appear in it. A Beitragsfreistellung is a
transfer between the two ledgers, not an exit R14, and check_pols_roll_fwd() asserts both that
lᵖ + lᶠ = l and that l decrements on mortality alone. The closure identity is therefore simply
Σ_{t=0..12n−1} pols_death(t) + l(12n) = pols_if_init with l(12n) = 0
because mort_rate_at_age(omega_age, ·) = 1 and the certainty falls in the terminal year’s last
month.
The conversion at Rentenbeginn#
The conversion happens at the start of projection year T — the end of month 12T − 1 — on the
fund carried out of year T − 1, and it is the model’s only single-date event:
fund_at_conv() = av_at(T, "BEF_PREM") × ( 1 + σ )
rentenfaktor_applied() = max( rentenfaktor_gtd , rf_curr(ret_age) ) × rf_option_factor()
rf_option_factor() = factor("guarantee_period", G) × factor("survivor", s)
ann_pp(T) = fund_at_conv() / l(12T) / rf_unit × rentenfaktor_applied() × ann_freq
ann_mth_pp(t) = ann_pp(k) / ann_freq the instalment actually paid
with rf_unit = 10 000 and ann_freq = 12. ann_pp(T) is the cohort-average annual annuity per
annuitant, which is exact at fund level even though the paying and premium-free cohorts arrive with
different per-policy reserves; it is an annual figure and nobody’s payment, because the
Rentenfaktor is quoted in euro a month and ann_mth_pp is what is paid.
check_conversion() inverts the identity:
check_conversion_resid(T) = ann_pp(T) × l(12T) × rf_unit / ( rentenfaktor_applied() × ann_freq )
− fund_at_conv()
and is zero at every other k. For a model point that opens in payment (T < 0) the conversion never
occurs inside the projection, ann_pp(0) = ann_pp_init, and the check is vacuously true.
The conversion basis is not the projection basis, and that is deliberate. rentenfaktor_gtd was
struck at inception on first-order mortality with a prudential margin and a conservative interest
basis R17 [S1]; the projection runs on the second-order best estimate. The wedge between them is
the Risikoüberschuss of the payout phase, and ann_bonus_rate is the mechanism that gives it back
to the annuitant. A model that converted on its own best-estimate mortality would abolish the wedge
and, with it, the whole German payout-phase surplus mechanic (pitfall 11).
The annuity in payment, and the Rentengarantiezeit ledger#
ann_pp(k) = ann_pp_init if k = 0 and T < 0
= fund_at_conv() / l(12T) / rf_unit × R × ann_freq if k = T ≥ 0
= ann_pp(k − 1) × ( 1 + b(k − 1) ) if k > max(0, T)
= 0 otherwise
ann_mth_pp(t) = ann_pp(proj_year(t)) / ann_freq for t ≥ max(0, 12T), else 0
The annuity is struck once and uplifted once a year, so the twelve instalments of a payout year are
equal and the thirteenth is (1 + b) times the twelfth; check_annuity_roll_fwd() asserts both the
compounding and 12 × ann_mth_pp(t) = ann_pp(k) across each year’s months.
The Rentengarantiezeit runs G years from Rentenbeginn, so every continuation ends on the same
date — on this grid the 12G-th instalment, gtd_end_t() = max(0, 12T) + 12G − 1 — which
makes the ledger a one-line monthly recursion:
g(t + 1) = 0 if t + 1 > gtd_end_t()
= g(t) + pols_death(t) × π if t ≥ max(0, 12T) and G > 0
= 0 otherwise
A continuation therefore begins in the month after the death that triggered it rather than in the following year, which is the one place the finer grid puts money on the liability.
π is what makes this a Schicht-1 guarantee rather than a Schicht-3 one: the instalments continue
only to an eligible survivor R1, and where none exists the payments simply cease. They are also
never commutable — g(t) is a stream, never a discounted lump sum (pitfall 14).
Benefits and cash flows#
Every line here is monthly; the per-policy amounts they read are annual, k = proj_year(t).
db_pp(k) = Aᵖ(k, "AFT_INT") per dying paying policy
db_pu_pp(k) = Aᶠ(k, "AFT_INT") / lᶠ(12k) per dying premium-free policy
claims(t, "DEATH") = 1{s > 0} × 1{t < 12T} × π
× [ db_pp(k) × pols_death_paying(t) + db_pu_pp(k) × pols_death_paidup(t) ]
claims(t, "ANNUITY") = ann_mth_pp(t) × l(t) for t ≥ max(0, 12T), else 0
claims(t, "SURVIVOR")= ann_mth_pp(t) × g(t) the Rentengarantiezeit stream
expenses(t) = acq_expense_pp × pols_if_init × 1{t = 0 and duration_init = 0}
+ maint_expense_pp / 12 × (1 + expense_infl)^k × l(t) for t < 12T
+ annuity_admin_pp / 12 × (1 + expense_infl)^k × ( l(t) + g(t) ) for t ≥ 12T
commissions(t) = comm_init_rate × S × pols_if_init × 1{t = 0 and duration_init = 0}
+ comm_renew_rate × ( premiums(t) + zuzahlungen(t) ) for t ≥ 1
net_cf(t) = premiums(t) + zuzahlungen(t)
− claims(t, "DEATH") − claims(t, "ANNUITY") − claims(t, "SURVIVOR")
− expenses(t) − commissions(t)
liability_cf(t) = − net_cf(t)
The amount released by a death is the annual db_pp(k), struck at the end of the
Versicherungsjahr where the account is struck, so the month decides when the reserve is released
and not how much, and the year’s total is the annual-step model’s to the last bit. The expense
inflation factor steps on the anniversary, so the twelve months of a year carry the same monthly
amount and a policy that dies in the third month bears three twelfths of it. The renewal commission
is a percentage of a contribution and so falls in the month the contribution does; t ≥ 1 excludes
the first year’s premium at t = 0 and admits every later one at t = 12, 24, …, which is the
annual-step model’s rule unchanged.
The death benefit is booked as a single amount and is not a lump sum to a beneficiary. R1
requires everything paid to a survivor to be paid as an annuity; what the model books at the
moment of death is the Deckungskapital leaving this contract as the single premium of a survivor’s
annuity, which is itself a new liability — an immediate annuity, delib product 7 — that this model
does not project. That is stated here rather than left to be inferred, because a reader who takes
claims_death for a payable lump sum has misread the product (pitfall 10).
Where the rider is on, the survivor’s annuity fraction s reduces the Rentenfaktor through
rf_option_factor() rather than scaling the death benefit: the cover is paid for out of the annuity,
which is how a German tariff prices it.
The published frame#
result_cf() returns a DataFrame indexed by the projection month t
(df.index.name == "t"), contiguous from t = 0 to proj_len() − 1, with these columns in this
order:
# |
Column |
Content |
|---|---|---|
1 |
|
policies in force at the start of the month — the weight on this row |
2 |
|
the premium-paying subset; the weight on the two premium columns |
3 |
|
laufende Beiträge, in the first month of a projection year and no other |
4 |
|
Zuzahlungen, kept separate because they are a distinct premium form on a distinct charge basis |
5 |
|
death benefits in the Aufschubphase; structurally 0 where |
6 |
|
monthly annuity instalments in the Rentenphase |
7 |
|
Rentengarantiezeit continuations; structurally 0 where |
8 |
|
acquisition, and a twelfth of the annual maintenance or annuity administration |
9 |
|
Abschluss- and Bestandsprovision |
10 |
|
income-positive |
11 |
|
|
Columns 3 and 4 enter net_cf positively and 5 to 9 negatively; columns 1 and 2 are counts,
published because a reader cannot follow the projection without them, and named in
check_net_cf()’s docstring as excluded from the identity.
The Deckungskapital is not a column of this frame. It is a balance on the annual clock — one
contribution, one set of charges and one interest credit per Versicherungsjahr — so it lives in
result_pols(), the annual state, beside the two ledgers, the rates and the per-policy amounts that
move with it. Putting a balance in a monthly cash flow statement invites exactly the summation that
is a category error.
Two further frames travel with it. result_cf_annual() sums the cash flow columns over the twelve
months of each projection year and takes the two counts at the year’s start, which is the view these
notes print and the one directly comparable to the annual-step model — its t is this model’s k.
result_pols() is the annual state: the ledgers, mort_rate beside mort_rate_mth, bf_rate,
cred_rate, the per-policy contribution and charges, both account balances, and ann_pp beside the
ann_mth_pp it is paid in. Neither is a second projection: both are regroupings of cells the monthly
frame has already evaluated.
The published checks#
The residual’s argument follows its cells’ clock. Three of the six are statements about payments and take a month; three are statements about the Deckungskapital, the conversion and the Überschussrente, which move once a Versicherungsjahr, and take a projection year. Calling one with the other’s index is a category error rather than a rounding question.
Check |
Clock |
The identity it closes |
|---|---|---|
|
month |
|
|
month |
|
|
month |
The nicht kapitalisierbar invariant: no payment to the policyholder at any |
|
year |
|
|
year |
The whole fund converts exactly once, at |
|
year |
|
Each returns a bool over the whole projection and has a per-period residual companion
check_*_resid, compared against roll_fwd_tol = 1e-9.
Processing order#
The order is stated per Versicherungsjahr, because that is the order the contract happens in;
the months sit inside it. For k = 0 … n − 1, and within each year for t = 12k … 12k + 11, in
exactly this order:
Open the year. Compute
age_y(k),duration_y(k),cal_year_y(k); determine the phase fromk < ret_y()(Aufschubphase) ork ≥ ret_y()(Rentenphase); take the year’s annual rates —mort_rate,bf_rate,cred_rate— and the monthly death ratemort_rate_mthfrom the first.Open the ledgers.
pols_paying(12k)andpols_paidup(12k)from the previous month’s recursion;pols_if(12k)is their sum. Open the accounts at"BEF_PREM":av_pp_at(k, "BEF_PREM")per paying policy andav_pu_at(k, "BEF_PREM")at fund level;av_at(k, "BEF_PREM")is the total.If
k = ret_y(), convert. Add the Schlussüberschussanteil to the fund carried out of yeark − 1, strikerentenfaktor_applied(), setann_pp(k)and with it the instalmentann_mth_pp = ann_pp(k) / 12, and zero the account — from here on there is no Deckungskapital in this model. There is no lump sum, no election and no notice period R1.Aufschubphase, first month of the year — take the contribution, in advance.
prem_pp(k)with its Ratenzahlungszuschlag andzuz_pp(k)with its take-up, weighted bypols_paying(12k). A fractionated mode changes the amount, not the month.Aufschubphase — strike the charges and credit the account. β on premium and Zuzahlung, the Zillmerung instalment α, the Zuzahlung acquisition charge α_z, the Stückkosten u; the residue
prem_to_av_pp(k)goes toav_pp_at(k, "AFT_PREM"). The premium-free block pays only u. All four are annual and fall here.Each month — charge the insurer’s own expenses and commission. Acquisition expense and initial commission at inception; a twelfth of the annual maintenance expense per in-force policy, or of the annuity administration per annuitant and per guarantee continuation in the payout phase; renewal commission in the month the contribution falls.
Rentenphase, each month — pay the instalment.
ann_mth_pp(t)in advance onpols_if(t), plus the Rentengarantiezeit stream onpols_gtd(t).Each month — death.
pols_death_paying(t)andpols_death_paidup(t)atmort_rate_mth(t)on the counts opening that month. Where the survivor rider is on andt < ret_t(), bookclaims(t, "DEATH")aselig_surv_prob ×the released reserve — the annualdb_pp(k), struck at the end of the Versicherungsjahr; where it is off, the whole released reserve is a mortality profit and nothing is paid. Roll the two ledgers and, inside the guarantee window,pols_gtd.End of year — credit interest (Aufschubphase only).
cred_rate(k) = max(gtd_rate, decl_rate(k)), applied net of γ in one step to both blocks, giving"AFT_INT". A policy that died during the year has been credited the whole of it, which is the annual-step convention preserved.End of year — Beitragsfreistellung.
pols_freeze(12k + 11)on the survivors of the last month’s death decrement, carryingav_pp_at(k, "AFT_INT")per policy from the paying block into the premium-free block. § 165 VVG takes effect for the end of the current Versicherungsperiode R14, so this falls here and in no other month. Zero in the Rentenphase and zero on a single-premium contract.Roll forward. The two account blocks and, in the payout phase, the annuity:
ann_pp(k+1) = ann_pp(k) × (1 + ann_bonus_rate(k)), so the next year’s twelve instalments are each(1 + b)times this year’s.Assemble.
net_cf(t)from the published parts, in every month;liability_cf(t) = −net_cf(t).
At t = 12n − 1 the last survivor dies — mort_rate is 1 in the terminal year and the certainty
falls in its last month — pols_if(12n) = 0, and there is no tail state, no maturity payment and
nothing left to pay.
Known modeling pitfalls#
These are the specific ways an implementation of this product looks right and is wrong; each becomes a test.
Carrying a surrender value across from the endowment or Schicht-3 chassis. There is none, at any duration R1 R14 REG-R28 REG-R39. Assert that
result_cf()has noclaims_lapsecolumn, that nocv_pp,surr_value_pp,loan_pporlapse_ratecells exists, and thatcheck_no_capital()isTrueon every model point. The mirror error is subtler: computing a Rückkaufswert internally “for reference” and then flooring the Deckungskapital at it, which changes the account in the early years even though nothing is ever paid.Treating Beitragsfreistellung as a lapse. It removes the premium, not the policy R14. Assert
pols_if(t+1) = pols_if(t) × (1 − mort_rate_mth(t))exactly, withbf_rateabsent from the identity, and that a model point run withbf_rate ≡ 0has the samepols_ifseries as the base run while itspremiumsseries is strictly larger fromt = 12. The freeze itself is annual and falls in the year’s last month, § 165 VVG taking effect for the end of the current Versicherungsperiode, sopols_freeze(t) = 0wherevernot is_anniv(t).Averaging the paying and premium-free account values into one per-policy figure. They diverge from the first freeze, because one keeps receiving
prem_to_av_ppand the other does not. Assertav_pp(k) > av_pu_at(k, "BEF_PREM") / pols_paidup(12k)for everykafter the first freeze on the anchor, and thatcheck_av_roll_fwd()fails if the two blocks are collapsed.Double-counting a charge as an expense. β, γ, the Stückkosten and the Zillmerung amortisation are account deductions, i.e. insurer income; the insurer’s outgo is the acquisition expense, the commission, the maintenance expense and the annuity administration. Assert that
expenses(t)is invariant tobeta_prem,gamma_avandzill_rate, and that changinggamma_avmovesnet_cfonly through the annuity that the smaller fund buys at Rentenbeginn.Charging the whole Zillmerung in year one. It is spread over
zill_spread_y = 5premium-paying years std and capped at 25 ‰ of the Beitragssumme R16 REG-R16. Assertalpha_amort_pp(k)is equal fork = 0 … 4and zero fromk = 5, thatΣ_k alpha_amort_pp(k) = zill_rate × beitragssumme_pp()to 1e-9, and that an in-force point past duration 5 (model point 6) seesalpha_amort_pp(k) = 0in every year.Stacking the declared rate on top of the guarantee. A German laufende Verzinsung is the total credited rate, so
cred_rate(k) = max(gtd_rate, decl_rate(k)), notgtd_rate + decl_rate(k)R15 R16. Assert that on model point 8 (gtd_rate = 2,75 %, above the whole declared path)cred_rate(k) = gtd_ratein every year, while on the anchor it equalsdecl_rate(k).Letting premiums or Zuzahlungen run past Rentenbeginn, or the Dynamik run off the policy duration. Assert
prem_pp(k) = 0andzuz_pp(k) = 0for everyk ≥ ret_y(), thatzuz_pp(k) = 0onceduration_y(k) ≥ zuzahlung_end_dur, and that on an in-force point the Beitragsdynamik is keyed toduration_y(k)and not tok— model point 6, atduration_init = 17, must open atprem_base_pp × 1.02^17, not atprem_base_pp. On the monthly frame the same rule sayspremiums(t) = 0for everyt ≥ ret_t()and in eleven months of twelve before it.Applying the Ratenzahlungszuschlag twice, or to the wrong thing. It multiplies the laufender Beitrag only. Assert
prem_pp(k) / (prem_base_pp × (1+δ)^d(k)) = φexactly in every year, thatzuz_pp(k)is invariant toprem_mode, and that a single-premium point carriesφ = 1. The monthly grid does not change this: φ prices a fractionated mode by loading the amount, soprem_due(t)still puts the whole year’s contribution in the first month of the year, and a model that both split the cash into instalments and kept φ would charge for the deferral twice.Paying a death benefit with the rider off. Death before Rentenbeginn pays nothing in the base design; the reserve is released as a mortality profit R1 REG-R39. Assert
claims_death(t) == 0.0at everyton every model point withsurv_annuity_rate == 0, including the anchor, and thatcheck_av_roll_fwd()still closes in every year — the released reserve leaves the fund whether or not anything is paid.Paying the death benefit to the estate, or as a lump sum. With the rider on it is payable only where an eligible survivor exists and must buy an annuity R1. Assert
claims_death(t) = elig_surv_prob × av_at(k, "AFT_INT") × mort_rate_mth(t)on model point 3 — the annual reserve released, the month’s share of the deaths — that the twelve months of a year sum to the annual-step model’s figure, that settingelig_surv_prob = 0reproduces the rider-off run cash flow for cash flow, and that the model publishes no lump-sum column of any kind.Converting on the projection’s own mortality. The Rentenfaktor is a contractual rate struck on first-order bases R17 [S1]; the projection runs on the best estimate. Assert
ann_pp(ret_y())is invariant tomort_be_factorwhileclaims_annuityis not, so that the wedge between the two bases shows up as payout-phase margin rather than being silently removed.Paying the year’s annuity on the year’s opening count. The annuity is monthly in advance R1, and this is the one thing the monthly grid was adopted for. The annual-step model these notes were first written for booked twelve instalments together at the start of the payout year on
pols_if(t), which paid a life that died in the first month of a year for the whole of it — a stated std approximation, generous by up to a full year’s annuity and concentrated in the high-mortality tail. Assertclaims_annuity(t) = ann_mth_pp(t) × pols_if(t)withann_mth_pp(t) = ann_pp(k) / 12exactly, that the twelve instalments of a payout year are equal and the thirteenth(1 + b)times the twelfth, and that the whole payout phase is below the annual booking — 265 725,57 € against 270 016,08 € on the anchor, 1,6 % of it.Taking the guaranteed Rentenfaktor when the current one is higher, or the reverse. The rule is
max(garantiert, aktuell)R17 [S1]. Assert the anchor converts atrf_curr(67)and model point 13 atrentenfaktor_gtd, and thatrentenfaktor_applied()is monotone in both inputs.Getting the Rentengarantiezeit wrong in either of two ways. It runs
Gyears from Rentenbeginn, not from each death, and it pays only to an eligible survivor and is never commutable R1. On the monthly grid the window is12Ginstalments, so assertpols_gtd(t) = 0for everyt > gtd_end_t() = ret_t() + 12 × guarantee_period_y − 1—t = 299on model point 4 — thatpols_gtdis monotone non-decreasing inside the window, that a continuation starts in the month after the death that triggered it, and that no cash flow anywhere discounts a continuation into a lump sum.Using a period table where the basis is generational. DAV 2004 R is a Generationentafel and the improvement lives inside it R17 REG-R49. Assert
mort_rate_at_age(x, y2) < mort_rate_at_age(x, y1)fory2 > y1at every age, and that two model points at the same attained age but differentconclusion_year— 6 and 9 both reach age 60 — see differentmort_rate. Assert too thatmort_rate_mthis the geometric twelfth, so that(1 − mort_rate_mth(t))^12 = 1 − mort_rate(t)to 1e-14 and the annual layer is reproduced exactly;mort_rate(t) / 12closes neither.Applying today’s Höchstrechnungszins to an in-force contract. The rate attaches at conclusion and stays with the contract REG-R14 REG-R15. Assert that the shipped model points carry four distinct
gtd_ratevalues and thatcred_rate(k) ≥ gtd_ratein every year on every point.Modelling the BUZ as premium income with no benefit.
prem_base_ppis the old-age contribution; the BUZ premium and the BU-Rente belong toBU_DE_S. Assertbuz_prem_share < 0.50for every model point R1, thatprem_total_pp(k) > prem_pp(k) + zuz_pp(k)exactly wherebuz_prem_share > 0, and thatprem_total_ppappears in noresult_cf()column and innet_cfat not.
Policyholder behaviour modelling#
All formulas here are std reference constructions; there is no German calibration evidence for any of them, and the research file records the absence as gap 3.
The exit. This product has one behavioural exit and it pays nothing: the Beitragsfreistellung R14.
bf_rate(dur)is a duration table — 4,0 % / 3,0 % / 2,0 % — whose shape is argued from the product’s structure (an income-volatile buyer, a penalty-free and reversible option early; nothing realisable to leave for, late) and whose levels are invented. There is no dynamic component, because there is no competing product a Basisrente holder can move to and no cash to move.What a Beitragsfreistellung does and does not do. It stops the premium and moves the policy to the premium-free cohort. It does not end the contract, release any value, change the Rentenbeginn or release any of the § 10 constraints: a paid-up Basisrente is still certified, still protected, still payable only as an annuity R1 R9 R14. No Wiederinkraftsetzung is modelled — premiums can in practice be resumed within a window R14, but none was established (gap 8), so the premium-free block is absorbing, which is conservative on premium income.
The Zuzahlung take-up is the second behavioural assumption and the more distinctive one. The contribution the tax ceiling makes possible is paid out of a profit not known until the year end, so
zuz_take_up(dur)is a utilisation rate, not a contract term: 0.70 early, 0.85 in mid-term, 0.90 late. A model that treats the Zuzahlung as contractual has hard-coded an assumption.Selection on the annuitant pool is not modelled, and the direction is known. A Basisrente cannot be surrendered or commuted, so a policyholder in poor health has no exit and nobody leaves the pool — which argues for lighter mortality than a comparable Schicht-3 portfolio, where the Kapitalwahlrecht lets an impaired life leave R17. No evidence for this was found; it is a std view and a stated model risk, and
mort_be_factoris the single lever that would carry it.Almost no annuitisation behaviour, and what remains is not modelled: no Kapitalwahlrecht and no Teilkapitalauszahlung at any duration R1 R23. The one election Schicht 1 does admit is the Kleinbetragsrenten-Abfindung of § 10 Abs. 1 Nr. 2 Satz 3 EStG, which permits commutation of a Kleinbetragsrente “im Sinne von § 93 Absatz 3 Satz 2 oder 4” R1 R23 REG-R42, and this model does not implement it std. Two of the three reasons it gave have since fallen: the threshold is settled at 1,5 % of the monthly Bezugsgröße R23, and a carrier’s AVB treatment is now known — one offers the Abfindung [S1] and the GDV model conditions make it the insurer’s right, not the policyholder’s [S12], which is itself a reason a projection cannot assume take-up. The remaining reason stands:
Riester_DE_Salready carries the machinery (is_kleinbetrag(),commutation_pp()) for a reader who wants it. Model point 10 is a small enough contract to reach it, and this model annuitises it. The Schicht-3 chassis needs a take-up assumption and a declaration window for a Kapitalwahlrecht; this product needs neither, which is the cleanest simplification the layer buys. Deferral of Rentenbeginn is likewise a model-point input rather than a behaviour: no carrier’s permitted range was established (gap 8), and model point 12 exercises a deferral to 70 as a configuration.
Worked example#
Configuration. Model point 1, the anchor cell, exactly as shipped in model_point_table.csv:
point_id = 1; policy_id = DE-BAS-0001; sex = M (reporting only; pricing is unisex REG-R34);
entry_age = 45; conclusion_year = 2026; duration_init = 0; ret_age = 67;
pols_if_init = 1.0; prem_form = regular; prem_base_pp = 6,000.00 € p.a.; prem_mode = annual,
so prem_freq_load = 1.000; prem_dyn_rate = 0.02; zuzahlung_pp = 4,000.00 € p.a.;
zuzahlung_end_dur = 22; paidup_at_init = 0; av_pp_init = 0.00 €; ann_pp_init = 0.00 €;
gtd_rate = 0.0100; rentenfaktor_gtd = 28.00 € per month per 10 000 €; guarantee_period_y = 0;
surv_annuity_rate = 0.00, so the survivor rider is off and claims_death(t) = 0 at every t;
buz_prem_share = 0.00; tariff_id = de_basis_std; beh_table_id = base;
surplus_scenario_id = base; rf_scenario_id = base. Hence age(0) = 45,
ret_y() = 67 − 45 = 22 and ret_t() = 264, omega_age() = 121,
proj_len_y() = 121 − 45 + 1 = 77 and proj_len() = 924: twenty-two
years of Aufschubphase at attained ages 45 to 66, then fifty-five years of Rentenphase at attained
ages 67 to 121. The annual table below therefore shows selected rows of
result_cf_annual() — every year of the first five, the years in which a lever changes, the
conversion year and its neighbours, and a decade sample of the payout phase — together with
full-precision totals over all seventy-seven; a second table opens the first payout year month by
month, which is the view only the finer grid can give.
Assumptions, each tagged. Mortality: the shipped mort_table.csv is a std DAV 2004
R-shaped first-order proxy, anchored so that mort_rate_at_age(67, 2005) = 0.014000 exactly, with
a flat improvement trend = 0.015 at every age applied from mort_base_year = 2005, so that
mort_rate_base(t) = qx(age(t)) × (1 − 0.015)^(cal_year(t) − 2005); the best-estimate factor is
mort_be_factor = 0.85 std, giving mort_rate(t) = 0.85 × mort_rate_base(t) as the year’s
annual rate and mort_rate_mth(t) = 1 − (1 − mort_rate(t))^(1/12) as the monthly rate applied. The real basis is
DAV 2004 R, which is the property of the Deutsche Aktuarvereinigung and is cited by name and
never shipped R17 REG-R47 REG-R49; a replacement must preserve the generational structure, the
first-order margin and the Altersverschiebung convention, and must reproduce the anchor above if the
worked example is to close. Interest: gtd_rate = 1.00 % p.a., the Höchstrechnungszins for new
business from 1 January 2025 R16 REG-R14 REG-R15; declared decl_rate(t) = 2.60 % for
k = 0…9, 2.40 % for k = 10…19, 2.20 % for k ≥ 20 std, so
cred_rate(k) = max(0.0100, decl_rate(k)) = decl_rate(k) throughout and the guarantee never binds on
this cell. Surplus at and after conversion: terminal_bonus_rate = 4.0 % of the fund at
Rentenbeginn std, allocated at that single date because the contract has no earlier exit
trigger R15; ann_bonus_rate(k) = 1.0 % p.a. compounding std, a teildynamische Rente.
Conversion: rf_curr(67) = 31.50 € in scenario base std against the guaranteed 28,00 €
std, so rentenfaktor_applied() = max(28.00, 31.50) × 1.000 = 31.50 — the current factor
binds on this cell, and model point 13 exercises the other branch R17 [S1]. Charges:
zill_rate = 25 ‰ of the Beitragssumme R16 REG-R16 REG-R20, amortised over
zill_spread_y = 5 years, now cited rather than std R14 [S1] [S12]; alpha_zuz_rate = 2.5 % of each Zuzahlung std;
beta_prem = 7.5 % std; gamma_av = 0.35 % p.a. std; unit_cost_pp = 36.00 € p.a.
inflating at expense_infl = 1.5 % std. Insurer expense and commission, all std:
acq_expense_pp = 250.00 € at inception; comm_init_rate = 2.5 % of beitragssumme_pp() at
inception; comm_renew_rate = 1.5 % of premiums plus Zuzahlungen from the second projection year;
maint_expense_pp = 60.00 € per in-force policy p.a. inflating; annuity_admin_pp = 36.00 € per
annuitant p.a. inflating. Behaviour, all std: bf_rate = 4.0 % at durations 1–5, 3.0 % at
6–10, 2.0 % at 11+; zuz_take_up = 0.70 at durations 1–5, 0.85 at 6–15, 0.90 at 16+;
elig_surv_prob = 0.55, which is inert on this cell because the survivor rider is off and is
carried only so that model points 3 and 12 can exercise it. No BUZ, no Rentengarantiezeit, no
survivor’s annuity, no provider transfer, no Wiederinkraftsetzung.
All amounts in euros; pols_if, pols_paying and av to the precision shown; cash flows to the
cent. The Total row is summed at full precision and then rounded, which is not in general the same
as adding the rounded cells.
The frame, by projection year#
Selected rows of Projection[1].result_cf_annual() — the monthly frame summed into projection years
— with av read from result_pols(), the annual state. Transcribed from the model’s own output. The
two columns not shown — claims_death and claims_survivor — are structurally zero at every one of
the 924 months on this cell, because the survivor rider is off and there is no
Rentengarantiezeit; they are published as zero columns rather than dropped, because a column of
zeros states the product fact where a missing column would only hide it. liability_cf is omitted
for the same reason it is trivial: it is −net_cf to the last bit.
pols_if, pols_paying and av are a count, a count and a balance, read at the start of the
year. They are reported and not summed, which is why the Total row carries an em dash for all
three.
k |
age |
|
|
|
|
|
|
|
|
|
|---|---|---|---|---|---|---|---|---|---|---|
0 |
45 |
1.000000 |
1.000000 |
0.00 |
6,000.00 |
2,800.00 |
0.00 |
309.96 |
4,094.85 |
4,395.19 |
1 |
46 |
0.998560 |
0.958618 |
7,366.75 |
5,866.74 |
2,684.13 |
0.00 |
60.77 |
128.26 |
8,361.84 |
2 |
47 |
0.997024 |
0.918857 |
14,688.72 |
5,735.87 |
2,572.80 |
0.00 |
61.58 |
124.63 |
8,122.46 |
3 |
48 |
0.995385 |
0.880653 |
21,968.46 |
5,607.33 |
2,465.83 |
0.00 |
62.40 |
121.10 |
7,889.66 |
4 |
49 |
0.993635 |
0.843941 |
29,208.23 |
5,481.05 |
2,363.03 |
0.00 |
63.22 |
117.66 |
7,663.20 |
5 |
50 |
0.991769 |
0.808662 |
36,410.00 |
5,356.97 |
2,749.45 |
0.00 |
64.05 |
121.60 |
7,920.77 |
10 |
55 |
0.980403 |
0.686467 |
78,013.70 |
5,020.79 |
2,333.99 |
0.00 |
68.18 |
110.32 |
7,176.28 |
15 |
60 |
0.964766 |
0.610615 |
119,589.52 |
4,930.84 |
2,198.21 |
0.00 |
72.24 |
106.94 |
6,949.88 |
20 |
65 |
0.943366 |
0.539704 |
162,771.09 |
4,811.83 |
1,942.94 |
0.00 |
76.04 |
101.32 |
6,577.40 |
21 |
66 |
0.938235 |
0.526033 |
171,114.15 |
4,783.75 |
1,893.72 |
0.00 |
76.75 |
100.16 |
6,500.55 |
22 |
67 |
0.932780 |
0.512516 |
179,426.24 |
0.00 |
0.00 |
7,033.50 |
46.46 |
0.00 |
−7,079.96 |
23 |
68 |
0.926985 |
0.509331 |
0.00 |
0.00 |
0.00 |
7,058.31 |
46.86 |
0.00 |
−7,105.16 |
32 |
77 |
0.856165 |
0.470419 |
0.00 |
0.00 |
0.00 |
7,111.92 |
49.36 |
0.00 |
−7,161.28 |
42 |
87 |
0.724266 |
0.397948 |
0.00 |
0.00 |
0.00 |
6,610.57 |
48.20 |
0.00 |
−6,658.77 |
52 |
97 |
0.521776 |
0.286689 |
0.00 |
0.00 |
0.00 |
5,205.94 |
39.88 |
0.00 |
−5,245.82 |
62 |
107 |
0.272931 |
0.149962 |
0.00 |
0.00 |
0.00 |
2,945.81 |
23.71 |
0.00 |
−2,969.52 |
72 |
117 |
0.074193 |
0.040765 |
0.00 |
0.00 |
0.00 |
847.29 |
7.16 |
0.00 |
−854.46 |
76 |
121 |
0.032209 |
0.017697 |
0.00 |
0.00 |
0.00 |
416.84 |
3.60 |
0.00 |
−420.43 |
Total |
— |
— |
— |
113,761.91 |
51,236.28 |
265,725.57 |
3,695.91 |
6,437.82 |
−110,861.12 |
The whole Aufschubphase of this table is the annual-step model’s, to the last bit. The premium, the Zuzahlung, the commission, both counts and every account balance are unchanged, because twelve geometric monthly death rates compound back to the annual rate exactly. What moved is the payout phase — 265 725,57 € against 270 016,08 €, because the Rente is now paid one instalment at a time — and the expenses, 3 695,91 € against 3 731,36 €, because a mid-year leaver bears only the months it was there.
The Total row is summed over all 924 months at full precision and then rounded, which
is not in general the same as adding the rounded cells. Here it differs in four of the six money
columns: adding the seventy-seven rounded claims_annuity cells gives 265 725,58 € against
265 725,57 €, the rounded expenses cells 3 695,88 € against 3 695,91 €, the rounded
commissions cells 6 437,83 € against 6 437,82 € and the rounded net_cf cells −110 861,14 €
against −110 861,12 €. premiums and zuzahlungen happen to
agree at the cent. The differences are one to three cents and they are not errors; they are what
rounding before adding costs, and a test that asserted the sum of the printed
cells would be asserting the wrong number.
The same conversion year, month by month#
The first twelve rows of the Rentenphase, straight off result_cf(). This is the view the annual
grid could not give, and the reason the model was moved onto a monthly one.
t |
|
|
|
|
|---|---|---|---|---|
264 |
0.932780 |
587.80 |
3.88 |
−591.68 |
265 |
0.932296 |
587.50 |
3.88 |
−591.38 |
266 |
0.931812 |
587.19 |
3.88 |
−591.07 |
267 |
0.931328 |
586.89 |
3.88 |
−590.76 |
268 |
0.930845 |
586.58 |
3.87 |
−590.46 |
269 |
0.930361 |
586.28 |
3.87 |
−590.15 |
270 |
0.929878 |
585.97 |
3.87 |
−589.84 |
271 |
0.929395 |
585.67 |
3.87 |
−589.54 |
272 |
0.928913 |
585.36 |
3.87 |
−589.23 |
273 |
0.928430 |
585.06 |
3.86 |
−588.92 |
274 |
0.927948 |
584.76 |
3.86 |
−588.62 |
275 |
0.927467 |
584.45 |
3.86 |
−588.31 |
Year 22 |
— |
7,033.50 |
46.46 |
−7,079.96 |
Every instalment is the same 630,16 € per annuitant — ann_mth_pp = ann_pp(22) / 12 — and what falls
across the twelve rows is the count, from 0,932780 to 0,927467. The annual-step model paid
12 × 630,16 × 0,932780 = 7 053,60 €, the whole year on the opening count; the monthly grid pays
7 033,50 €, and the 20,11 € between them is the annuity the old convention paid to lives that died
during the year. Over the fifty-five payout years that is 4 290,52 €.
Four things in the frame are worth reading before the checks below.
The first year’s strain is the commission, not the Zillmerung. The 818,97 € instalment is a deduction from the policyholder’s account and hence insurer income; it is absent from
expensesand visible only inav(1)being 7 366,75 € rather than the 8 140,00 € that 8 800,00 € less the 7,5 % premium charge would otherwise have bought. On the monthly frame the whole strain falls in month 0, which nets +4 450,15 € against the year’s +4 395,19 €: the other eleven months carry a twelfth of the maintenance expense and nothing else, at −5,00 € each.pols_payingfalls far faster thanpols_if. Byk = 22the in-force count has fallen only to 0,932780 while the premium-paying count has fallen to 0,512516: 0,441765 of the cohort has passed through a Beitragsfreistellung by then, and the 0,420265 of it that has not since died is sitting in the premium-free ledger, where it is still in force, still credited and still converts. Not one policy has left through a surrender, there being none to leave through.The
avcolumn ends atk = 22and does not taper. The whole fund converts in one step; fromk = 23there is no Deckungskapital in this model, only an annuity obligation that a Deckungsrückstellung stands behind and that delib does not compute.The annuity rises and the claim falls.
ann_pp(k)compounds at 1,0 % — 7 561,91 €, 7 637,53 €, 7 713,91 €, paid as 630,16 €, 636,46 € and 642,83 € a month — while the year’sclaims_annuitypeaks atk = 29and then falls away as mortality outruns the Überschussrente. Nothing is paid aftert = 923: the last survivor dies in the last month of the terminal year, and there is no maturity value and no tail state.
Three independent checks and a closure identity#
Each of these rebuilds a cell of the table a different way, in arithmetic that can be followed with a calculator. They are what makes this example a check rather than a printout.
Check 1 — the first year’s account, from the charge scale up. The Beitragssumme is the sum of twenty-two escalating premiums,
S = 6,000.00 x (1.02^22 - 1) / 0.02 = 6,000.00 x 27.2989835388 = 163,793.9012327640
so the zillmerised acquisition charge is 0.025 x S = 4,094.8475308191 and its annual instalment
α(0) = 4,094.8475308191 / 5 = 818.9695061638. The Zuzahlung actually paid is
4,000.00 x 0.70 = 2,800.00 — the take-up at policy years 1 to 5 — and it carries its own 2,5 %
charge rather than a share of the Zillmerung. So
N(0) = (6,000.00 + 2,800.00) x (1 - 0.075) - 818.9695061638 - 0.025 x 2,800.00 - 36.00
= 8,140.00 - 818.9695061638 - 70.00 - 36.00
= 7,215.0304938362
and crediting it at the declared 2,60 % net of the 0,35 % reserve charge,
A^p(0, "AFT_INT") = 7,215.0304938362 x (1 + 0.026 - 0.0035) = 7,377.3686799475
The table’s av at k = 1 is 7 366,75 €, which is not that number: it is that number after the
year’s death decrement, 7,377.3686799475 x (1 - 0.0014396389) = 7,366.7479330812 — at the
annual rate, because the account is an annual quantity and twelve monthly decrements compound to
exactly that. That is the fund-level roll-forward check_av_roll_fwd() closes, and the fact that it
closes with bf_rate at 4 % is the point — a Beitragsfreistellung moves reserve between the two
blocks and removes none.
Check 2 — the first year’s decrement split, and the rate behind it. The shipped table’s rate at age
45 is 0.014000 x 1.085^(45 - 67) = 0.0023263433, improved from the 2005 base to the 2026 calendar
year by (1 - 0.015)^21 = 0.7280493868, giving a first-order 0.0016936928; the best-estimate
factor takes it to the annual q(0) = 0.85 x 0.0016936928 = 0.0014396389, and the recursion
applies its geometric twelfth
q_mth(0) = 1 - (1 - 0.0014396389)^(1/12) = 0.0001200491
in each of the year’s twelve months. The freeze falls once, in the last of them, on the survivors of that month’s deaths:
sum_{t=0..11} pols_death(t) = 1.000000 x (1 - (1 - 0.0001200491)^12) = 0.0014396389
pols_freeze(11) = 1.000000 x (1 - 0.0014396389) x 0.04 = 0.0399424144
pols_paying(12) = 1.000000 x (1 - 0.0014396389) x (1 - 0.04) = 0.9586179467
pols_paidup(12) = 0.0000000000 + 0.0399424144 = 0.0399424144
pols_if(12) = 0.9586179467 + 0.0399424144 = 0.9985603611
Two things to read here. (1 - 0.0001200491)^12 = 0.9985603611 is 1 - q(0) to the last bit, which
is why every annual figure in the table above is the annual-step model’s; the arithmetic twelfth
0.0014396389 / 12 = 0.0001199699 is a smaller number and closes nothing. And
0.9985603611 = 1.000000 - 0.0014396389 exactly: the 4 % Beitragsfreistellung rate has
cancelled out of pols_if entirely, which is what distinguishes this product’s decrement
structure from a Schicht-3 annuity’s and is what check_pols_roll_fwd() asserts at every t.
Check 3 — the conversion, and the branch of the max that binds. The fund at the start of
projection year k = 22, the end of month 263, is 179 426,2405488701 €; the Schlussüberschussanteil grosses it up once, at this single date,
F = 179,426.2405488701 x 1.04 = 186,603.2901708250
and it is shared over the 0,932780 policies still in force, giving 200 050,6219643070 € per
annuitant. The applied Rentenfaktor is max(28.00, 31.50) x 1.000 = 31.50 — the current
factor binds on this cell — and 31,50 € a month per 10 000 € of capital is 378,00 € a year, so
ann_pp(22) = 200,050.6219643070 / 10,000 x 378.00 = 7,561.9135102508
ann_mth_pp(264) = 7,561.9135102508 / 12 = 630.1594591876
and 630,16 € a month is what is actually paid. The annual table’s claims_annuity at k = 22 is
not that annuity times the opening count: it is the twelve instalments each weighted by the
count in force at the start of its own month,
sum_{t=264..275} 630.1594591876 x pols_if(t) = 7,033.4955825855
against the 12 x 630.1594591876 x 0.9327803550 = 7,053.6043684572 an annual booking on the opening
count would have paid — the 20,11 € difference being the annuity the old convention paid to lives
that died during the year. Had the guaranteed 28,00 € bound instead the annuity would have been
6 721,70 €, 11,1 % lower.
Closure identity — the decrements sum to one, and the cash flow statement closes. Over the whole projection,
sum_{t=0..923} pols_death(t) + pols_if(924) = 1.0000000000 + 0.0000000000 = 1.0000000000
because the terminal age is absorbing. Not one policy leaves by any other route: there is no lapse decrement, no surrender and no commutation on this product, and the 0,441765 of the cohort that went beitragsfrei is inside that 1,000000 rather than beside it. And on the money side, the Total row itself closes,
113,761.9053943146 + 51,236.2751085046 - 265,725.5654249013 - 3,695.9135293351 - 6,437.8202383614
= -110,861.1186897786
which is check_net_cf() — delib’s first ruling — evaluated over all 924 months at once
rather than one month at a time. A last arithmetic coincidence that is not a coincidence:
commissions(0) = 0.025 x S = 4,094.8475308191 is the same number as
alpha_total_pp() = 0.025 x S, because the initial commission rate and the Höchstzillmersatz are
both 2,5 %. That is the German design rather than an accident — what the insurer pays out at
inception is sized to what it may write into the reserve — and it is why moving comm_init_rate
without moving zill_rate opens a hole in the first year that nothing closes.
The variant: the Einmalbeitrag (model point 5)#
The notes’ model point table promises a second premium form, and this is it: model point 5, a
58-year-old paying a single 60 000,00 € Einmalbeitrag and deferring to 67, on the same tariff,
behaviour and surplus scenario as the anchor. prem_form = single, so prem_freq_load() = 1.000
— a single payment carries no Ratenzahlungszuschlag — bf_rate(t) = 0 at every t, there being
no premium left to stop, and ret_y() = 9 (ret_t() = 108), proj_len_y() = 64,
proj_len() = 768. Rows of result_cf_annual(), with av from result_pols():
k |
age |
|
|
|
|
|
|
|
|---|---|---|---|---|---|---|---|---|
0 |
58 |
1.000000 |
0.00 |
60,000.00 |
0.00 |
309.89 |
1,500.00 |
58,190.11 |
1 |
59 |
0.995842 |
56,170.68 |
0.00 |
0.00 |
60.52 |
0.00 |
−60.52 |
2 |
60 |
0.991418 |
56,838.16 |
0.00 |
0.00 |
61.15 |
0.00 |
−61.15 |
4 |
62 |
0.981702 |
58,157.41 |
0.00 |
0.00 |
62.36 |
0.00 |
−62.36 |
8 |
66 |
0.958317 |
61,584.00 |
0.00 |
0.00 |
64.56 |
0.00 |
−64.56 |
9 |
67 |
0.951537 |
62,484.63 |
0.00 |
2,447.87 |
39.03 |
0.00 |
−2,486.90 |
10 |
68 |
0.944341 |
0.00 |
0.00 |
2,453.07 |
39.31 |
0.00 |
−2,492.37 |
19 |
77 |
0.857193 |
0.00 |
0.00 |
2,427.85 |
40.67 |
0.00 |
−2,468.52 |
39 |
97 |
0.468265 |
0.00 |
0.00 |
1,587.41 |
29.35 |
0.00 |
−1,616.77 |
63 |
121 |
0.014921 |
0.00 |
0.00 |
65.92 |
1.37 |
0.00 |
−67.29 |
Total |
— |
— |
60,000.00 |
84,666.77 |
2,292.11 |
1,500.00 |
−28,458.88 |
Again summed at full precision and then rounded; adding the sixty-four rounded net_cf cells gives
−28 458,87 € against −28 458,88 €, a one-cent difference. Every account balance in the column is
the annual-step model’s to the last bit; the annuity is 1 496,37 € lower and the expenses 29,11 €
lower, for the two reasons the grid changed.
Two features are the whole point of the variant. The Beitragssumme of a single-premium contract
is the single premium, so S = 60,000.00, the Zillmerung is 0.025 x 60,000.00 = 1,500.00 and
the initial commission is the same 1 500,00 € — an order of magnitude below the anchor’s, because
the anchor’s twenty-two escalating premiums sum to 163 793,90 €. And the five Zillmerung
instalments still run, 300,00 € a year at k = 0 ... 4, so from k = 1 the account is debited by
an acquisition charge that the one premium has already come and gone without covering. That is
visible in the first year’s account, which checks in one line:
N(0) = 60,000.00 x (1 - 0.075) - 300.00 - 0.00 - 36.00 = 55,164.0000000000
av_pp(1) = 55,164.0000000000 x (1 + 0.026 - 0.0035) = 56,405.1900000000
av(1) = 56,405.1900000000 x 0.9958424243 = 56,170.6811550510
the last line being the fund-level value the table publishes — av_pp is per paying policy and
av is at fund level, and on this cell the two differ only by the death decrement, because there
is no premium-free block at all.
The other branch of the Rentenfaktor (model point 13)#
Pitfall 13 promises a cell on which the guaranteed factor binds, and model point 13 is it: the same
6 000,00 € annual premium as the anchor, no Dynamik, no Zuzahlung, entry at 46 and
rf_scenario_id = low, so that rentenfaktor_curr() is 27,72 € against a guaranteed 34,00 €.
Cell |
|
|
per annuitant |
|
|
applied |
|
a month |
|---|---|---|---|---|---|---|---|---|
Anchor, |
179,426.24 |
186,603.29 |
200,050.62 |
28.00 |
31.50 |
31.50 |
7,561.91 |
630.16 |
Point 13, |
98,185.81 |
102,113.25 |
109,428.02 |
34.00 |
27.72 |
34.00 |
4,464.66 |
372.06 |
On the anchor the guarantee is worth nothing and would have given 6 721,70 €; on model point 13 it is worth 824,65 € a year, the current factor alone giving 3 640,01 €. The projection is sensitive to whichever factor is higher and completely insensitive to the other, which is why a sensitivity run on the guaranteed Rentenfaktor over a whole book returns zero until it crosses the current one and then moves in a straight line.
What changed in these notes when the model was built#
The specification above was written before the model existed. Building it settled eight points the prose had left ambiguous or got wrong; each was resolved in favour of the arithmetic that closes and each is now written into the notes at its source rather than only here.
The terminal age is absorbing. The horizon bullet asserted
mort_rate_at_age(omega, ·) = 1, but the generational trend carries the table’s terminal rate below 1 in every calendar year after the base year andmort_be_factorwould take it to 0,85 in any case. The rule now sits onmort_rate(t), where it belongs.av(ret_y())is the pre-conversion fund, not zero.check_av_roll_fwd()now assertsav_at(ret_y(), "AFT_INT") = 0— the conversion empties the account — andav(k) = 0for everyk > ret_y(). The old wording would have hidden the one number the conversion is struck on.The acquisition expense is a fund-level amount,
acq_expense_pp × pols_if_init × 1{…}, matching the initial commission beside it. Nothing moves on the shipped points, all of which carrypols_if_init = 1.0.omega_age_maxis not a Reference.omega_age()is read off the last row ofmort_table.csvand needs no separate cap, so the name is dropped from the scalar list.zuz_take_up(k)is published as a cells, alongsidebf_rate(t): pitfall 7 turns on it, and hiding it insidezuz_ppwould have made it untestable.behaviour_table.csvis keyed by the policy year,policy_year(t), so the notes’ “durations 1–5” reads off the file directly.duration(t)itself stays completed policy years and is 0 in the first projected year of a new-business point.Model point 9 carries no Zuzahlung. It exists to sit at the whole Höchstbetrag, and a top-up above a contribution that already consumes the ceiling would model relief the deduction could not absorb.
zuzahlung_ppis exercised by points 1, 3 and 11.The guaranteed Rentenfaktor binds on two cells, not one. Model point 6 is a 2009 tariff converting at 60, and an older tariff’s guaranteed factor standing above today’s current factor at that age is the realistic case rather than a contrivance.
A ninth was settled later, when the model moved from an annual step to a monthly one:
The grid is monthly and the contract is not.
tcounts months andk = t // 12projection years, and a cells’ argument says which clock it is on. The contribution, the four charges, the declared rate, the Deckungskapital, the Beitragsfreistellung and the conversion stay annual — they are Versicherungsjahr terms, and the Ratenzahlungszuschlag is how a German tariff prices a fractionated mode without moving the Versicherungsperiode — so the whole Aufschubphase is bit-identical to the annual-step model’s. What the finer grid was adopted for is the Rente, which is quoted and paid monthly: pitfall 12’s compression is gone and the payout phase falls 4 290,52 € because a life that dies in a payout year is no longer paid the whole of it. Gap 21 is not closed by it — whether the instalment is vorschüssig or nachschüssig was still not established, and the model pays in advance std.
Nothing in the shipped model contradicts a cited fact. Every figure in the tables above is the model’s own output, and every parameter behind them is std except the 25 ‰ Höchstzillmersatz R16 REG-R16 and the 1,00 % Rechnungszins R16 REG-R15.
Valuation and reserve pointers#
This library projects gross best-estimate-style liability cash flows, undiscounted, on a declared grid. The valuation layers consume them and are cited, not reproduced.
The German statutory Deckungsrückstellung. An HGB reserve of § 341f HGB REG-R54, computed on the Rechnungsgrundlagen the DeckRV fixes REG-R14: the contract’s own Rechnungszins — capped at the Höchstrechnungszins in force at conclusion and fixed for the whole term REG-R15 — and first-order DAV 2004 R REG-R49, with acquisition costs entering through the Zillmerung of § 4 DeckRV REG-R16. The model’s
av_at(k, ·)and, after conversion, the monthly annuity obligationann_mth_pp(t) × pols_if(t)are what a Deckungsrückstellung stands behind; neither is a reserve and delib computes none.The Zinszusatzreserve. The additional HGB reserve arising where the Referenzzins of § 5 Abs. 3 DeckRV falls below a contract’s tariff rate REG-R17. It exists in no other jurisdiction in this repository and bites hardest on annuity business, because the § 12 MindZV test looks at the highest Rechnungszins applicable over the next fifteen years REG-R18. A long-dated Basisrente is exactly the business it bites on, and nothing here represents it.
Überschussbeteiligung and the MindZV floor. The credited rate here is a std scenario, not a derivation: a real declaration runs through the four surplus sources, the MindZV 90 / 90 / 50 floor REG-R18, the RfBV REG-R19 and the § 139 VAG Sicherungsbedarf test REG-R9. Making the declaration endogenous would need the insurer’s whole HGB result — a fund model, not a policy model.
Solvency II. Technical provisions are a best estimate — the probability-weighted average of future cash flows discounted at the relevant risk-free term structure — plus a risk margin REG-R1 REG-R2 REG-R6, with EIOPA publishing the curves monthly REG-R4.
BEL = Σ_t v(t) × liability_cf(t)over the recursions above. No cost-of-capital rate, contract-boundary rule or standard-formula shock anywhere in this library was read from a retrieved instrument, so every such figure would be std REG-R2.Contract boundary. The premium is contractually variable — the Beitragsdynamik may be declined and the Zuzahlung is discretionary — which raises a boundary question this library does not resolve: whether the projected Zuzahlungen and Dynamik increments fall inside the contract boundary at all. The model projects the whole stream and publishes it; a boundary-truncated view is obtained by zeroing
zuzahlung_ppandprem_dyn_rateon the model point, not by editing formulas.IFRS 17. A profit-participating Basisrente is a direct-participating contract that would be measured under the variable fee approach REG-R55; the same expected-cash-flow engine feeds it, and grouping, CSM and risk adjustment are out of scope.
The guarantees are options and this model prices none of them. The guaranteed Rechnungszins is a written floor on the credited rate; the guaranteed Rentenfaktor is a written option on the annuity conversion, and on this product it is worth materially more than on its Schicht-3 sibling because the policyholder has no Kapitalwahlrecht to fall back on R1 R17. A deterministic path prices neither. A stochastic-on-deterministic run — the crediting rule and the
max(gtd, curr)conversion re-evaluated per scenario — is what a time-value-of-options-and-guarantees calculation consumes.
Key sensitivities and model risks#
In rough order of leverage for a German Schicht-1 block:
The Rentenfaktor. It converts the entire accumulated fund into the entire payout-phase liability, so it is the single largest lever in the model — and no Rentenfaktor level, range or time series exists anywhere in the delib corpus, for this or any product (gap 4). Both the guaranteed 28,00 € and the current 31,50 € are std, and the
maxof the two means the projection is sensitive to whichever is higher and completely insensitive to the other. Model point 13 exists to make that discontinuity visible.The conversion-basis wedge. The fund is converted on a first-order Rentenfaktor and then run off on second-order mortality, so the payout phase carries a structural margin that
ann_bonus_rategives back. Both levers are std independently, so the payout phase’s profitability here is an artefact of two unanchored numbers rather than a result — the most important thing to understand before quoting any figure from it.Mortality level and the generational trend. A 1,5 % annual improvement compounded over a 22-year deferment and a 40-year payout is worth far more than it looks, and the trend is flat across ages here where DAV 2004 R’s own are not REG-R49 — the more dangerous of the two on a long run.
The Beitragsfreistellung rate. It governs how much premium is ever collected and how large the premium-free block grows, and on this product it is the only behavioural exit. The base table takes about a third of the cohort out of premium payment before Rentenbeginn; nothing in the corpus supports any level (gap 3).
The declared surplus path. A 20 bp difference in
decl_ratecompounded over the anchor’s 22-year deferment moves the fund at conversion by several per cent, and the annuity with it. The path is a scenario and is labelled one; the guarantee at 1,00 % never binds on it, and a path that fell below the guarantee would makecred_rate’smaxoperative and change the shape.The charge levels, all of them std. The § 7 AltZertG Produktinformationsblatt exists precisely to publish this product’s total charge burden as a single comparable Effektivkosten number, per quotation, and not one was reached (gap 2). The charge set lands inside the argued 0,6 %–1,2 % band for a klassisch tariff, but that is a construction, not a calibration.
The Zuzahlung take-up. The product’s signature premium form is entirely a modeller’s view. Setting
zuz_take_up ≡ 0removes about two fifths of the anchor’s contribution stream and is a legitimate variant, not a bug — and a projection that treats the Zuzahlung as contractual has quietly made the opposite assumption.The eligible-survivor probability. Inert on the anchor and decisive on model points 3, 4 and 12: it scales the whole death benefit and the whole Rentengarantiezeit stream. 0.55 has nothing behind it.
The instalment’s timing within the month, and the living texts around it. The compression that the annual grid forced — twelve instalments booked at the start of the payout year, generous to the year of death by up to a full year’s annuity — is gone with the monthly step. What remains std is vorschüssig against nachschüssig: no German convention was established (gap 21), and the model pays in advance, which is worth about one month’s interest on the annuity over the payout phase. Separately, the Höchstbetrag moves every year with the Sozialversicherungsrechengrößen-Verordnung R20, the Besteuerungsanteil every year by construction R4 R6, and the Höchstrechnungszins moved in 2025 for the first time in about thirty years REG-R15: none is a cash flow of this contract, and all three decide what a realistic model point looks like.
Data provenance. Two carrier artefacts stand behind this entire product, neither a Bedingungswerk, and not one carrier’s Basisrente contract terms were established (gap 1). Every parameter that would normally be sourced to a carrier is std. A calibration pass against a real Produktinformationsblatt, a real Bedingungswerk and a real declared-rate history is required before any quantitative use of this model.