The Data Space#

Input data shared by every by-policy projection.

The seven input CSVs are read here, once per model, and referenced from Projection as data. Projection is parameterized by point_id, so each Projection[N] is a separate ItemSpace with its own cells cache; if the readers lived there, every model point would re-read every file. Holding them in an unparameterized Space reads each file once no matter how many policies are projected.

Inputs are external files: plain CSVs in the model folder’s parent directory, products/kapitallebensversicherung/, rather than data 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. 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.

input_dir() resolves the directory from _model.path.parent at run time, so the model works wherever the repository is checked out. Each table has a filename Reference and a reader Cells:

Reference

Cells

File

model_point_file

model_point_table()

model_point_table.csv

mort_table_file

mort_table()

mort_table.csv

lapse_file

lapse_table()

lapse_table.csv

surplus_rate_file

surplus_rate_table()

surplus_rate_table.csv

cost_file

cost_table()

cost_table.csv

freq_loading_file

freq_loading_table()

freq_loading_table.csv

deckrv_file

deckrv_table()

deckrv_table.csv

Every file but model_point_table.csv carries a final ``provenance`` column, one tag per row — this library’s second ruling, and machine-checked. The model point table is the single exemption, because a model point is a configuration rather than an assumption.

The mortality table is a [std] proxy, and this is its anchor

mort_table.csv is not a fitted or an industry table. It is a Makeham-form proxy, sex-specific over ages 0 to 120:

mort_rate_1st(M, x) = 0.00022 + B * 1.10 ** x
mort_rate_1st(F, x) = 0.00016 + B * 1.10 ** (x - 3)

with the single free constant B fixed by one anchor:

mort_rate_1st(M, 37) = 0.001200 exactly

which is the first-order death rate at the worked example’s entry age. That anchor is why the notes’ twenty-five-year table reproduces to the cent, and it is the one number a replacement table must reproduce for the worked example to survive unchanged. Rates are capped at 1.0 at the top of the age range, which no shipped model point reaches.

The table this proxy stands in for is DAV 2008 T, the market-standard first-order basis for German death-benefit business, derived over 2006–2008 from German insurers’ own policy data, the cleansed insured data covering 60 % of the German market in the Kapitallebensversicherung segment. It is the property of the Deutsche Aktuarvereinigung, is not public and is not redistributed here; it is cited by name. A replacement must preserve four things beyond the anchor: an insured-lives level, materially lighter than the national population table at the working ages; sex-specific base tables, which are the raw material even though a German tariff written since 21 December 2012 may not price on sex; no projected improvement, because for a death cover improvement favours the insurer; and an explicit Sicherheitszuschlag directed upward on the death leg. The proxy carries no selection factors, which DAV 2008 T is understood to have, so a book of newly underwritten lives shows more early deaths here than a real one — stated rather than corrected by a second unsourced factor.

The best-estimate basis is this same table scaled: mort_rate(t) = mort_rate_base(t) * mort_be_factor with mort_be_factor = 0.75, so the first-order table carries a 33 % safety loading. That wedge is the Sicherheitszuschlag, and its systematic release is the Risikoüberschuss. The model does not compute the Risikoüberschuss, and the two bases must not be crossed: mort_rate_base prices and reserves, mort_rate projects.

The other six tables

lapse_table.csv carries two duration-keyed rates that are easily confused. lapse_rate is the surrender decrement — the only voluntary exit modelled — and storno_rate is the Stornoabzug, the percentage deduction from the guaranteed value on a surrender. Both are [std]: the only German lapse data are market aggregates that are neither endowment-specific nor by duration, and the headline one counts conversions to beitragsfrei alongside surrenders, so calibrating a surrender decrement to it double-counts. The shape — suppressed approaching policy year 12 and spiking at it — is what the twelve-year income-tax threshold supports; the levels are not sourced. Its key column policy_year is the contractual, 1-based label and is left that way: the projection’s own index t is 0-based, and Projection.policy_year(t) = t + 1 maps between them.

surplus_rate_table.csv carries three declared-rate paths keyed by scenario_id and the same 1-based policy_year. base is one carrier’s 2026 laufende Verzinsung for its classic endowment book, held level for the whole projection — a modelling choice, not a forecast; low and nil exist so that the sensitivity is exercisable rather than argued, nil resting on the sourced statement that the surplus may be zero euros.

cost_table.csv deliberately carries the first-order tariff loadings and the second-order expense assumptions on the same row, because the difference between them is the Kostenüberschuss. deckrv_table.csv carries both DeckRV ceilings — § 2’s Höchstrechnungszins and § 4’s Höchstzillmersatz — keyed by issue_year, both being cohort facts that travel with the contract for its whole term. freq_loading_table.csv carries the Ratenzahlungszuschlag, which applies only where the sub-annual premium is an instalment of an annual Versicherungsperiode (unecht) and not where the period is genuinely sub-annual (echt).

Cells Descriptions#

input_dir()[source]#

The directory holding the input CSVs: the model folder’s parent.

Inputs are external files, not data stored inside the model, so the model folder is pure formulas. The path is resolved at run time from where the model was read, following annuallife.TradLife_A.

model_point_table()[source]#

The model point table, read from model_point_table.csv.

Fourteen single-policy configurations indexed by point_id. The only input file without a provenance column: a model point is a configuration — one policy’s own terms — rather than an assumption, and that exemption is the only one in the library.

mort_table()[source]#

The first-order annual death rates by sex and attained age, from mort_table.csv.

A [std] Makeham-form proxy standing in for DAV 2008 T, which is not public and is not redistributed here. See the Space docstring for the anchor that fixes it and for what a replacement must preserve.

lapse_table()[source]#

The surrender rate and the Stornoabzug by policy year, from lapse_table.csv.

lapse_rate is the decrement; storno_rate is the deduction the surrender value suffers. Both [std], and they are different quantities — see the Space docstring. The policy_year key is the contractual, 1-based label, read through Projection.policy_year(t) = t + 1 rather than by the 0-based t directly.

surplus_rate_table()[source]#

The declared surplus rates by scenario and policy year, from surplus_rate_table.csv.

decl_rate is the laufende Verzinsung — the total declared rate, from which the interest surplus is derived by subtracting the guarantee, never added on top of it. term_rate is the Schlussüberschussanteilsatz and ans_rate the Ansammlungszinssatz. Three scenarios ship: base, low and nil. The policy_year key is the contractual, 1-based label, read through Projection.policy_year(t) = t + 1.

cost_table()[source]#

The tariff loadings and the expense basis by cost_id, from cost_table.csv.

First order and second order on the same row — alpha_rate, beta_rate and gamma_rate are what the tariff charges, acq_expense, maint_expense, expense_infl, claim_expense, comm_init_rate and comm_renew_rate are what the insurer expects to spend — because the difference between them is the Kostenüberschuss.

freq_loading_table()[source]#

The Ratenzahlungszuschlag by payment frequency, from freq_loading_table.csv.

instalments is the number of payments a year and prem_freq_load the multiplier on the annual Bruttobeitrag. The loading applies only to an unechte unterjährige Zahlweise — an instalment of an annual Versicherungsperiode — and is inert where the sub-annual period is genuine.

deckrv_table()[source]#

The two DeckRV cohort ceilings by issue year, from deckrv_table.csv.

hoechstrechnungszins is § 2’s maximum technical interest rate for new business and hoechstzillmersatz is § 4’s maximum Zillmersatz as a fraction of the Beitragssumme. Both are fixed at conclusion and stay with the contract for its whole term, which is why the German in-force book is a stack of cohorts and why they are keyed by issue_year rather than by projection year.