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/basisrente/, 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, 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 Basis_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

surplus_file

surplus_table()

surplus_table.csv

rentenfaktor_file

rentenfaktor_table()

rentenfaktor_table.csv

charge_file

charge_table()

charge_table.csv

behaviour_file

behaviour_table()

behaviour_table.csv

option_file

option_table()

option_table.csv

Every file but ``model_point_table.csv`` carries a final ``provenance`` column, one tag per row — delib’s second ruling, asserted by the conventions suite. A model point is a configuration rather than an assumption, which is why it is the single exemption.

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

mort_table.csv is not DAV 2004 R. DAV 2004 R is the property of the Deutsche Aktuarvereinigung, is not public, and is not redistributed here; it is cited by name in sources.md and in the technical notes, and what ships instead is a shaped proxy:

qx(age) = min(1.0, 0.014000 x 1.085^(age - 67)) over ages 20 to 121, with qx(121) = 1.0 and a flat improvement trend = 0.015 at every age applied from mort_base_year = 2005.

The anchor a replacement must preserve is ``qx(67) = 0.014000`` exactly, because the notes’ worked example converts at attained age 67 and every figure in the payout phase of that example is struck off it. A replacement must also preserve three structural properties, or the model stops being a model of this product:

  • it must be generational — DAV 2004 R is a Generationentafel, with the improvement inside the table rather than applied on top of it, which is why mort_rate_at_age takes a calendar year as well as an age and why cal_year(t) is carried at all;

  • it must be first order, carrying the DAV’s prudential margins, because the guaranteed Rentenfaktor is struck on that basis and the projection’s own mort_be_factor steps down from it to a best estimate;

  • it must run to a terminal age at which qx is 1.0, because proj_len() is derived from the last age in this file and the projection has no tail state.

The 1.085 slope and the 1.5 % flat trend are [std] with nothing behind them: DAV 2004 R’s own trends are age-dependent, and no Basisrente-specific decrement evidence exists anywhere in the delib corpus.

What the other six files are

surplus_table.csv is the insurer’s discretionary path in the Aufschubphase and the Rentenphase, keyed on the projection index t itself, 0-based like the frame: decl_rate is the declared laufende Verzinsung, which in German practice is the total credited rate including the Rechnungszins and not a spread over it, and ann_bonus_rate is the Überschussrente uplift. It is a scenario, not a forecast, and the base path is set above the 1,00 % Höchstrechnungszins so the guarantee does not bind on the anchor.

rentenfaktor_table.csv is the aktueller Rentenfaktor by conversion age and scenario, in euro of monthly annuity per 10 000 € of capital. No Rentenfaktor level, range or time series exists anywhere in the delib corpus, so both this table and the guaranteed factors on the model points are [std]; the low scenario exists so that model point 13 exercises the other branch of max(garantiert, aktuell).

charge_table.csv holds one row per tariff: the four charges struck against the policyholder’s Deckungskapital (the Zillmerung rate, the premium charge β, the reserve charge γ and the Stückkosten), the Schlussüberschussanteil rate, and the insurer’s own expense and commission scale. Two tariffs ship, differing only in zill_rate: 25 ‰ of the Beitragssumme for business written from 1 January 2015 and 40 ‰ for the pre-LVRG in-force cohorts.

behaviour_table.csv holds the two behavioural assumptions that vary by duration — the Beitragsfreistellung rate and the Zuzahlung take-up. Its ``dur`` index is the policy year, ``duration(t) + 1``, so the notes’ “durations 1–5” reads off the file directly; duration(t) itself is completed policy years and is 0 in the first year.

option_table.csv holds one multiplicative factor per contractual option: prem_mode is the Ratenzahlungszuschlag applied to the laufender Beitrag alone, and guarantee_period and survivor are reductions in the Rentenfaktor, because a German tariff pays for those covers out of the annuity rather than by scaling the death benefit.

model_point_table.csv is the thirteen policies, indexed by point_id, which is Projection’s only parameter.

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.

Indexed by point_id. The single input file exempt from delib’s provenance rule: a model point is a configuration — one policy’s own terms — rather than an assumption.

mort_table()[source]#

The first-order annual death rates and improvement trends by age.

Read from mort_table.csv, indexed by age: qx is the rate at the base calendar year mort_base_year and trend the annual improvement that makes the basis generational. A [std] proxy anchored at qx(67) = 0.014000, not DAV 2004 R; see the Space docstring for what a replacement must preserve.

surplus_table()[source]#

The declared surplus path by scenario and projection year.

Read from surplus_table.csv, indexed by scenario_id and the 0-based projection year t, whose first row is t = 0: decl_rate is the declared laufende Verzinsung in the Aufschubphase — the total credited rate including the Rechnungszins, not a spread over it — and ann_bonus_rate the Überschussrente uplift in the Rentenphase.

rentenfaktor_table()[source]#

The aktueller Rentenfaktor by scenario and conversion age.

Read from rentenfaktor_table.csv, indexed by rf_scenario_id and age: rf_curr is euro of monthly annuity per 10 000 € of capital at Rentenbeginn. Entirely [std] — no Rentenfaktor level, range or time series exists anywhere in the delib corpus.

charge_table()[source]#

The charge, expense and commission scale by tariff, from charge_table.csv.

Indexed by tariff_id. The first four columns are deductions from the policyholder’s Deckungskapital and are therefore insurer income; the last five are the insurer’s own outgo. Booking a charge as both is the fourth listed modeling pitfall, and keeping the two groups in one file is what makes the split visible.

behaviour_table()[source]#

The duration-varying behavioural assumptions, from behaviour_table.csv.

Indexed by beh_table_id and dur, where ``dur`` is the policy year, duration(t) + 1. bf_rate is the Beitragsfreistellung rate — the product’s only behavioural exit, and not a lapse — and zuz_take_up the utilisation rate of the Zuzahlung, which is paid out of a profit not known until the year end and is therefore behavioural rather than contractual. Both are [std] with no calibration evidence of any kind.

option_table()[source]#

One multiplicative factor per contractual option, from option_table.csv.

Indexed by option_id and option_key. prem_mode gives the Ratenzahlungszuschlag, which multiplies the laufender Beitrag and nothing else; guarantee_period and survivor give the reduction in the Rentenfaktor that pays for a Rentengarantiezeit and a Hinterbliebenenrente.