The Data Space#
Input data shared by every by-policy projection.
The five 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 points
are projected.
Inputs are external files: plain CSVs in the model folder’s parent directory,
products/sofortrente/, 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
Sofort_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 |
improvement_file |
improvement_table() |
improvement_table.csv |
surplus_scale_file |
surplus_scale_table() |
surplus_scale_table.csv |
hoechstrechnungszins_file |
hoechstrechnungszins_table() |
hoechstrechnungszins_table.csv |
Every file but model_point_table.csv carries a final provenance column, one tag
per row, per the library’s second ruling. A model point is a configuration rather than
an assumption, and that is the only exemption.
The decrement tables are a [std] proxy, and here is its anchor
DAV 2004 R and DAV 2004 R-Bestand are the property of the Deutsche Aktuarvereinigung,
are not public, and are not redistributed here. They are cited by name in
sources.md and mort_table.csv ships a constructed proxy in their place, in four
series — {FIRST, SECOND} x {M, F} — over attained ages 50 to 120:
q_base(x) = 1 - exp( -( A + B c^x (c - 1) / ln c ) ), A = 0.0002, B = 1.5e-5, c = 1.10 FIRST/M = 1.250000 x q_base(x) SECOND/M = 1.20 x FIRST/M FIRST/F = 0.795455 x q_base(x) SECOND/F = 1.20 x FIRST/F
q_base is the Gompertz-Makeham law the research file constructs and prints, with life
expectancy 24,29 years at 65 and q(65) = 0.00789, q(75) = 0.02001,
q(85) = 0.05078 — a prudent annuitant shape of the right order for a German
first-order basis, and not DAV 2004 R.
The anchor is the unisex blend. The two sex factors are chosen so that the
mix_male = 0.45 blend of the FIRST series reproduces q_base(x) itself:
0.45 x 1.250000 + 0.55 x 0.795455 = 1.00000025, the exact factor for the female
series being 0.4375 / 0.55 = 0.79545454... and 0.795455 its six-decimal rounding. The
blend therefore reproduces the research file’s own law to 2.5e-7 relative at every age,
which is four orders of magnitude inside the tolerance any published figure is quoted to,
and every annuity factor printed in _research/sofortrente.md can be traced into this
model. Age 120 is the closing row: all four series are set to 1.0 there, so the survival
path reaches zero inside the omega_age = 121 horizon.
improvement_table.csv carries the second half of the surface — a Trendfunktion
proxy lambda_SECOND(x) = 0.0150 to age 70, tapering linearly to zero at 105, with
lambda_FIRST = 1.25 x lambda_SECOND — and the model builds
q(x, sex, cohort, basis) = q_table x (1 - lambda)^(cohort + x - mort_base_year) with
mort_base_year = 2025, so the shipped tables are the period tables of calendar year
2025 and the exponent is the calendar year the life attains age x, less 2025. The
exponent is negative for a cohort attaining an age before 2025 — an in-force point
issued in 2012 reads pre-2025 mortality — and is not floored.
What a replacement must preserve. Three things, and a substitution that keeps only
the first is not a substitution for this table. (i) The generational structure: a
q(x, cohort) surface, not a period table, because a period proxy applied to a
forty-year annuity understates the liability by a margin that dwarfs every other
assumption. (ii) The first-order margin in both dimensions: lighter mortality
(SECOND = 1.20 x FIRST) and a stronger improvement trend
(lambda_FIRST = 1.25 x lambda_SECOND), because prudence in an annuity table must reach
the rate of improvement as well as its level. (iii) The age-adjustment convention —
here, integer attained age last birthday with the closing row at 120 — since a table
supplied with an Altersverschiebung must have it applied before it is read here.
Replacing the level alone leaves the wedge between the two bases wrong, and that wedge is
the systematic Risikoüberschuss this product’s Überschussrente is largely financed
from.
The other two assumption files are levels, not tables. surplus_scale_table.csv gives
the opening Überschussrente share and its annual growth for each of the four
Überschussverwendung forms, all [std]: no Überschussanteilsatz was established at any
carrier for any year. hoechstrechnungszins_table.csv is the statutory rate history a
model point’s tariff rate is checked against, by the contract’s own vintage — a German
in-force book is a stack of cohorts and each carries its own cap.
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 points, indexed by
point_id. The only input file without aprovenancecolumn: a model point is a configuration rather than an assumption, and its columns are one contract’s own terms.
- mort_table()[source]#
The annual death rates by basis, sex and attained age, from mort_table.csv.
Indexed by
(basis, sex, age)withbasisin{FIRST, SECOND}andsexin{M, F}. The unisex series the tariff is struck on is computed from these two atmix_male; it is deliberately not a row, because no real sex-distinct table carries one.A [std] proxy, not DAV 2004 R; see the Space docstring for what it is, what its anchor is and what a replacement must preserve.
- improvement_table()[source]#
The annual mortality improvement rates, from improvement_table.csv.
Indexed by
(basis, age). This is the Trendfunktion of a generational table: the improvement lives inside the mortality surface, keyed by birth cohort, rather than being applied on top of a period rate keyed to the projection year. A [std] proxy; DAV 2004 R’s own trend is not public.
- surplus_scale_table()[source]#
The Überschussverwendung scale by form, from surplus_scale_table.csv.
Indexed by
surplus_form.surplus_init_pctis the Überschussrente at outset as a fraction of the garantierte Rente andsurplus_growthits annual increase, for the four formsnone/konstant/teildynamisch/volldynamisch.Every figure is [std]. No Überschussanteilsatz was established for this product at any carrier for any year; what the corpus establishes is the shape — the constant form highest at outset and flat in intention only, the volldynamic form lowest at outset and rising with each declaration. The four forms are not calibrated to equal present value here, and a user who needs them to be must do that calibration.
- hoechstrechnungszins_table()[source]#
The statutory Höchstrechnungszins history, from hoechstrechnungszins_table.csv.
Indexed by
year_from, withyear_toclosing each band andmax_ratethe cap in force. A contract’s cap is the one in force at its own Vertragsbeginn and stays with it for life, so an in-force model point is checked against its vintage rather than against today’s rate. The cap is an upper bound and not the tariff rate: a carrier may price below it, and one in the corpus is observed doing so.