The Data Space#

Input data shared by every by-policy projection.

The eight 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/indexpolice/, 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 Index_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

index_return_file

index_return_table()

index_return_table.csv

index_param_file

index_param_table()

index_param_table.csv

surplus_rate_file

surplus_rate_table()

surplus_rate_table.csv

election_file

election_table()

election_table.csv

mort_file

mort_table()

mort_table.csv

lapse_file

lapse_table()

lapse_table.csv

freq_load_file

freq_load_table()

freq_load_table.csv

Every table but the model point table is a [std] construction

Nothing about an Indexpolice — no Cap level, no Partizipationsquote, no declared surplus rate, no charge, no lapse rate — was established for any German carrier: direct HTTP egress was blocked in the build environment and the session’s search budget was exhausted before this product was researched. Every assumption file therefore carries a per-row provenance tag that says so in the library’s own vocabulary, and the tags are overwhelmingly [std]. That is the honest form of the evidence, not a placeholder for better data that was withheld.

mort_table.csv is a [std] Gompertz-form proxy, qx(M, x) = 0.001200 x 1.095^(x - 40) over ages 20-100 with qx(F, x) = 0.65 x qx(M, x). The market bases are DAV 2008 T for death cover and DAV 2004 R, a Generationentafel in attained age and calendar year, for every annuity promise: they are the property of the Deutsche Aktuarvereinigung, are not public, and are cited by name in this library rather than redistributed. The anchor a substitute table must preserve is ``qx(M, 40) = 0.001200`` exactly, so the technical notes’ worked example still closes; the female ratio and the 0.095 log-slope may move freely. Two properties of the real bases the proxy deliberately does not have, and a user replacing it should know which: it is a period table, not a generational one, so it understates a long-deferred annuitisation; and it carries no selection effect. Neither matters much here, because mortality in this model is a timing assumption and not an amount assumption — the death benefit is the account value with a floor, not a sum at risk — but both matter greatly to the Rentenfaktor that the terminal capital buys, which is why the Rentenfaktor is a [std] input rather than a computed one.

index_return_table.csv is the file the whole product turns on: one row per (index_id, t) and twelve monthly returns m01 ... m12, as decimals. Three paths ship, and each is reproducible from its own provenance tag rather than taken on trust:

  • eqidx_vol17 — a broad equity price index, from numpy.random.default_rng(20260829).normal(0.0060, 0.0500, size=(40, 12)) rounded to four decimal places (0.60 % a month at an annualised 17.3 %). Rows ``t = 8`` and ``t = 9`` — policy years 9 and 10 on the 0-based index — are overwritten with the research file’s constructed Example A and Example B, so that the two Indexjahre the mechanic turns on are reproduced by the model rather than restated in prose. The anchors a substitute path must preserve are those two rows: t = 8 must sum, capped at 3 %, to +8.90 %, and t = 9 must sum to -2.60 % while its compounded raw return is +6.4402 %.

  • houseidx_vol5 — the volatility-targeted house multi-asset index, from numpy.random.default_rng(20260830).normal(0.0025, 0.0144, size=(40, 12)) rounded to four decimal places, carrying a 6 % Cap and a 100 % Partizipationsquote in index_param_table.csv because a low-volatility underlying is cheap to buy options on.

  • zero_path — every monthly return exactly zero. Not a scenario but an instrument: it isolates the guaranteed accumulation, makes every Indexjahr credit exactly 0.00 EUR, and lets the Beitragsgarantie floor be tested where it actually binds.

index_param_table.csv carries the monthly Cap and the Partizipationsquote per (index_id, t), so that a path can be repriced year by year without a formula change. The Cap and the declared surplus rate in ``surplus_rate_table.csv`` are not independent parameters — the Cap is the level at which the option strip costs the budget — and the shipped pair (3.00 %, 2.50 %) is not mutually consistent at this volatility. The projection reports the diagnostic index_budget_ratio() for exactly that reason.

election_table.csv carries the Wahlrecht path w(t), the fraction of the year’s declared surplus directed to the index arm: always_index, always_safe, half_half and switch_at_15. It is a behavioural assumption and not a contractual one, and no election distribution for this product family is established.

model_point_table.csv is the one file with no provenance column, and that exemption is the library’s only one: a model point is a configuration — one policy’s own terms — rather than an assumption, and tagging it row by row would repeat the same provenance once per policy. Thirteen points ship; point 1 is the anchor cell of the technical notes’ worked example, and point 8 is an in-force cell whose first projected Indexjahr is t = 8, so it reproduces the research file’s Examples A and B on a 50,000.00 EUR base to the euro.

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.

Thirteen single-policy model points, indexed by point_id, each carrying the twenty-two contract attributes the projection reads. The one input file exempt from the library’s provenance rule, a model point being a configuration rather than an assumption.

index_return_table()[source]#

The twelve monthly index returns of each Indexjahr, from index_return_table.csv.

Indexed by (index_id, t), with value columns m01 … m12 as decimal returns. Wide rather than long because the twelve observations of an Indexjahr live inside one annual step: the contract’s clock is annual, and this is what keeps Index_DE_S an _A model while the genuinely unit-linked FRV_DE_S is _S. All three shipped paths are [std]; see the Space docstring for how each was constructed and which rows a replacement must preserve.

index_param_table()[source]#

The monthly Cap and the Partizipationsquote by index and year.

Read from index_param_table.csv, indexed by (index_id, t): cap is the ceiling C applied to each month’s return before the twelve are summed, quote the fraction q of the year’s compounded movement in the alternative payoff design. Both are per year, because the insurer redetermines them for each Indexjahr; the shipped paths hold them level only because no level for any carrier and any year was established.

surplus_rate_table()[source]#

The declared Überschussanteilsatz by year, from surplus_rate_table.csv.

Indexed by the model’s 0-based t, so its first row is t = 0, the first policy year. This rate is the option budget: for a contract in the index arm the same declared amount that a classic contract would receive as interest is spent on the option package instead. It is exogenous here — the model consumes a declared rate and does not derive one from an investment result under the MindZV minimum.

election_table()[source]#

The Wahlrecht election paths, from election_table.csv.

Indexed by (elect_id, t); w is the fraction of that year’s declared surplus directed to the Indexbeteiligung, the remainder being credited as sichere Verzinsung. w in [0, 1] rather than a binary flag, because some tariffs permit a partial election and all-or-nothing is then the special case.

mort_table()[source]#

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

A [std] Gompertz-form proxy anchored at qx(M, 40) = 0.001200, not a DAV table; see the Space docstring for what it is, what it is not, and what a replacement must preserve. Sex selects the best-estimate row only and is never a rating factor.

lapse_table()[source]#

The base surrender rates by year, read from lapse_table.csv.

Indexed by the model’s 0-based t, so its first row is t = 0, the first policy year. These are the rates before the terminal-year override: in the final period the projection applies zero, because the end of that period is Rentenbeginn and the survivors leave as maturities rather than as surrenders. The step at t = 11 (policy year 12) is the § 20 Abs. 1 Nr. 6 EStG tax threshold and is the shape’s whole point; the levels are [std].

freq_load_table()[source]#

The Ratenzahlungszuschlag multipliers by payment frequency.

Read from freq_load_table.csv, indexed by prem_freq: the surcharge for paying other than annually, as a multiplier on the annual-mode premium. It multiplies the premium collected and does not enter the Beitragssumme, so it changes neither the acquisition charge nor the Mindesttodesfallschutz floor.