The Projection Space#
The by-policy projection of the KLV_DE_S model.
The Space is parameterized by point_id, so Projection[1] is an ItemSpace
projecting model point 1:
>>> Projection[1].result_cf() # the worked example's anchor cell
>>> Projection.point_id = 8 # or switch the default
t counts policy months, 0-based and measured from issue: month t runs from time
t to time t + 1, so t = 0 is the first policy month and the frame runs
t = t_start() ... proj_len() - 1 contiguously, with t_start() = 12 * duration_init()
and proj_len() = 12 * policy_term() — 300 on the anchor cell — the frame’s exclusive
end. The Ablauf falls at the end of the last month. There is no ``t = proj_len()`` row.
Two clocks, and which cells carries which
A monthly grid is not a monthly product, and on this one almost nothing is monthly. The whole Überschussbeteiligung is an annual statement — the laufende Verzinsung is declared for a Versicherungsjahr, the Schlussüberschussanteilsatz accrues on that year’s closing Deckungskapital, the Ansammlungszins is an annual rate — and so is everything the tariff defines: an annual Rechnungszins, an annual first-order table, a Fackler roll-forward from one anniversary to the next, the § 169 Abs. 3 five-year spreading, the Beitragsfreistellung election at the end of a Versicherungsperiode. None of that becomes monthly. What the finer grid resolves is everything that is not contractually annual: the Beitrag the contract bills in instalments, the insurer’s running expense, and the decrements, which now fall in the month they happen and are paid the balances standing at the last anniversary.
So the argument of a cells says which clock it is on:
cells that state an annual account take
k, the 0-based policy year — the whole pricing block,res_net_pp(),res_zill_pp(),res_min_pp(),res_pp(),res_pp_at(),res_guar_pp(),pu_single_prem(),is_paid_up(),bfz_uplift_pp(),decl_rate(),term_rate(),ans_rate(),surplus_base_pp(),surplus_credit_pp(),av_sur_pp(),term_bonus_pp(),bonus_si_pp(),prem_offset_pp(),prem_charged_pp(),prem_paid_pp(),storno_rate()andinflation_factor();cells that state a month take
t— the in force, the claims, the instalments, everyresult_cf()column, and the*_close_ppcells that say what a mid-month exit is paid;the decrement rates take ``t`` and return the year’s annual rate, which is the library-wide convention and is not the same thing as an annual statement.
duration(t) = t // 12 is the bridge: it is the completed policy years at the start of
month t, and it is what a month resolves to when it reaches an annual cells.
policy_year(t) = duration(t) + 1 is the contractual 1-based label the input tables are
keyed on, age(t) = age_y(duration(t)) steps on the anniversary, and
is_anniv(t) = (t % 12 == 11) marks the month the annual machinery acts in. The frame is
indexed by t; the policy year is derived and never indexed by.
The two-speed structure that follows is the library’s convention: mort_rate() and
lapse_rate() are the annual rates of the policy year containing month t — the
vectors the technical notes tabulate — and mort_rate_mth() and lapse_rate_mth()
are the monthly rates actually applied, each 1 - (1 - r)^(1/12), so that twelve of them
compound back to exactly the year’s rate. That is what leaves the annual layer untouched:
pols_if(12k) here is the annual-step model’s own pols_if(k), so every reserve, every
declared credit and every ledger balance is the same number on the two grids.
Input data
Inputs are external files: plain CSVs living in the model folder’s parent directory,
products/kapitallebensversicherung/, read at run time rather than stored inside the
model. The model folder therefore holds nothing but formulas — no _data/, no IOSpec, no
embedded values — so a diff of the model shows logic changes only, and an input can be
edited or swapped without rewriting the model. This follows annuallife.TradLife_A;
contrast basiclife.BasicTerm_S, which keeps its inputs inside the model through
modelx’s IOSpec machinery.
The consequence worth knowing: the model is not portable on its own. Copying the
KLV_DE_S folder without its parent’s CSVs produces a model that reads and then fails on
first evaluation.
Each table has a filename Reference and a reader Cells, both on Data,
reached here through the data Reference:
Reference |
Cells |
File |
|---|---|---|
model_point_file |
data.model_point_table() |
model_point_table.csv |
mort_table_file |
data.mort_table() |
mort_table.csv |
lapse_file |
data.lapse_table() |
lapse_table.csv |
surplus_rate_file |
data.surplus_rate_table() |
surplus_rate_table.csv |
cost_file |
data.cost_table() |
cost_table.csv |
freq_loading_file |
data.freq_loading_table() |
freq_loading_table.csv |
deckrv_file |
data.deckrv_table() |
deckrv_table.csv |
Naming
Cells names follow lifelib’s basiclife.BasicTerm_S and savings.CashValue_SE
wherever those models have an analogue — pols_* for policy counts, plural nouns for
cash flows, *_rate for rates, *_pp for per-policy amounts, claims(t, kind) with
an uppercase kind string, *_at(t, timing) for the within-year reads. The technical
notes use compact actuarial symbols instead. The mapping is:
Notes symbol |
Cells |
Meaning |
|---|---|---|
(none) |
model_point() |
The selected model point row |
n |
proj_len_y() |
Policy years = policy_term |
12 n |
proj_len() |
Projected months; frame end |
(none) |
t_start() |
First projected month index |
(none) |
k_start() |
First projected policy year |
m |
prem_term() |
Beitragszahlungsdauer |
(none) |
duration_mth(t) |
Completed policy months, = t |
k |
duration(t) |
Completed policy years, t // 12 |
(none) |
is_anniv(t) |
Last month of a policy year |
x(t), x(k) |
age(t), age_y(k) |
Attained age, on each clock |
y(t) = k + 1 |
policy_year(t) |
Contractual 1-based policy year |
SE |
sum_assured() |
Guaranteed Erlebensfallleistung |
SD |
sum_death() |
Guaranteed Todesfallleistung |
i1 |
rechnungszins() |
First-order interest rate |
v1^k |
disc_factor_1st(k) |
First-order discount factor |
kpx |
tpx_1st(k) |
First-order survival from issue |
q1(x) |
mort_rate_at_age(x) |
First-order tariff rate at age x - the unisex blend, which prices and reserves |
(table) |
mort_rate_base(t) |
Sex-specific annual table rate |
q(t) |
mort_rate(t) |
Best-estimate annual mortality |
qm(t) |
mort_rate_mth(t) |
The same, applied in month t |
f |
rating_factor() |
Risikozuschlag on the death leg |
alpha, beta, gamma |
alpha_rate(), beta_rate(), gamma_rate() |
Zillmersatz; premium loading; sum-insured loading |
phi |
prem_freq_load() |
Ratenzahlungszuschlag |
(none) |
instalments() |
Payments a year |
(pricing) |
pv_death_1st(), pv_maturity_1st(), pv_benefit_1st(), ann_due_prem_1st(), ann_due_term_1st() |
The equivalence’s parts |
B |
prem_gross_pp() |
Annual Bruttobeitrag before phi |
BS |
beitragssumme() |
Beitragssumme = B x m |
A |
alpha_cost() |
Zillmered acquisition cost |
P^n |
prem_net_level_pp() |
Net level premium |
P^Z |
prem_zill_pp() |
Zillmer premium |
(prospective) |
pv_benefit_fut(k), ann_due_prem_fut(k) |
The reserve’s parts |
V^n, V^Z, V^min |
res_net_pp(k), res_zill_pp(k), res_min_pp(k) |
The three constructions |
V(k) |
res_pp(k) |
Deckungskapital at start of year k |
(within year) |
res_pp_at(k, timing) |
BEF_PREM / AFT_PREM / AFT_INT |
G(k) |
res_guar_pp(k) |
Section 169 value at end of year k |
(closing) |
res_guar_close_pp(t) |
The same, standing at end of month t |
RK(t) |
surr_value_pp(t) |
Rueckkaufswert payable in month t |
(unit paid-up) |
pu_single_prem(k) |
Single premium for one unit |
(none) |
bfz_si_pp() |
Beitragsfreie Versicherungssumme |
(none) |
bfz_uplift_pp(k) |
Section 169 uplift on election |
(none) |
is_paid_up(k) |
Whether the contract is beitragsfrei |
(none) |
bfz_fails() |
Whether the election became a surrender |
d(k) |
decl_rate(k) |
Declared laufende Verzinsung |
z(k) |
zins_ueberschuss_rate(k) |
Interest surplus rate |
s(k) |
term_rate(k) |
Schlussueberschussanteilsatz |
a(k) |
ans_rate(k) |
Ansammlungszinssatz |
(base) |
surplus_base_pp(k) |
Deckungskapital at allocation |
C(k) |
surplus_credit_pp(k) |
Surplus allocated for year k |
S(k) |
term_bonus_pp(k) |
Accrued Schlussueberschussanteil |
(closing) |
term_bonus_close_pp(t) |
The same, standing at end of month t |
U(k) |
av_sur_pp(k) |
Ueberschussguthaben per policy |
(within year) |
av_sur_pp_at(k, timing) |
BEF_INT / AFT_INT / AFT_CREDIT |
(closing) |
av_sur_close_pp(t) |
The same, standing at end of month t |
(aggregate) |
av_sur(k), av_sur_at(k, timing) |
The same, times pols_if(12k) |
Z(k) |
bonus_si_pp(k) |
Bonus sum insured |
(closing) |
bonus_si_close_pp(t) |
The same, standing at end of month t |
(offset) |
prem_offset_pp(k) |
Beitragsverrechnung offset |
B phi |
prem_charged_pp(k) |
Annual Zahlbeitrag before the offset |
(none) |
prem_paid_pp(k) |
Annual Zahlbeitrag after it |
(none) |
prem_cycle(), prem_due(t) |
The instalment cycle |
(none) |
prem_charged_inst_pp(t) |
Instalment charged in month t |
(none) |
prem_inst_pp(t) |
Instalment collected in month t |
(none) |
premiums(t) |
Premium income in month t |
w(t) |
lapse_rate(t) |
Annual surrender rate of the year |
wm(t) |
lapse_rate_mth(t) |
The same, applied in month t |
sigma(k) |
storno_rate(k) |
Stornoabzug rate |
l(t) |
pols_if(t) |
In force at the start of month t |
(within month) |
pols_if_at(t, timing) |
BEF_DECR / AFT_MORT / AFT_LAPSE |
(exits) |
pols_death(t), pols_lapse(t), pols_maturity(t) |
Expected exits in month t |
(none) |
benefit_full_pp(t) |
Full death benefit before 161 |
(none) |
benefit_death_pp(t) |
What a death claim pays |
(none) |
benefit_maturity_pp(t) |
What the Ablauf pays |
claims_* |
claims(t, kind) |
DEATH / MATURITY / LAPSE |
(none) |
inflation_factor(k) |
Expense inflation factor |
(none) |
claim_expenses(t) |
Claim handling expense |
(none) |
expenses_pp(t) |
Per-policy expense in month t |
E(t) |
expenses(t) |
Expense outgo, no commission |
(none) |
commissions(t) |
Commission outgo |
net_cf(t) |
net_cf(t) |
Net cash flow, income positive |
liability_cf(t) |
liability_cf(t) |
The same stream, outgo positive |
(the notes’ table) |
result_cf_annual() |
result_cf() summed by policy year |
The declared rate is a total, not an add-on
The single most common way to get this product wrong. The laufende Verzinsung is the Garantieverzinsung plus the laufende Zinsüberschussbeteiligung, so
zins_ueberschuss_rate(k) = max(0, decl_rate(k) - rechnungszins())
is 1,70 pp on the anchor cell’s 2,70 % declaration against a 1,00 % guarantee, and never
2,70 pp on top of 1,00 pp. The interest-surplus rate is derived and never an input. The
outer max is what keeps the nil scenario honest: the declared rate is then below the
guarantee, which the reserve roll-forward still meets in full, so the surplus is zero rather
than negative.
The base it multiplies is the *Deckungskapital* at the allocation date —
max(res_pp_at(k, "AFT_INT"), 0) — not the sum insured and not the premium. That inner
max is equally load-bearing: a gezillmerte Deckungskapital is negative for the
first several years, and a positive rate on a negative base credits a negative surplus. It
follows that a gezillmert contract earns no interest surplus in its early years even
though the § 153 VVG entitlement runs from inception — economically right, because there is
no fund to earn on, and worth saying because it looks like a bug.
Three reserves, and the one the customer gets
res_zill_pp() is the gezillmerte Deckungskapital: it is exactly -alpha_cost() at
issue and stays negative for several years. res_min_pp() is the § 169 Abs. 3 VVG floor,
the same net reserve with the acquisition cost amortised straight-line over the first five
contract years rather than over the whole premium term. Because
ann_due_prem_fut(k) / ann_due_prem_1st() falls roughly linearly over m years while
max(0, 1 - k/5) reaches zero after five, the floor normally binds on a long
gezillmert contract, with equality only at durations 0 and m. res_guar_pp() is
their maximum, floored at zero, struck at the end of policy year k because that is
what “zum Schluss der laufenden Versicherungsperiode” requires — so it reads the reserves at
k + 1. What a surrender in a non-anniversary month is paid is
res_guar_close_pp(), the value struck at the last anniversary.
With zillmer_on = 0 (model point 13) alpha_cost() is zero, all three coincide and the
floor is slack — a useful invariance test. With prem_term = 1 (model point 2) the 25 ‰
Zillmersatz buys almost nothing and the floor is slack from the first anniversary. Both are
the correct answer rather than a degenerate case.
Note that the acquisition cost is in the premium whether or not the contract is
zillmered: zillmer_on enters alpha_cost(), which is a reserving quantity, and not
the pricing equation, which always charges alpha_rate() * beitragssumme(). That is why one
insurer can publish a gezillmerte and a non-gezillmerte edition of the same tariff at the
same price.
Paid-up is not lapse, and it can fail
§ 165 VVG lets the policyholder demand conversion to a prämienfreie Versicherung at the end
of the current Versicherungsperiode, provided the agreed *Mindestversicherungsleistung* is
reached. The paid-up sum is bought with the § 169 value, so, writing e = bfz_year() - 1
for the 0-based index of the election year, bfz_si_pp() = res_guar_pp(e) /
pu_single_prem(e + 1), and the contract
stays in pols_if with that reduced sum in place of sum_assured(). Where the sum falls
short of bfz_min_si, the statute obliges the insurer to pay the § 169 value instead and
the election becomes a surrender: lapse_rate() returns 1.0 for that year and
lapse_rate_mth() places the whole of it in the year’s last month, where the election
falls. The cohort leaves there as claims(t, "LAPSE") and every later row is zero. Model
point 11 takes the first branch and model point 12 the second.
Placing that 1.0 is the one decrement decision the monthly grid forced. Spreading an annual
rate of 1.0 geometrically would put the whole cohort out in the election year’s first
month, eleven months before the election it models; the election is an act at the end of a
Versicherungsperiode, so it is a month-specific event and lapse_rate_mth() says so.
Because the § 169 floor generally exceeds the Zillmer reserve, the paid-up sum bought is worth
more than the Zillmer reserve released. bfz_uplift_pp() is that difference, discounted
to the start of the election year, and it enters res_pp_at() so that
check_res_roll_fwd() still closes in the election year rather than being switched off
there.
What surrender pays, and what it does not
A surrender at the end of month t, in the policy year k = duration(t), pays:
surr_value_pp(t) = res_guar_close_pp(t) x (1 - storno_rate(k))
+ av_sur_close_pp(t)
+ term_surr_share x term_bonus_close_pp(t)
where the *_close_pp cells return the balance standing at the end of that month: the
year’s own closing figure in an anniversary month, and the one struck at the last
anniversary in the other eleven. On a gezillmert contract that makes the guaranteed leg
exactly zero through the whole first policy year — the consumer fact this product is best
known for, and one the annual grid could not express, having paid a month-0 surrender the
value the coming anniversary would close at.
Four rules ride on that line. The *Stornoabzug* bites on the guaranteed value only — the
published deduction is a percentage of the Deckungskapital — so the accumulated
Überschussguthaben passes through undeducted. term_surr_share = 0 in the base run: the
accrued Schlussüberschussanteil is paid at the Ablauf and on death and not on
surrender, which is the choice that does not invent an entitlement the sources do not
describe; the parameter is exposed rather than hard-coded. And the surrender value is what a
suicide inside three years is paid: § 161 VVG makes the insurer leistungsfrei and
obliges it to pay the Rückkaufswert including Überschussanteile, so the German rule is a
benefit substitution and not a forfeiture. And the Stornoabzug band is a policy-year
band: it steps on the anniversary, not monthly.
The two mortality bases must not be crossed
mort_rate_at_age() is the first-order tariff rate: it prices and it reserves, and
it is a fixed unisex blend of the two table rows, because German new business has been unisex
since 21 December 2012. mort_rate_base() is this policy’s own sex-specific table
rate, and mort_rate() is mort_rate_base(t) * mort_be_factor with
mort_be_factor = 0.75: it projects. The 33 % wedge is the Sicherheitszuschlag, whose systematic release is the
Risikoüberschuss — which this model does not compute, and which a model that reserves on
the best estimate has thrown away. rating_factor is a third thing again: it is the
Risikozuschlag, and it multiplies the first-order rate in the death leg of the pricing and
the prospective reserve only — never the survivorship factors, never the benefit, and never
a best-estimate rate. sex reaches mort_rate_base(), and therefore the
decrement, and it reaches nothing else: prem_gross_pp() is identical for two model
points differing only in it, which is what model points 1 and 7 exist to make visible. The
unisex blend behind the tariff is [std] - no German insurer publishes the portfolio mix
behind its own.
The three Überschussverwendung systems
ansammlung accumulates the credit at ans_rate in av_sur_pp() and raises the maturity
benefit; bonus buys paid-up sum insured at first-order rates in bonus_si_pp(),
raising the death benefit immediately by the full bonus sum but accumulating only at
rechnungszins; beitragsverrechnung carries last year’s credit forward as this year’s
premium offset in prem_offset_pp() and neither balance grows. Because
ans_rate > rechnungszins the first gives a higher maturity benefit and the second a higher
death benefit — exactly the asymmetry the sources record, and the test that distinguishes
them. A model that sets the two rates equal destroys it.
Under beitragsverrechnung the renewal commission is charged on
:func:`prem_charged_pp`, not on :func:`prem_paid_pp`: the intermediary is paid on the tariff
premium, the surplus offset being a policyholder rebate.
Modules that are off in the base run
Two dynamic lapse constructions ship switched off, so the base run reproduces the worked
example while the machinery stays visible. Premium-shock lapse, beta_shock = 0:
1 + beta_shock * max(0, prem_paid_pp(k)/prem_paid_pp(k-1) - 1 - 0.05) on the annual
Zahlbeitrag of consecutive policy years, inert on a level Bruttobeitrag but live under
Beitragsverrechnung, where a fall in the declared rate raises the Zahlbeitrag.
Rate-gap lapse, lapse_gap_a = 0:
lapse_gap_a * max(0, ref_rate - decl_rate(k) - 0.005), keyed on the gap between the
declared rate and what is available elsewhere. Both compare annual declarations, which is
what they are about; neither becomes a monthly comparison. No German calibration of any of these numbers
exists, which is why both ship off. bwr_rate = 0 likewise switches off the
Beteiligung an den Bewertungsreserven, on the reasoning that the Sicherungsbedarf has
routinely exhausted the half share.
Sign convention
net_cf() is income positive — Beiträge in, claims, expenses and commission out —
which is the notes’ own orientation and the library-wide sign. liability_cf() publishes
the same stream outgo-positive, liability_cf(t) = -net_cf(t) exactly. Both are columns of
result_cf(), so the identity is verifiable in the frame rather than only in prose.
expenses() excludes commission — the deliberate difference from the frlib chassis,
where commission sits inside the expense column — so the six flow columns of
result_cf() sum to net_cf() without a double count. That sum is what
check_net_cf() asserts, and it is this library’s first ruling.
The shape to expect on the anchor cell, read on result_cf_annual(), is a first year that
very nearly washes - the Beitrag of 2 004,04 € almost exactly meeting the initial commission
of 2,5 % of the Beitragssumme plus the 300 € acquisition expense - then annual margins of
the order of a thousand euros that decay as the cohort lapses, and a single very large
negative year at the Ablauf when the Erlebensfallleistung and the whole accumulated
Überschussguthaben fall due together. The new-business strain of a gezillmert German
endowment sits in the reserve, which opens at -alpha_cost(), and not in the cash flow.
What the monthly grid changes, and what it does not
Nothing annual moved, and that is checkable rather than asserted. Because the monthly
decrement rates compound back to their policy year’s annual values,
[(1 - qm)(1 - wm)]^12 = (1 - q)(1 - w), the in force at every anniversary is the
annual-step recursion term for term. So prem_gross_pp() is bit-identical, and so is
every one of the three reserves at every duration, the § 169 value, the Beitragssumme, the
Zillmer cost, the declared credit, the Ansammlung balance, the bonus sum, the accrued
terminal share and the paid-up sum the § 169 value buys. The equivalence, the Fackler
roll-forward and the surplus ledgers are all policy-year identities and are checked as
such.
What the finer grid changes is the cash flows, and each change is a decision:
a Beitrag is collected in instalments on the Zahlweise’s own cycle (
prem_cycle(),prem_due(),prem_inst_pp()), so the four fractionated model points collect less than the annual grid charged them — and theecht/unechtdistinction ofunterjaehrig_form(), which on an annual grid lived entirely in a multiplier, is now a difference in the frame;a death, surrender or Ablauf falls at the end of the month of exit and is paid the balances standing then (
av_sur_close_pp()and the three cells beside it), so on a gezillmert contract a surrender in the first policy year is paid the Überschussguthaben and nothing guaranteed. The annual grid had to pay it the value the coming anniversary would close at, which is a forward-looking payment at a date it is not yet due;a twelfth of the maintenance expense accrues each month on that month’s in-force rather than on the anniversary’s, so a decrementing block costs less;
the renewal commission follows the instalment it is charged on.
On the anchor cell that is death claims 2 506,85 → 2 446,09 €, surrender claims
10 104,99 → 9 112,99 €, expenses 1 327,88 → 1 314,12 € and net_cf −11 048,31 →
−9 981,79 €, with premiums and the Ablauf payment unchanged.
result_cf_annual() sums the frame into policy years so the two can be laid side by side,
and result_surplus() stays annual because what it publishes moves once a year.
Cells Descriptions#
- sex()[source]#
The insured’s sex, M or F. Decrement lookup only - never a pricing input.
§ 20 Abs. 2 Satz 1 AGG was repealed and German new business has been unisex since 21 December 2012, so
prem_gross_pp()must be identical for two model points differing only here. The first-order table is nevertheless sex-specific, because that is the raw material a unisex tariff blends; the blend itself is [std] and this model prices every point on its ownsexrow only through the decrements, which is what model points 1 and 7 exist to make visible.
- smoker()[source]#
The insured’s smoker status, N or S.
Carried because the Gesundheitsprüfung asks, and because it is what a Risikozuschlag would be struck on. It feeds
rating_factor()through the model point rather than through a formula: DAV 2008 T R / NR exist for smoker-differentiated pricing but are not public, and no German insurer publishes a loading scale.
- issue_year()[source]#
The calendar year of conclusion: the contract’s cohort identity.
It fixes the two DeckRV ceilings through
hrz_max()andzillmer_max(), both of which stay with the contract for its whole term, and it fixes the income-tax cohort. A 4,00 % guarantee on a 2026 issue year is not a stress, it is a data error - which is whycheck_rechnungszins_cap()is a model invariant rather than a build script.
- issue_age()[source]#
The age last birthday at issue, stepping at the policy anniversary [std].
No located German endowment wording states an age basis, so the convention is a standardization. On this annual grid an implementation on real dates carries a fractional offset of at most one year.
- duration_init()[source]#
The completed policy years at the valuation date; 0 for new business.
An elapsed count, so it is 0-based by nature and is already on the frame’s scale: it fixes where the frame opens -
t_start() = duration_init()- and it is what suppresses the acquisition expense and the initial commission on an in-force point, which incurred both long ago. Model point 10 carries 14, and its frame therefore opens att = 14, the fifteenth policy year.
- pols_if_init()[source]#
The number of policies represented at
t_start(): 1.0, a single-policy model point.The library’s projections are per policy, so this is 1 everywhere; it is named rather than written as a literal because it is the scale of the roll-forward tolerances and because
result_cf()’s firstpols_ifmust equal it exactly.
- policy_term()[source]#
n: the Versicherungsdauer in years. Equals
proj_len().The Ablauf falls at the end of the last policy year, index
n - 1on the frame, and the Erlebensfallleistung is paid there to the survivors of that year’s mortality.
- prem_term()[source]#
m: the Beitragszahlungsdauer in years, at most
policy_term().m = 1is the Einmalbeitrag - the other premium form, and the case in whichann_due_prem_1st()collapses to 1, the Beitragssumme to the single premium itself and the 25 ‰ Zillmersatz to almost nothing.m < nis the abgekürzte Beitragszahlungsdauer: premiums stop while cover runs on, and the reserve then rolls forward with no premium at all.
- sum_assured()[source]#
SE: the guaranteed Erlebensfallleistung, in euros - the Versicherungssumme.
Paid at the Ablauf to a survivor, plus the accumulated Überschussguthaben, any bonus sum insured and the accrued Schlussüberschussanteil. It is not what a paid-up contract receives; see
bfz_si_pp().
- death_ratio()[source]#
The Todesfallleistung as a multiple of the Erlebensfallleistung.
1.00 is the gemischte Versicherung auf den Todes- und Erlebensfall proper, where the two guaranteed sums are equal. Below 1 the contract is the same chassis with an unequal death sum, subject to the Mindesttodesfallschutz: for a contract concluded from 1 April 2009 the death sum must be at least 50 % of the Beitragssumme, which is a model point design constraint checked when the table is built and not a model formula.
- prem_freq()[source]#
The payment frequency, a key into freq_loading_table.csv.
annual,half_yearly,quarterlyormonthly. The frequency buys a Ratenzahlungszuschlag only whereunterjaehrig_form()isunecht.
- unterjaehrig_form()[source]#
Whether a sub-annual premium is
echtorunecht.unechtmeans the Versicherungsperiode remains the year and the sub-annual payment is an instalment of an annual premium, which is what the Ratenzahlungszuschlag compensates.echtmeans the period is genuinely sub-annual, and then no loading applies. Model points 4 and 5 are the same monthly contract under the two readings, and the distinction is entirely lost on a model that treats frequency as a single multiplier.
- rechnungszins()[source]#
i1: the contract’s own guaranteed technical rate, fixed at conclusion.
A contract term, not a market rate: it is set once, at conclusion, and carried for the whole term, which is why a German in-force book is a stack of cohorts and why the in-force model point carries 1,75 % while new business carries 1,00 %. It must not exceed the cohort’s
hrz_max();check_rechnungszins_cap()asserts it.
- zillmer_on()[source]#
1 where the Deckungskapital is gezillmert, 0 where it is not.
A per-tariff design choice a German insurer makes and publishes - one carrier maintains a gezillmerte and a non-gezillmerte edition of the same tariff - and not an invariant of German practice. It enters
alpha_cost()and therefore the reserve; it does not enter the pricing equation, so the two editions cost the same.
- surplus_use()[source]#
The Überschussverwendung:
ansammlung,bonusorbeitragsverrechnung.Verzinsliche Ansammlung accumulates the credit in
av_sur_pp()atans_rate; the Bonussystem buys paid-up sum insured inbonus_si_pp()at first-order rates; the Beitragsverrechnung carries the credit forward as next year’s premium offset. Becauseans_rate > rechnungszins, the first pays more at maturity and the second more on an early death - the asymmetry the corpus records and pitfall 15 asserts.
- scenario_id()[source]#
The key into surplus_rate_table.csv naming this policy’s declared-rate path.
base,lowornil. The declared rate is insurer-discretionary, revisable annually and capable of being zero, so it is a scenario rather than an assumption; thenilpath is the sourced statement that the surplus may be zero euros, made runnable.
- rating_factor()[source]#
f: the Risikozuschlag multiplier; 1.00 at standard rates.
It scales the first-order mortality in the death leg of
pv_death_1st()andpv_benefit_fut(), so it raisesprem_gross_pp(). It must not reach the survivorship factors, the benefit or the best-estimate rate:benefit_death_pp()andmort_rate()are both invariant to it. No German scale is public; model point 14 carries 1.50 [std].
- av_sur_pp_init()[source]#
The Überschussguthaben per policy carried at
t_start(), in euros.Zero for new business; model point 10 opens at duration 14 with 6 000 € already accumulated. It is not part of the Deckungsrückstellung: § 341f HGB forms that provision excluding verzinslich angesammelte Überschussanteile, and the separation is the reason this balance is a cells of its own rather than part of
res_pp().
- bonus_si_init()[source]#
The bonus sum insured already bought at
t_start(), in euros.Zero on every shipped model point: the Bonussystem point starts at issue with nothing bought. The column exists so that an in-force contract on that system can be projected without a formula change.
- bfz_year()[source]#
The contractual, 1-based policy year at whose end Beitragsfreistellung is elected; 0 means never.
It is a policy-year label and not a frame index, which is why the column is left 1-based: 0 has to stay free to mean “never”. The election falls at the end of policy year
bfz_year(), which is the end of periodbfz_year() - 1and the start of periodbfz_year(), sois_paid_up()testst >= bfz_year()whilelapse_rate(),bfz_uplift_pp()andbfz_si_pp()read the election year itself asbfz_year() - 1. A value at or belowduration_init()means the contract was already beitragsfrei when the frame opened.A deterministic model point column, not a decrement. The corpus establishes the § 165 VVG right in full and gives no take-up rate at all, and the one market aggregate that would bear on it mixes the paid-up route in with surrenders and cannot be split - so modelling the election as a schedule keeps an unsourced number out of the base run. What that costs is stated rather than hidden: a real German book converts a material, duration-dependent share to beitragsfrei, and this model shows that path only where a model point elects it.
- proj_len_y()[source]#
n: the number of policy years counted from year 0, equal to
policy_term().The unit every annual construction in this model is written against: the equivalence, the three reserves, the § 169 value, the Überschussdeklaration and the paid-up purchase all run over policy years and are indexed by
k = 0 ... n.proj_len()is twelve times this and is the frame.
- proj_len()[source]#
The number of projected policy months,
12 x proj_len_y().300 on the anchor cell. The exclusive end of the frame, which is 0-based:
result_cf()coverst = t_start() ... proj_len() - 1, soresult_cf().index[-1] == proj_len() - 1on every model point andlen(result_cf()) == proj_len() - t_start(). This is lifelib’s ownfor t in range(proj_len()). The Ablauf falls at the end of the last month, where the survivors take the Erlebensfallleistung. There is no ``t = proj_len()`` row;pols_if(proj_len())is defined because the closure identities need it, and it weights no cash flow.
- t_start()[source]#
The first projected month:
12 x duration_init().A new-business point opens at
t = 0and an in-force point at the month its completed duration reaches - an elapsed count of policy years, so the conversion is a multiplication and not an offset. Where the frame starts is a product fact and the conventions suite does not assert it; contiguity from here toproj_len() - 1is what it asserts instead.
- k_start()[source]#
The first projected policy year:
duration_init().The annual clock’s counterpart of
t_start(), and the two are the same statement in two units:t_start() == 12 * k_start(). Every annual cells that has to know where the frame opens reads this one, so the two clocks cannot drift apart.
- duration_mth(t)[source]#
Completed policy months at the start of month t; equal to
t.tis 0-based and counted from issue on every model point, the in-force one included - its frame opens att = t_start()rather than re-basing the clock - so the identity is trivial and the cells exists to name the unit. It is what the premium instalment cycle is counted in, and the vocabularySofort_DE_S,BU_DE_S,Pflege_DE_SandRLV_DE_Salready use.
- duration(t)[source]#
k: the completed policy years at the start of month t,
duration_mth(t) // 12.0-based, as lifelib’s
durationis: it is 0 through the whole first policy year. It is the bridge between the two clocks — every annual cells in this model takes a policy yeark, and this is what a month resolves to. It is what every duration-keyed schedule is indexed on: the § 169 Abs. 3 five-year spreading inres_min_pp(), the Stornoabzug band, the Beitragszahlungsdauer, the § 161 window.
- is_anniv(t)[source]#
Whether month t is the last month of a policy year,
t % 12 == 11.The policy anniversary falls at the end of it, and that is the instant the contract’s annual machinery acts: the Überschussdeklaration is credited, the Deckungskapital rolls forward by Fackler, the § 169 value is struck, the Beitragsfreistellung election takes effect and the Ablauf falls. A claim in any other month is paid the balances that were standing at the last anniversary; see
av_sur_close_pp().
- age(t)[source]#
x(t): the attained age in the policy year containing month t.
age_y(duration(t)). The age steps on the policy anniversary — att = 12, 24, ...— and not monthly: the age basis is the attained age at the anniversary and the finer grid does not make it finer.
- age_y(k)[source]#
x(k): the attained age at the start of policy year k,
issue_age() + k.The annual face of
age(), and the one every pricing and reserving cells reads: those all take a policy year, so they must not go through a month to reach an age.
- policy_year(t)[source]#
y(t): the contractual, 1-based policy year containing month t,
duration(t) + 1.Derived, never indexed by:
tis the model’s clock and this is the label the contract and the input tables use. It is the key into thepolicy_yearcolumn of lapse_table.csv (lapse_rate(),storno_rate()) and of surplus_rate_table.csv (decl_rate(),term_rate(),ans_rate()), which are 1-based schedules and are left that way rather than being re-keyed to the frame.
- policy_year_y(k)[source]#
The contractual, 1-based label of policy year k:
k + 1.The annual face of
policy_year(), for the annual cells that key a 1-based schedule.
- mort_rate_at_age(x)[source]#
q1(x): the first-order tariff death rate at attained age x - the unisex blend.
unisex_shareof the male table row plus the rest of the female one, at 0.5 / 0.5 [std]. This is the Rechnungsgrundlage erster Ordnung of the tariff, and every pricing and reserving formula in the model reads it:tpx_1st(),pv_death_1st(),pv_benefit_fut(),ann_due_prem_fut()and the Fackler roll-forward inres_pp_at().It is blended rather than read off the policy’s own
sexrow because German new business has been unisex since 21 December 2012: § 20 Abs. 2 Satz 1 AGG was repealed, and a tariff that charged a woman less than a man for the same endowment would be unlawful. The first-order table is nevertheless sex-specific, because that is the raw material a unisex tariff blends - which is exactly why this cells andmort_rate_base()are two different quantities and not two indexings of one. The blend itself is a fixed portfolio mix and is [std]: no German insurer publishes the mix behind its unisex tariff.The table is a [std] Makeham-form proxy anchored at
mort_rate_1st(M, 37) = 0.001200exactly, standing in for DAV 2008 T, which is the property of the Deutsche Aktuarvereinigung, is not public and is not redistributed here; see theDatadocstring for what a replacement must preserve.
- mort_rate_base(t)[source]#
The sex-specific annual first-order table rate of month t’s policy year.
A lookup into mort_table.csv on this policy’s own
sexrow atage(), and the parent of the best-estimate decrement:mort_rate(t) = mort_rate_base(t) * mort_be_factor. Flat across a policy year’s twelve months, the attained age stepping on the anniversary.It is not the rate the contract is priced or reserved on - that is
mort_rate_at_age(), the unisex blend - and the two are deliberately different quantities. A German tariff may not price on sex; a best-estimate projection of a particular life must.sextherefore reaches the decrement and nothing else, andprem_gross_pp()is identical for two model points differing only in it.
- mort_rate(t)[source]#
q(t): the best-estimate annual death rate of the policy year containing month t.
mort_rate_base(t) * mort_be_factorwithmort_be_factor = 0.75[std], so the first-order table carries a 33 % safety loading. Annual, as the library-wide convention requires: this is the vector the technical notes tabulate, andmort_rate_mth()is what the recursion applies. Invariant torating_factor(), which is a first-order loading and has no business in a best estimate.It is the only place
sexreaches a cash flow.
- mort_rate_mth(t)[source]#
qm(t): the monthly best-estimate death rate applied at the end of month t [std].
1 - (1 - q(t))^(1/12), the constant-force conversion of the policy year’s annual rate — derived geometrically and not by dividing by twelve, so that twelve months of it compound back to exactlyq(t), which is the factor the annual-step model this replaced applied at the anniversary. That is what makes the in-force at every policy anniversary identical to the annual model’s, and with it every annual quantity the reserve and the Überschussbeteiligung are built on.No German instrument states a conversion convention for any decrement, so the choice is a standardization; what is not optional is that it reproduce the annual factor.
- disc_factor_1st(k)[source]#
v1^k: the first-order discount factor over k years, at
rechnungszins().Used only by the pricing and reserving formulas. The published cash flows are undiscounted; discounting a liability is a valuation layer’s job and this library does not do it.
- tpx_1st(k)[source]#
kpx: first-order survival from the issue age to
issue_age() + k.tpx_1st(0) = 1andtpx_1st(k) = tpx_1st(k-1) * (1 - q1(issue_age + k - 1)), on the unrated first-order table:rating_factor()loads the death claim rate, not the survivorship, so a Risikozuschlag raises the price without shortening the life the survival benefit is priced on.
- hrz_max()[source]#
The § 2 DeckRV Höchstrechnungszins for this contract’s
issue_year.A cohort fact: the ceiling in force at conclusion applies for the whole term. The published history splits 1994 and 2000 mid-year and a year-keyed table cannot, so both split years carry the higher of the two rates - which makes
check_rechnungszins_cap()permissive rather than strict in exactly the two years where the model cannot know which half of the year a contract was written in [std].
- zillmer_max()[source]#
The § 4 DeckRV Höchstzillmersatz for this contract’s
issue_year.40 ‰ of the Beitragssumme to 2014 and 25 ‰ from 1 January 2015, the LVRG cut. A cap on the charge, and not to be confused with § 169 Abs. 3 VVG’s five-year spreading, which is a floor on the value:
check_zillmer_cap()andcheck_surr_floor()assert the two separately for that reason.
- instalments()[source]#
The number of premium instalments a year for this policy’s
prem_freq().1, 2, 4 or 12. Reported rather than used: the projection runs on an annual grid and collects the year’s Beitrag in advance, so the instalment count enters only the Ratenzahlungszuschlag it justifies.
- prem_freq_load()[source]#
phi: the Ratenzahlungszuschlag multiplier on the annual Bruttobeitrag.
The table value where
unterjaehrig_form()isunecht- 1.000 annual, 1.020 half-yearly, 1.030 quarterly, 1.050 monthly [std] - and exactly 1.000 where it is ``echt``, because a genuine sub-annual Versicherungsperiode is not an instalment of an annual one and carries no loading. Model points 4 and 5 are the same monthly contract under the two readings.
- alpha_rate()[source]#
alpha: the Zillmersatz, a fraction of the Beitragssumme.
25 ‰, sitting at the § 4 DeckRV ceiling for a contract written from 2015 - the ceiling is cited, the level is [std], and no German carrier’s actual acquisition cost is public. It is charged in the premium whether or not the contract is zillmered; only
alpha_cost()carrieszillmer_on.
- beta_rate()[source]#
beta: the collection loading, a fraction of the Bruttobeitrag over
prem_term.3,0 % [std]. The form - a percentage of the gross premium over the premium-paying period - is what the corpus establishes; the level is not.
- gamma_rate()[source]#
gamma: the administration loading, a fraction of the Versicherungssumme p.a.
1,5 ‰ over the whole Versicherungsdauer [std]. Neither the form nor the level is established anywhere in the corpus, which is why the reserve carries no separate Verwaltungskostenrückstellung for the years after the Beitragszahlungsdauer: the pricing equation funds the running cost and the classical reserve convention assumes the ongoing loadings meet it.
- sum_death()[source]#
SD: the guaranteed Todesfallleistung,
sum_assured() * death_ratio().The tariff death sum, which is what the pricing and the reserve are struck on. What a death claim actually pays is
benefit_death_pp(), which adds the surplus balances and substitutes the Rückkaufswert on the § 161 VVG suicide share; and a paid-up contract’s guaranteed death sum isbfz_si_pp() * death_ratio()instead.
- pv_death_1st()[source]#
The first-order present value at issue of the death benefit, per policy.
SD * sum over k of v1^(k+1) * kpx * f * q1(x0 + k)over the whole Versicherungsdauer, with the Risikozuschlagfon the claim rate in this leg only. Claims fall at the end of the policy year of death, which is why the discount exponent isk + 1.
- pv_maturity_1st()[source]#
The first-order present value at issue of the Erlebensfallleistung, per policy.
SE * v1^n * npx. No Risikozuschlag: an extra-mortality loading may not make the survival benefit cheaper, so it stays out of both the discount and the survivorship.
- pv_benefit_1st()[source]#
The first-order present value at issue of both guaranteed benefits, per policy.
On the gemischte Versicherung proper, where
death_ratio = 1, this is very nearly the endowment factor times the sum insured, and the price is only weakly sensitive to the mortality basis - the survival leg dominating a twenty-five-year contract’s reserve.
- ann_due_prem_1st()[source]#
The first-order annuity-due factor over the Beitragszahlungsdauer, per policy.
sum over k = 0 .. m-1 of v1^k * kpx. Exactly 1.0 on an Einmalbeitrag, which is why the single-premium branch needs no special case anywhere.
- ann_due_term_1st()[source]#
The first-order annuity-due factor over the Versicherungsdauer, per policy.
sum over k = 0 .. n-1 of v1^k * kpx. It is the base of thegammaadministration loading, which runs for the whole term rather than for the premium-paying period - the asymmetry that makes an abgekürzte Beitragszahlungsdauer dearer per premium.
- prem_gross_pp()[source]#
B: the annual Bruttobeitrag per policy before the Ratenzahlungszuschlag.
Struck by the first-order equivalence principle, which is linear in
Bbecause the Beitragssumme isB * m:B (1 - beta) a_m - alpha B m = pv_benefit_1st + gamma SE a_n
so
B = (pv_benefit_1st + gamma SE a_n) / ((1 - beta) a_m - alpha m).check_equivalence()asserts that the identity closes.The Bruttobeitrag is not a model point column: no German endowment premium rate table is public for any carrier, so a shipped rate would be an invention. It is derived, reported, and it rises with
rating_factor()while being identical for two points differing only insex().
- beitragssumme()[source]#
BS: the Beitragssumme,
prem_gross_pp() * prem_term().The total of all premiums payable over the agreed term, before the Ratenzahlungszuschlag, and the reference base for the § 4 DeckRV acquisition-cost cap, for the initial commission and for the Mindesttodesfallschutz test.
- alpha_cost()[source]#
A: the zillmered acquisition cost written into the reserve, in euros.
zillmer_on() * alpha_rate() * beitragssumme(). Zero on a non-gezillmert tariff, where the three reserve constructions then coincide - but the cost is charged in the premium either way, becausezillmer_ondecides where it sits in the reserve and not whether it is charged. It is capped byzillmer_max()times the Beitragssumme;check_zillmer_cap()asserts it.
- prem_net_level_pp()[source]#
P^n: the net level premium,
pv_benefit_1st() / ann_due_prem_1st().The pure benefit premium with no loading of any kind. It is a pricing quantity that never becomes a cash flow: what is collected is
prem_paid_pp(). It is the premium the net reserveres_net_pp()is struck on.
- prem_zill_pp()[source]#
P^Z: the Zillmer premium,
prem_net_level_pp() + alpha_cost() / ann_due_prem_1st().The net premium plus the level annual charge that amortises the zillmered acquisition cost over the Beitragszahlungsdauer. It is the premium the Zillmer reserve rolls forward on, which is why
check_res_roll_fwd()reads it and notprem_charged_pp(): one is a first-order reserving quantity and the other is a cash flow.
- pv_benefit_fut(k)[source]#
The first-order present value of the remaining guaranteed benefits at the start of k.
Prospective, over the remaining term
n - kfor the 0-based policy yeark, on the attained ageage_y(k), with the Risikozuschlag on the death leg only. Atk = nthe remaining term is zero and the value isSE, the maturity payment then due; beyond that it is zero.The argument is a policy year and not a month, like every pricing and reserving cells here: a first-order reserve is struck on the tariff’s annual bases against an annual Rechnungszins, so it stays on the annual clock whatever the projection grid is.
- ann_due_prem_fut(k)[source]#
The first-order annuity-due factor over the remaining premium-paying period.
sum over j = 0 .. max(0, m - k) - 1 of v1^j * jp(x(k))for the 0-based policy yeark. Zero once the Beitragszahlungsdauer has run out, which is what makes the reserve of an abgekürzte Beitragszahlungsdauer roll forward on interest and mortality alone.
- res_net_pp(k)[source]#
V^n: the net prospective reserve at the start of policy year k, per policy.
pv_benefit_fut(k) - prem_net_level_pp() * ann_due_prem_fut(k), and therefore exactly zero atk = 0on a new-business point - which is the equivalence principle stated as a reserve. It carries no acquisition cost at all, so it is neither what the insurer holds nor what the customer gets; it is the construction the other two are built from.This and the two below are the premium-paying constructions, computed on the full
sum_assured()for the whole remaining term, whether or not the contract has been made paid-up. What the contract actually holds isres_pp().
- res_zill_pp(k)[source]#
V^Z: the gezillmerte Deckungskapital at the start of policy year k, per policy.
res_net_pp(k) - alpha_cost() * ann_due_prem_fut(k) / ann_due_prem_1st(): the net reserve less the part of the acquisition cost the future premiums have yet to repay.It is exactly ``-alpha_cost()`` at ``k = 0``, which on the anchor cell is -1 252,53 €. That is not a defect: it is the arithmetic of Zillmerung, and it is the reason § 169 Abs. 3 VVG needs a floor at all. How long it stays negative is a parameter question. At the post-2015 25 ‰ ceiling over a twenty-five-year Beitragszahlungsdauer the zillmered cost is 0,625 of one annual premium, so the first Zillmer premium more than repays it and the reserve is positive from the first anniversary; at the pre-2015 40 ‰ ceiling, or over a long term with a short premium period, it is negative for longer. With
zillmer_on = 0it coincides withres_net_pp().
- res_min_pp(k)[source]#
V^min: the § 169 Abs. 3 VVG floor reserve at the start of policy year k, per policy.
res_net_pp(k) - alpha_cost() * max(0, 1 - k/5)for the 0-based policy yeark: the same net reserve with the angesetzte Abschluss- und Vertriebskosten spread evenly over the first five contract years rather than over the whole premium term. The straight-line reading is [std]; the alternative - a five-year Zillmerung - gives a slightly lower floor at durations 1 to 4 and the same value from duration 5.On a long gezillmert contract this floor normally binds, with equality to
res_zill_pp()only at durations 0 andm. A model publishing only the Zillmer reserve as the surrender value understates it at essentially every duration.
- is_paid_up(k)[source]#
Whether the contract is beitragsfrei at the start of policy year k.
True only where a Beitragsfreistellung was elected (
bfz_year > 0), the election year has passed and the election succeeded - that is, the beitragsfreie Versicherungssumme it bought reached the agreed Mindestversicherungsleistungbfz_min_si. Where it did not, § 165 VVG obliges the insurer to pay the § 169 value instead and the election becomes a surrender; seelapse_rate().bfz_year()is the contractual, 1-based policy year at whose end the election falls, so the election year is periodbfz_year() - 1and the contract is paid-up from periodbfz_year()onwards: hencek >= bfz_year()here.The clause order matters and is not cosmetic: testing the year before calling
bfz_si_pp()is what keeps the election year itself off the paid-up basis, so thatres_guar_pp()can price the purchase without depending on its own result.
- res_pp(k)[source]#
V(k): the guaranteed Deckungskapital per policy at the start of policy year k.
The gezillmerte construction
res_zill_pp()while the contract is premium-paying, andbfz_si_pp() * pu_single_prem(k)once it is beitragsfrei - the reserve of the reduced paid-up endowment the § 169 value bought.Defined at
k = proj_len_y(), where it issum_assured()(or the paid-up sum): the closing reserve of the last policy year is the maturity payment itself. That value weights no cash flow and exists forcheck_res_roll_fwd().This is the model’s contribution to the § 341f HGB Deckungsrückstellung line and is not floored at zero as the balance sheet would floor it, so the negative early gezillmert values stay visible.
av_sur_pp(k)is explicitly not part of it.
- res_pp_at(k, timing)[source]#
The guaranteed Deckungskapital per policy at a point inside policy year k.
"BEF_PREM"res_pp(k), the opening reserve before the year’s Beitrag."AFT_PREM"after the first-order Zillmer premium has been credited, and after any
bfz_uplift_pp(). The premium credited here isprem_zill_pp()- a first-order reserving quantity, not the Zahlbeitrag ofprem_charged_pp()- and it is credited only while the contract is premium-paying."AFT_INT"the closing guaranteed reserve of policy year k: the Fackler roll-forward of
AFT_PREMatrechnungszins()with the first-order mortality released over the survivors,(V + P^Z) (1 + i1) = f q1 SD + (1 - q1) V(k+1)
This is the *Deckungskapital* at the allocation date that the declared surplus rate multiplies, and it is computed retrospectively here while
res_pp()computes the same quantity prospectively - which is what givescheck_res_roll_fwd()its teeth.
- pu_single_prem(k)[source]#
The first-order single premium at the start of year k for one unit of paid-up cover.
pv_benefit_fut(k) / sum_assured(): the present value of one euro of Erlebensfallleistung withdeath_ratioeuros of Todesfallleistung over the remaining term, on the contract’s own Rechnungsgrundlagen der Prämienkalkulation including the Risikozuschlag.It is the price at which the § 169 value buys the beitragsfreie Versicherungssumme (
bfz_si_pp()), and the price at which the Bonussystem buys bonus sum insured (bonus_si_pp()). Atk = proj_len_y()it is exactly 1.
- bfz_si_pp()[source]#
The beitragsfreie Versicherungssumme the § 169 value buys, in euros; 0 if never.
res_guar_pp(e) / pu_single_prem(e + 1)withe = bfz_year() - 1, the 0-based index of the election year - exactly what § 165 VVG prescribes, the paid-up benefit being calculated by recognised actuarial rules on the Rechnungsgrundlagen der Prämienkalkulation on the basis of the *Rückkaufswert* under § 169 Abs. 3 bis 5. Two structural consequences follow: the paid-up sum inherits the five-year spreading floor, and because that floor generally exceeds the Zillmer reserve the sum bought is worth more than the reserve released - the difference beingbfz_uplift_pp().Where the result falls below
bfz_min_si(2 500 € [std]) the election is not a Beitragsfreistellung at all; seeis_paid_up()andlapse_rate().
- bfz_uplift_pp(k)[source]#
The § 169 uplift credited to the reserve in the Beitragsfreistellung year; else 0.
(res_guar_pp(k) - res_zill_pp(k+1)) * (1 - q1(k)) / (1 + i1)atk = bfz_year() - 1- the 0-based index of the election year - where the election succeeds, and zero everywhere else. It is the § 169 Abs. 3 floor uplift - the amount by which the value the paid-up sum is bought with exceeds the Zillmer reserve released - discounted back to the start of the election year so that it can enterres_pp_at()as a credit.It exists so that
check_res_roll_fwd()still closes in the election year rather than being switched off there, and the identity it then asserts is a real one: thatbfz_si_pp() * pu_single_prem(bfz_year())really isres_guar_pp(bfz_year() - 1), i.e. that the paid-up purchase was made at the right price.
- res_guar_pp(k)[source]#
G(k): the § 169 VVG guaranteed value at the end of policy year k, per policy.
max(res_zill_pp(k+1), res_min_pp(k+1), 0)while the contract is premium-paying, andmax(res_pp(k+1), 0)once it is beitragsfrei, where the floor is already inside the paid-up sum that was bought.It reads the reserves at
k + 1because § 169 Abs. 3 VVG strikes the value zum Schluss der laufenden Versicherungsperiode and not at the cancellation date, and it takes the maximum because the Mindestrückkaufswert is a floor on the value: the customer gets whichever construction is higher. It is the base of the Stornoabzug, the base of the paid-up purchase and the base of the Bewertungsreserven share.
- decl_rate(k)[source]#
d(k): the declared laufende Verzinsung in policy year k, from the scenario table.
The total declared rate - the Garantieverzinsung plus the laufende Zinsüberschussbeteiligung - and not an increment over the guarantee. 2,70 % on the
basepath, one carrier’s 2026 rate for its classic endowment book held level for the whole projection [std]; 1,20 % onlow; 0 onnil. It is insurer-discretionary, revisable annually and may be zero euros, which is why it is a scenario rather than an assumption.The table is keyed by the contractual, 1-based
policy_year, so the lookup goes throughpolicy_year(), clamped at the table’s last row.
- zins_ueberschuss_rate(k)[source]#
z(k): the interest-surplus rate in policy year k,
max(0, d(k) - i1).Derived and never an input. A declared 2,70 % on a 1,00 % guarantee is a 1,70 pp credit, not 2,70 pp on top of 1,00 pp - the single most common way to get this product wrong. The
maxmatters on thenilscenario, where the declared rate falls below the guarantee: the reserve still rolls forward at the fullrechnungszins(), so the surplus is zero and never negative.
- term_rate(k)[source]#
s(k): the Schlussüberschussanteilsatz in policy year k, from the scenario table.
0,40 % p.a. of the Deckungskapital on the
basepath [std] - nothing in the corpus fixes a terminal-bonus level, for any insurer, in any year. It accrues on the same base as the interest surplus and is paid at the Ablauf and on death, and not on surrender unlessterm_surr_shareis raised. Keyed by the contractual, 1-basedpolicy_year, so the lookup goes throughpolicy_year().
- ans_rate(k)[source]#
a(k): the Ansammlungszinssatz in policy year k, from the scenario table.
2,70 % on the
basepath, set equal to the declared rate [std]. That equality matters for one reason: becauseans_rate > rechnungszins, the verzinsliche Ansammlung out-accumulates the Bonussystem at maturity while the Bonussystem pays more on an early death. Setting it equal to the guarantee would destroy that asymmetry. Keyed by the contractual, 1-basedpolicy_year, so the lookup goes throughpolicy_year().
- surplus_base_pp(k)[source]#
The Deckungskapital the year-k surplus rates are applied to, per policy.
max(res_pp_at(k, "AFT_INT"), 0): the closing guaranteed reserve of the year, after that year’s interest and mortality and before this year’s surplus - the reserve “calculated at the allocation date”, which the sources put at the Bilanzstichtag.The
maxis load-bearing wherever the base is negative, and a gezillmerte Deckungskapital is negative at issue. A positive rate on a negative base credits a negative surplus - so a contract whose Zillmerung is not yet recovered earns no interest surplus at all, even though the § 153 VVG entitlement runs from inception: economically right, because there is no fund to earn on, and worth saying because it looks like a bug.On the shipped parameters the guard is inert: the base here is the closing reserve, and at a 25 ‰ Zillmersatz over a twenty-five-year premium term that is already positive in the first policy year,
k = 0(570,75 € on the anchor cell against an opening -1 252,53 €). It is not inert at the pre-2015 40 ‰ ceiling, and it is the kind of guard whose absence is invisible until the parameter that needs it arrives.
- surplus_credit_pp(k)[source]#
C(k): the surplus allocated to the contract for policy year k, per policy.
zins_ueberschuss_rate(k) * surplus_base_pp(k). Zero before the frame opens - a defensive floor only: no cells reads it there, becauseprem_offset_pp()returns zero in the first projected year instead of reaching for a predecessor outside the frame.What it is applied to is decided by
surplus_use(), not here: this cells is the amount declared, and the three systems differ in what they do with it.
- term_bonus_pp(k)[source]#
S(k): the accrued Schlussüberschussanteil at the start of policy year k, per policy.
S(k+1) = S(k) + term_rate(k) * surplus_base_pp(k), opening at zero. It is paid at the Ablauf and on death and not on surrender in the base run,term_surr_sharebeing zero - the choice that does not invent an entitlement the sources do not describe. It accrues but never compounds: no source describes interest on an accrued terminal share.
- av_sur_pp(k)[source]#
U(k): the Überschussguthaben per policy at the start of policy year k, in euros.
The verzinsliche Ansammlung balance: it receives declared surplus and never premium. There is no unit fund and no policyholder account fed by contributions in this product, so the house vocabulary’s
prem_to_av_pphas no counterpart here and is not published.Named ``av_sur_*`` and not ``av_*``. Across delib
av_pp/av_pp_at/av/av_atis the principal account balance — the Deckungskapital onRV_DE_S,Index_DE_SandBasis_DE_S, the Fondsguthaben onFRV_DE_S— andav_sur_*is the verzinsliche Ansammlung side account beside it, which isRV_DE_S’s spelling on the one model that carries both. This product’s principal balance is a reserve rather than an account and is published asres_pp/res_zill_pp, soKLV_DE_Spublishes theav_sur_*half of the pair and noav_pp.It is explicitly not part of the Deckungsrückstellung: § 341f HGB forms that provision excluding verzinslich angesammelte Überschussanteile.
- av_sur_pp_at(k, timing)[source]#
The Überschussguthaben per policy at a point inside policy year k.
"BEF_INT"the opening balance,
av_sur_pp(k)."AFT_INT"after the year’s Ansammlungszins,
av_sur_pp(k) * (1 + ans_rate(k)). The balance earns its own interest whatever the current Überschussverwendung is."AFT_CREDIT"after this year’s declared surplus has been added, which happens only under
ansammlung. This is the closing balanceav_sur_pp(k + 1), and it is what a death, maturity or surrender at the end of year k is paid on top of the guaranteed benefit.
- av_sur(k)[source]#
The Überschussguthaben of the whole model point at the start of year k, in euros.
av_sur_pp(k) * pols_if(12 * k): the per-policy balance weighted by the in-force count at the anniversary opening policy year k, which is the quantity a portfolio roll-up consumes. The balance is an annual one and so is the weight; a mid-year in-force count would pair a start-of-year balance with a population that has already decremented.
- av_sur_at(k, timing)[source]#
The aggregate Überschussguthaben at a point inside policy year k.
av_sur_pp_at(k, timing) * pols_if(12 * k), on the in-force at the anniversary opening policy year k. The timings areav_sur_pp_at()’s.
- bonus_si_pp(k)[source]#
Z(k): the bonus sum insured bought out of surplus, per policy, at the start of year k.
Z(k+1) = Z(k) + surplus_credit_pp(k) / pu_single_prem(k+1)under the Bonussystem, and frozen atbonus_si_init()under the other two systems.The bonus sum is paid-up insurance: it raises the death benefit immediately by its full face amount, which is why the Bonussystem pays more on an early death - but it accumulates only at
rechnungszins(), which is why the verzinsliche Ansammlung pays more at the Ablauf.
- prem_offset_pp(k)[source]#
The Beitragsverrechnung offset applied to the year-k Zahlbeitrag, per policy.
min(prem_charged_pp(k), surplus_credit_pp(k - 1))underbeitragsverrechnungand zero otherwise: last year’s declared surplus reduces this year’s premium, floored at zero so that a surplus larger than the premium never becomes a payment to the policyholder. In the first projected year there is no last year, so the offset is zero outright rather than reaching for a predecessor outside the frame.What it reduces is a Zahlbeitrag, not a Bruttobeitrag: the tariff premium is unchanged and the offset is a discretionary rebate the insurer may withdraw without invoking § 163 VVG at all. That is why the renewal commission is charged on
prem_charged_pp()and not onprem_paid_pp().
- prem_charged_pp(k)[source]#
The annual Zahlbeitrag charged per policy in policy year k, before any offset.
prem_gross_pp() * prem_freq_load()whilek < prem_term()and the contract is not beitragsfrei; zero otherwise.mpremiums fall in yearsk = 0 ... m - 1, which is the 0-based reading of “payable over the Beitragszahlungsdauer”. An annual amount:philoads the year’s Bruttobeitrag once, and what is collected in a month isprem_charged_inst_pp().
- prem_paid_pp(k)[source]#
The annual Zahlbeitrag actually paid per policy in year k, after the offset.
prem_charged_pp(k) - prem_offset_pp(k). It differs fromprem_charged_pp()only underbeitragsverrechnung, and the difference is a policyholder rebate rather than a price change - which is why the two are separate cells and why the commission reads the first of them.
- prem_cycle()[source]#
Months between premium instalments: 12 annual, 6 half-yearly, 3 quarterly, 1 monthly.
12 // instalments()— arithmetic of the elected Zahlweise rather than an assumption.
- prem_due(t)[source]#
Whether a premium instalment falls due at the beginning of month t.
duration_mth(t) % prem_cycle() == 0. An annual payer is due in the first month of every policy year and nowhere else; a monthly payer is due in every month.This is what the ``echt`` / ``unecht`` distinction was always about. Under
unechtthe Versicherungsperiode remains the year and the twelve payments are instalments of an annual premium, which is exactly what the Ratenzahlungszuschlag compensates; underechtthe period is genuinely monthly and no loading applies. On the annual grid this model ran on, both readings collected the same amount at the same instant and the whole distinction lived in a multiplier. Here they differ in the frame: model points 4 and 5 are the same monthly contract, and only the Ratenzahlungszuschlag separates them.
- prem_charged_inst_pp(t)[source]#
The Zahlbeitrag instalment charged in month t before any offset, or zero.
prem_charged_pp(duration(t)) / instalments()whereprem_due()makes one due. The instalments of a policy year therefore sum to exactly that year’s annual charge, the loading included:phimultiplies the annual amount once and the division into instalments is what it pays for. Loading each instalment again charges it twice.This, and not
prem_inst_pp(), is the base of the renewal commission: under Beitragsverrechnung the intermediary is paid on the tariff premium, the surplus offset being a policyholder rebate.
- prem_inst_pp(t)[source]#
The Zahlbeitrag instalment actually collected in month t, or zero.
prem_paid_pp(duration(t)) / instalments()whereprem_due()makes one due — the year’s premium net of the Beitragsverrechnung offset, divided into the elected number of instalments. Formed from the annual amount so that the offset is spread over the year’s instalments exactly as the charge is, and the two cannot fall on different cycles.
Beitrag income at the beginning of month t, an inflow.
prem_inst_pp(t) * pols_if(t): the instalment the elected Zahlweise makes due this month, weighted by the in-force entering it. An annual payer contributes the whole year’s Beitrag in the first month of the policy year and nothing in the other eleven; a monthly payer contributes a twelfth each month on a block that has already lost lives, which is where the premium-cessation rule finally bites.Not further multiplied by
(1 - qm): decrements fall at the end of the month, so a life that dies or surrenders in it has already paid that month’s instalment, and applying the premium-cessation rule again here charges it twice.
- lapse_rate(t)[source]#
w(t): the annual surrender rate applied at the end of policy year t.
From lapse_table.csv by the contractual, 1-based
policy_year- so the lookup goes throughpolicy_year()- and [std] throughout: 5,0 % in policy years 1-2, 3,5 % in 3-8, 2,0 % in 9-11, 6,0 % in policy year 12 and 2,5 % from 13. The shape is the one thing the evidence supports - the income-tax half-income rule needs twelve years and age 60 or 62, so surrenders are suppressed approaching duration 12 and spike at it. The levels are not sourced: the only German data are market aggregates that are neither endowment-specific nor by duration, and the headline one counts conversions to beitragsfrei as well as surrenders, so calibrating a surrender decrement to it double-counts.Two overrides. Zero through the whole final policy year,
duration(t) >= proj_len_y() - 1: its end is the Ablauf, so the survivors leave as a maturity - and unlike a term cover this is a real payment decision, a surrender paying the § 169 value while a maturity pays the sum insured plus surplus. The zero covers the year and not merely its last month, because that is what the annual-step model this replaced said of it. 1.0 in the year a *Beitragsfreistellung* election fails the *Mindestversicherungsleistung* test, where § 165 VVG turns the election into a surrender and the whole cohort leaves; that override is a statutory consequence and not a behavioural rate, and it is the only place the shipped table is departed from.This is the annual rate of the policy year containing month
t;lapse_rate_mth()is what the recursion applies, and it is where the election year’s 1.0 is placed on the anniversary rather than spread.
- bfz_fails()[source]#
Whether a Beitragsfreistellung was elected and failed the minimum-sum test.
bfz_year() > 0 and not is_paid_up(bfz_year()): the election was made and the beitragsfreie Versicherungssumme it bought fell short ofbfz_min_si, so § 165 VVG obliges the insurer to pay the § 169 value instead and the election becomes a surrender. Named because two cells need the same test and a monthly grid makes them read it at different arguments —lapse_rate()at a month,is_paid_up()at a policy year. Model point 12 is the failing cell.
- lapse_rate_mth(t)[source]#
wm(t): the surrender rate applied at the end of month t, after the mortality one.
1 - (1 - w(t))^(1/12)on the policy year’s annual rate [std], derived geometrically and not by dividing by twelve, so that twelve months of it compound back to exactly that rate.One month is excepted, and it is a statutory event rather than a rate. Where a Beitragsfreistellung election fails the Mindestversicherungsleistung test, § 165 VVG turns it into a surrender — and the election falls at the end of the election policy year, not spread across it. So the whole cohort leaves in that year’s last month and nowhere else, which is what
1.0 if is_anniv(t) else 0.0says. Spreading an annual rate of 1.0 geometrically would put the entire cohort out in the year’s first month, eleven months before the election it models.
- storno_rate(k)[source]#
sigma(k): the Stornoabzug rate on a surrender at the end of policy year k.
10 % of the guaranteed value in policy years 1-5, 7,5 % in 6-10, 5 % in 11-15 and 2,5 % from 16 [std] - contractual, 1-based policy years, so the lookup goes through
policy_year()- against an observed range of 5 % to 20 % of the Deckungskapital from one carrier, under collective action and a BGH remittal.§ 169 Abs. 5 VVG permits a deduction only where it is vereinbart, beziffert and angemessen, and a deduction for noch nicht getilgte Abschluss- und Vertriebskosten is unwirksam - which is what stops an insurer recovering through the deduction what the five-year spreading denies it. It bites on the guaranteed value only; see
surr_value_pp().
- pols_if(t)[source]#
l(t): the number of policies in force at the start of month t.
pols_if_init()att_start(), thenl(t+1) = l(t) (1 - qm(t)) (1 - wm(t))on the best-estimate monthly rates. This is the weight on every cash flow of the sameresult_cf()row.Because both monthly rates compound back to their policy year’s annual rate, twelve months of this recursion collapse to
l(t+12) = l(t) (1 - q(t)) (1 - w(t))— the annual-step recursion this replaced, term for term — so the in-force at every policy anniversary is the annual model’s own figure,pols_if(12k)here equalling itspols_if(k)to floating point. That is what leaves the whole annual layer of this product — the three reserves, the § 169 value, the surplus ledgers, the paid-up purchase — unmoved by the conversion.A contract made beitragsfrei stays here: § 165 VVG keeps it in force with a reduced sum insured, and only a Kündigung removes it.
pols_if(proj_len())is defined and is the maturing cohort; it is read bycheck_pols_roll_fwd()andcheck_decrement_closure()and weights no cash flow.
- pols_if_at(t, timing)[source]#
The number of policies in force at a point inside policy year t.
"BEF_DECR"l(t), the start of the year before any decrement - the same number as
pols_if()and the weight on that year’s cash flows."AFT_MORT"after the mortality decrement,
l(t) (1 - qm(t)). This is the population the surrender rate is taken from and the population that matures att = proj_len() - 1, which is why the two exits cannot both be applied to it in the last month."AFT_LAPSE"l(t+1), the end-of-month state. Through the final policy year
lapse_rate()is zero, so in the last month this equalspols_maturity().
- pols_death(t)[source]#
l(t) qm(t): expected deaths in month t, claimed at the end of the month.
On the best-estimate monthly mortality, not the first-order one, so a claim now falls in the month it happens rather than at the anniversary. The decedent has already paid whatever instalment fell due in advance at the start of that month - which is what “premiums cease on death” means on a grid with premiums in advance.
- pols_lapse(t)[source]#
Expected surrenders at the end of month t, from the survivors of that month’s mortality.
pols_if_at(t, "AFT_MORT") * lapse_rate_mth(t). They are paidsurr_value_pp(), which on this product is a real and often large amount - unlike a term cover, where a lapse pays nothing. Zero through the final policy year, where the survivors leave as a maturity instead.
- pols_maturity(t)[source]#
Policies reaching the Ablauf at the end of the last projected month; zero before it.
pols_if(N) * (1 - mort_rate_mth(N))atN = proj_len() - 1- the survivors of that month’s mortality, all of them, becauselapse_rate()is zero through the whole final policy year. They take the Erlebensfallleistung.
- av_sur_close_pp(t)[source]#
The Überschussguthaben per policy standing at the end of month t, in euros.
av_sur_pp(duration(t) + 1)in an anniversary month andav_sur_pp(duration(t))in every other — the balance a claim leaving at the end of that month is actually paid.This is the rule for every time-varying benefit leg on this model, and it is what the monthly grid is for. The Überschussdeklaration is an annual act: § 153 VVG’s entitlement is settled once a year, the rates are declared for a Versicherungsjahr and the credit lands at the anniversary. A death or surrender in March is therefore paid the balance that was standing at the last anniversary, not the one the coming anniversary will produce. The annual-step model this replaced could only pay a March exit the December balance, which is a forward-looking payment at a date it is not yet due; it did so because on that grid the exit and the crediting were the same instant.
The documented alternative — a pro rata temporis accrual, crediting a twelfth of the year’s declared surplus in each month — is a variant and not the base: no retrieved German wording describes one, and adopting it would put an unsourced accrual rule inside the benefit. It would reconcile at the anniversary just as exactly, the year’s credit still summing to
C(k), so only the sources decide between them.
- bonus_si_close_pp(t)[source]#
The bonus sum insured per policy standing at the end of month t, in euros.
bonus_si_pp(duration(t) + 1)in an anniversary month andbonus_si_pp(duration(t))otherwise, onav_sur_close_pp()’s rule: the Bonussystem buys its paid-up cover out of the year’s declared surplus, so the purchase falls at the anniversary too.
- term_bonus_close_pp(t)[source]#
The accrued Schlussüberschussanteil per policy at the end of month t, in euros.
term_bonus_pp(duration(t) + 1)in an anniversary month andterm_bonus_pp(duration(t))otherwise. The Schlussüberschussanteilsatz is declared for a Versicherungsjahr and accrues on that year’s closing Deckungskapital, so it lands at the anniversary like the rest.
- res_guar_close_pp(t)[source]#
G(t): the § 169 VVG guaranteed value standing at the end of month t, per policy.
res_guar_pp(duration(t))in an anniversary month andres_guar_pp(duration(t) - 1)in every other — the value struck at the last anniversary, which is what an administration system holds and what a mid-year surrender is quoted.§ 169 Abs. 3 VVG strikes the value “zum Schluss der laufenden Versicherungsperiode”, and § 12 VVG makes that period follow the Zahlweise, so on a monthly-paying contract the statute would strike it monthly. The model does not, and says so rather than interpolating: a monthly § 169 value needs a monthly Deckungskapital, and the tariff defines the Rechnungsgrundlagen der Prämienkalkulation on an annual Rechnungszins and an annual first-order table. What the monthly grid does remove is the opposite and worse error, which the annual grid had to make — paying a surrender in the first month of a policy year the value that year will close at.
In the first eleven months of the contract
res_guar_pp(-1)is the § 169 value at issue, which on a gezillmert contract is exactly zero: a surrender inside the first policy year is paid the accumulated Überschussguthaben and nothing guaranteed, which is the consumer fact this product is best known for.
- benefit_full_pp(t)[source]#
The full death benefit per claim in month t, before the § 161 VVG substitution.
The guaranteed Todesfallleistung plus the three surplus balances standing at the end of the month: the Überschussguthaben, the bonus sum insured and the accrued Schlussüberschussanteil, each through the closing rule of
av_sur_close_pp(). The surplus is added to the death benefit whole - the two benefits of a gemischte Versicherung differ only in their guaranteed leg.The guaranteed leg carries no timing question at all: it is a sum, not a balance. A beitragsfrei contract’s is
bfz_si_pp() * death_ratio()instead ofsum_death().
- benefit_death_pp(t)[source]#
What a death claim in month t actually pays, per claim, in euros.
benefit_full_pp()from policy year 4 (duration(t) >= 3) onwards. In policy years 1 to 3 — the first thirty-six months — the § 161 VVG Selbsttötung rule applies to a sharesuicide_shareof deaths: the insurer is leistungsfrei and must nevertheless pay the *Rückkaufswert* including *Überschussanteile* under § 169. The German rule is a benefit substitution, not a forfeiture - materially unlike art. L. 132-7 of the French code, where the cover is of no effect in the first year and there is no surrender value to fall back on.The window is measured in whole years from conclusion, so its boundary falls on an anniversary and
duration(t) < 3is the same statement ast < 36: the monthly grid resolves it exactly rather than approximately.suicide_share = 0.02[std] stands for “about one death in fifty inside the window is an excluded suicide”; no source gives a suicide share of deaths at any age. Setting it to zero is a defensible variant. Paying nil on the excluded share is not.
- benefit_maturity_pp(t)[source]#
What the Ablauf pays per surviving policy at the end of the last projected month
t = proj_len() - 1; zero before it.The guaranteed Erlebensfallleistung plus the three surplus balances, plus the Beteiligung an den Bewertungsreserven at
bwr_rateon the guaranteed value. The last projected month is an anniversary month, so every closing balance here is the year’s closing one and the Ablauf is unchanged by the conversion.bwr_rate = 0in the base run [std]: § 153 Abs. 3 VVG allocates half the Bewertungsreserven determined on termination, but § 139 VAG permits participation only to the extent they exceed the Sicherungsbedarf arising from contracts with an interest guarantee, and that need has routinely exhausted them. The parameter exists so the reasoning is visible and reversible.A beitragsfrei contract matures on
bfz_si_pp()in place ofsum_assured().
- surr_value_pp(t)[source]#
RK(t): the Rückkaufswert payable per policy on a surrender at the end of month t.
res_guar_close_pp(t) * (1 - storno_rate(duration(t))) + av_sur_close_pp(t) + term_surr_share * term_bonus_close_pp(t)— every leg struck on the balance standing at the end of the month of exit, which is the ruleav_sur_close_pp()states.The *Stornoabzug* bites on the guaranteed value alone: the published deduction is a percentage of the Deckungskapital, so the accumulated Überschussguthaben passes through undeducted. Its band is a policy-year band and steps on the anniversary.
term_surr_share = 0in the base run, the accrued Schlussüberschussanteil being payable at the Ablauf and on death and not on surrender; the parameter is exposed rather than hard-coded because that choice would move surrender values most.This is also what a § 161 VVG suicide inside three years is paid, and what a failed Beitragsfreistellung election is paid under § 165 VVG — the latter falling in the election year’s last month, where the closing rule makes it the year’s own § 169 value.
- claims(t, kind=None)[source]#
Benefit outgo in month t, by kind; the total when kind is omitted.
"DEATH"pols_death(t) * benefit_death_pp(t): the guaranteed Todesfallleistung plus the surplus balances standing at the end of the month, with the § 161 VVG substitution of the Rückkaufswert on the suicide share in the first thirty-six months."MATURITY"pols_maturity(t) * benefit_maturity_pp(t), nil except att = proj_len() - 1: the Erlebensfallleistung plus the surplus balances."LAPSE"pols_lapse(t) * surr_value_pp(t): the Rückkaufswert. Unlike a Risikolebensversicherung, where a lapse pays nothing, this is a real and often large outflow, and in the last month the distinction between it and a maturity decides a payment rather than only a label.
- inflation_factor(k)[source]#
The expense inflation factor in policy year k:
(1 + expense_infl)^k[std].Measured from issue, not from the valuation date, so an in-force model point opens on the inflation its duration has already accumulated - and because
kis the 0-based year from issue, the exponent iskitself and is 1.0 in the first policy year. 1,8 % p.a. is a placeholder.
- claim_expenses(t)[source]#
The claim handling expense on the month’s exits [std].
120 € per death, maturity or surrender claim, uninflated. Named separately because it is the only expense line that scales with claims rather than with policies, and it is inside
expenses().
- expenses_pp(t)[source]#
The per-policy expense in month t, in euros, excluding claim handling [std].
The 300 € acquisition expense at issue - only in month
t_start()and only for a new-business point, an in-force point having incurred it long ago, and a single amount rather than a twelfth of one - plus one twelfth of the 45 € annual maintenance expense, inflated to the policy year the month falls in. A policy that runs a full year therefore carries the same annual maintenance charge it did on the annual grid, but it is borne by the in-force of each month rather than of the anniversary, which is what makes a decrementing block cost less.All levels are placeholders: no charge level of any kind was established for any German carrier, and the levels shipped are sized so that the first-year acquisition outgo modestly exceeds what the Zillmerung recovers, so that the anchor cell carries the new-business strain a real German endowment carries.
- expenses(t)[source]#
Total insurer expense outgo in month t, excluding commission [std].
expenses_pp(t) * pols_if(t) + claim_expenses(t).The deliberate difference from the frlib chassis, where commission sits inside the expense column and is published beside it: here
commissions()is a separate line, so the six flow columns ofresult_cf()sum tonet_cf()rather than double-counting the commission. Whichever convention a model takes, taking both at once is the error.This is the rule on every delib model that has a commission to publish —
Basis_DE_S,Riester_DE_S,RLV_DE_SandFRV_DE_S— soexpensesmeans the same quantity across the library.BU_DE_Sis the one model that names no commission at all: its acquisition cost is a single [std]acq_ratewith nothing inside it to separate, and its docstring says so rather than implying a split.
- commissions(t)[source]#
Commission outgo in month t [std], excluded from
expenses().2,5 % of the Beitragssumme at conclusion - anchored to the 25 ‰ § 4 DeckRV ceiling and to one carrier’s reported 25 ‰, a single amount in month
t_start()- plus a 1,5 % Bestandsprovision on each Bruttobeitrag instalment from the second projected policy year. Neither term applies att_start()on an in-force point: it was paid at conclusion, long before the frame opens.The rate is a policy-year rate and steps on the anniversary; the base is the instalment actually charged, so a fractionated payer earns the renewal commission in instalments too. It is charged on
prem_charged_inst_pp()and not onprem_inst_pp(): under Beitragsverrechnung the intermediary is paid on the tariff premium, the surplus offset being a policyholder rebate rather than a price reduction.
- net_cf(t)[source]#
The net liability cash flow of month t, income positive.
Beiträge less death, maturity and surrender claims, less expenses, less commission - each subtracted exactly once,
expenses()excluding commission by construction. The notes’ own sign and the library-wide one.The shape to expect on the anchor cell is a first year that very nearly washes - +320,89 €, the year’s Beitrag of 2 004,04 € almost exactly meeting the 1 252,53 € initial commission plus the 300 € acquisition expense - then margins of the order of a thousand euros a year that decay as the cohort lapses, then a single very large negative year at the Ablauf, -28 172,76 €. The new-business strain of this product sits in the reserve, which opens at -1 252,53 €, and not in the cash flow.
- liability_cf(t)[source]#
The same stream as
net_cf(), outgo positive:-net_cf(t)exactly.The orientation a valuation layer consumes: a Solvency II best estimate is
sum v(t) * liability_cf(t)over the relevant risk-free term structure, plus a risk margin. Published as a column ofresult_cf()so the sign convention is verifiable in the frame rather than only in prose.
- check_net_cf_resid(t)[source]#
The cash flow statement residual in policy year t; zero everywhere.
net_cf - (premiums - claims_death - claims_maturity - claims_lapse - expenses - commissions), rebuilt from :func:`result_cf`’s own published columns rather than from the cells behind them. Reading the frame is the point: the identity then holds of what the model actually publishes, so a column dropped, renamed or mis-signed on the way into the frame fails here.The commission is subtracted once:
expenses()excludes it. A model on the frlib convention, where the expense column carries the commission, must not subtract both.
- check_net_cf()[source]#
True when the cash flow statement reconciles in every projected policy year.
This library’s first ruling: every model publishes the identity that reconstructs
net_cf(t)from its own cash flow statement’s published parts, so that the headline number of a cash flow model is not the one quantity nothing checks. No argument, one bool over allt;check_net_cf_resid()gives the signed residual of the year that failed.
- check_pols_roll_fwd_resid(t)[source]#
The in-force roll-forward residual in policy year t; zero everywhere.
pols_if(t) - pols_if(t+1) - pols_death(t) - pols_lapse(t), plus in the final policy year the difference betweenpols_maturity()and the survivors of that year’s mortality. The recursion multiplies(1 - q)(1 - w)while the exits are formed separately, so the two agree by algebra when - and only when - every one of them is read at the samet. What it catches is a misindexed recursion, and in the last year a maturity count that is not exactly the cohort that survived to it.
- check_pols_roll_fwd()[source]#
True when the in-force roll-forward closes in every projected policy year.
No argument, one bool over all
t;check_pols_roll_fwd_resid()gives the signed residual of the year that failed.
- check_decrement_closure_resid(t)[source]#
The cumulative decrement-closure residual at the end of policy year t; zero.
Deaths plus surrenders plus maturities up to
t, plus the survivors carried intot + 1before the Ablauf, less the original cohort. It is built by direct summation over the exit cells, with no reference to the recursion that producedpols_if(), which is what makes it more than the telescope ofcheck_pols_roll_fwd(): it catches a wrong starting cohort, an exit counted in two places, and a maturity that double-counts the final year’s surrenders.
- check_decrement_closure()[source]#
True when deaths, surrenders, maturities and survivors account for the whole cohort.
No argument, one bool over all
t;check_decrement_closure_resid()gives the signed residual of the year that failed. Att = proj_len() - 1it is the notes’ closure identity: the three exit streams sum topols_if_init()exactly.
- check_res_roll_fwd_resid(k)[source]#
The Deckungskapital roll-forward residual in policy year k; zero everywhere.
res_pp_at(k, "AFT_INT") - res_pp(k + 1): the Fackler recursion(V(k) + P^Z + uplift) (1 + i1) = f q1(k) SD + (1 - q1(k)) V(k+1)
computed retrospectively on the left and prospectively on the right. This is the strongest single check in the model: it proves that the premium, the first-order mortality, the interest rate and the prospective reserve formula are mutually consistent, and it fails on a Risikozuschlag applied to the survivorship, a Zillmer premium amortised over the wrong annuity, a reserve read at the wrong duration, and an abgekürzte Beitragszahlungsdauer that keeps crediting a premium after it has stopped.
In the Beitragsfreistellung year the identity it asserts is a different one -
bfz_uplift_pp()is defined to close it - and there it says that the paid-up sum was bought at exactly the § 169 value.It is a policy-year identity and stays one on the monthly grid: an annual Fackler recursion at an annual Rechnungszins, which is what the tariff defines.
- check_res_roll_fwd()[source]#
True when the guaranteed reserve rolls forward in every projected policy year.
No argument, one bool over all policy years
k;check_res_roll_fwd_resid()gives the signed residual of the year that failed.
- check_surplus_roll_fwd_resid(k)[source]#
The active Überschussverwendung ledger’s residual in policy year k; zero.
Under
ansammlung,av_sur_pp(k+1) - [av_sur_pp(k) (1 + a(k)) + C(k)]; underbonus,bonus_si_pp(k+1) - [bonus_si_pp(k) + C(k) / pu_single_prem(k+1)]; underbeitragsverrechnung,prem_offset_pp(k) - min(prem_charged_pp(k), C(k-1)).One check for three ledgers, because exactly one of them is live on any model point - and a model that credits the same surplus to two of them fails here rather than quietly paying it twice. All three are policy-year ledgers and stay so: the declaration is an annual act and the finer grid does not subdivide it.
- check_surplus_roll_fwd()[source]#
True when the live surplus ledger closes in every projected policy year.
No argument, one bool over all policy years
k;check_surplus_roll_fwd_resid()gives the signed residual of the year that failed.
- check_surr_floor_resid(k)[source]#
The § 169 Abs. 3 VVG surrender-floor residual in policy year k; zero everywhere.
The sum of four one-sided violations, each of which can only be negative:
res_guar_pp(k)belowres_zill_pp()atk + 1, belowres_min_pp()atk + 1, below zero, and the Rückkaufswert payable at that year’s anniversary —surr_value_pp(12k + 11)— below zero. The two reserve comparisons are made only while the contract is premium-paying: once it is beitragsfrei the premium-paying constructions describe a contract that no longer exists, and the floor is already inside the paid-up sum that was bought.Near-trivial by construction, since
res_guar_pp()is that maximum - and published for the same reason frlib publishes its gate checks: the rule is written twice, so the two disagree if either is edited. What it guards against is the specific and tempting error of publishing the Zillmer reserve alone as the surrender value, which understates it at essentially every duration on a gezillmert contract.
- check_surr_floor()[source]#
True when the § 169 Abs. 3 floor holds in every projected policy year.
No argument, one bool over all policy years
k;check_surr_floor_resid()gives the signed residual of the year that failed. The Rückkaufswert is also checked non-negative in every month bycheck_surr_nonneg(), because the monthly grid quotes one in eleven months the annual grid never priced.
- check_surr_nonneg_resid(t)[source]#
min(0, surr_value_pp(t)): the Rückkaufswert payable in month t, one-sided.Zero everywhere, and it exists because the monthly grid quotes a surrender value in the eleven months of each policy year the annual grid never priced. Those months are paid on the last anniversary’s § 169 value (
res_guar_close_pp()), which is a different and smaller number than the one the annual model used — and in the first eleven months of a gezillmert contract it is exactly zero. A model that reached for the coming anniversary’s value instead would pay a forward-looking amount; a model that subtracted a Stornoabzug from a zero guaranteed value and forgot the Überschussguthaben could go negative. This says it does not.
- check_equivalence_resid(t)[source]#
The first-order pricing equivalence residual; the same value at every month t.
B (1 - beta) a_m - alpha BS - pv_benefit_1st - gamma SE a_n. It does not depend ont- the equivalence is struck once, at issue - and it carries the argument only so that everycheck_*in this library has the same shape.Note that it charges
alpha_rate() * beitragssumme()and notalpha_cost(): the acquisition cost is in the premium whether or not the contract is zillmered, which is why a gezillmerte and a non-gezillmerte edition of one tariff cost the same.
- check_equivalence()[source]#
True when the first-order pricing equivalence closes.
No argument, one bool;
check_equivalence_resid()gives the signed residual. It is what makesprem_gross_pp()a derived quantity rather than an asserted one, and it is the only check that would fail if the Beitragssumme were formed on the loaded Zahlbeitrag instead of on the Bruttobeitrag.
- check_rechnungszins_cap_resid(t)[source]#
The § 2 DeckRV ceiling residual; zero unless the guarantee exceeds the cohort’s cap.
min(0, hrz_max() - rechnungszins()), the same value at everyt. A parameter invariant rather than a roll-forward identity, and it lives in the model rather than in a build script because a German model point’s cohort is an assumption: a 4,00 % guarantee on a 2026 issue year is not a stress, it is a data error.
- check_rechnungszins_cap()[source]#
True when the contract’s Rechnungszins is within its cohort’s Höchstrechnungszins.
No argument, one bool;
check_rechnungszins_cap_resid()gives the signed shortfall.
- check_zillmer_cap_resid(t)[source]#
The § 4 DeckRV ceiling residual; zero unless the Zillmersatz exceeds the cohort’s cap.
min(0, zillmer_max() - alpha_rate()) + min(0, zillmer_max() * beitragssumme() - alpha_cost()), the same value at everyt: the rate against the ceiling and the zillmered amount against the ceiling applied to the Beitragssumme.It is asserted separately from
check_surr_floor()on purpose. § 4 DeckRV caps how much may be zillmered at all - a cap on the charge - while § 169 Abs. 3 VVG fixes how the acquisition cost is spread for the surrender floor - a floor on the value. Conflating the two is a documented failure mode, and one search summary in the research corpus does exactly that.
- check_zillmer_cap()[source]#
True when the Zillmersatz is within its cohort’s Höchstzillmersatz.
No argument, one bool;
check_zillmer_cap_resid()gives the signed shortfall.
- result_cf()[source]#
Result table of cash flows, indexed by the 0-based policy month t.
pols_ifis the start-of-month count, which is the weight applied to every cash flow on the same row, and its first value ispols_if_init()exactly.expensesexcludes commission, so the six flow columnspremiums, claims_death, claims_maturity, claims_lapse, expenses, commissions
sum to
net_cfwith no double count - which is whatcheck_net_cf()asserts.liability_cfisnet_cfoutgo-positive and is published as the last column so that the sign convention is verifiable in the frame.premiumsis the instalment collected in the month, so on an annual Zahlweise eleven rows in twelve carry a zero there;result_cf_annual()sums the frame into policy years, which is the view the technical notes’ worked example is stated on.The frame runs
t = t_start() ... proj_len() - 1contiguously and stops: the Ablauf falls at the end of the last month and there is no ``t = proj_len()`` row.
- result_cf_annual()[source]#
result_cf()summed into policy years, indexed by the 1-basedpolicy_year.Every cash flow column is the total of that policy year’s twelve months;
pols_ifis the count at the start of the policy year,pols_if(12 k), which is the number the annual-step model this replaced carried on the same row and is unchanged by the conversion. It is the monthly frame regrouped and never a second projection, which is what lets the notes’ annual worked example stay annual and still be asserted cell by cell.pols_ifand the whole annual layer beside it — the three reserves, the § 169 value, the surplus ledgers, the paid-up purchase, the equivalence — are the annual model’s own numbers. The cash flow columns are not, and are not meant to be: claims fall in the month of exit and are paid the balances standing at the last anniversary, maintenance accrues a twelfth a month on that month’s in-force, and a fractionated Beitrag is collected on a block that has already lost lives.
- result_surplus()[source]#
Result table of the surplus machinery and the reserves, indexed by
policy_year.The annual frame, and deliberately so.
decl_rate,zins_ueberschuss_rate,surplus_base_pp,surplus_credit_pp,res_pp,av_sur_ppandterm_bonus_ppare annual state and move once a year; publishing them monthly would repeat each value twelve times and invite a reader to think a Deckungskapital accrues through the year.surr_value_ppis the Rückkaufswert payable at that policy year’s anniversary,surr_value_pp(12k + 11); what a mid-year surrender is paid is inresult_cf()throughres_guar_close_pp(), and is smaller.They are state, not cash flow, which is why they are published here rather than in
result_cf(): a cash flow statement whose columns do not all sum to its bottom line is a statement a reader has to know which columns to skip.Read the first rows of this frame beside the first rows of
result_cf_annual()and the product is visible in two numbers.res_ppopens at-alpha_cost()- the whole of the Zillmerung, unrecovered - whilesurplus_credit_ppis struck on the year’s closing reserve and is therefore small but positive from the first year. The gap between the two columns in the early durations is the entire economics of a German endowment’s first years.