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 policies are projected.

Inputs are external files: plain CSVs in the model folder’s parent directory, products/eurocroissance/, 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 EC_FR_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_table_file

lapse_table()

lapse_table.csv

scenario_table_file

scenario_table()

scenario_table.csv

tec_curve_file

tec_curve()

tec_curve.csv

Two of these are scenario files rather than assumption files, and that is a product statement. The A. 134-1 discount rate is 90% of the taux de l’échéance constante at the remaining maturity, so the level and the slope of the TEC curve drive the provision mathématique directly: in the notes’ worked example a 150 bp fall in the TEC adds 587.44 to the provision mathématique at the anniversary closing policy year 6 — pm(71) on the model’s 0-based policy-month index — more than twice the year’s time effect. A model that carried a flat TEC assumption in a Reference would not be modelling this product’s dominant risk, so the curve is a table with a maturity dimension and Projection.tec_rate interpolates across it exactly as the article requires.

The projection steps monthly, and the two time-keyed tables are read accordingly: a table keyed by the contractual policy year is read at Projection.policy_year(t), and the TEC curve — published once per elapsed year — is read at the most recently published row while the maturity it is interpolated at shortens month by month.

Every decrement table shipped here is a [std] proxy and says so in its own provenance column. The regulatory tables the code points to — TH 00-02 / TF 00-02 for non-annuity contracts, TGH05 / TGF05 for annuities, applied under art. A. 335-1 with the annexed décalages d’âge — are cited by name and arrêté in sources.md and never shipped; art. A. 132-18 also permits an insurer’s own table certified by an independent approved actuary, so no single market basis exists to ship.

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.

mort_table()[source]#

The base annual mortality rates by sex and âge atteint, from mort_table.csv.

A [std] Makeham proxy shaped like the INSEE quotients de mortalité, which are the only freely redistributable French mortality series. The homologated tables TH 00-02 / TF 00-02 are cited by arrêté and never shipped, and A. 132-18 permits an insurer’s own certified table besides, so there is no single market basis to reproduce. Anchored so that Projection.mort_be_factor applied to the male age-57 rate — the worked example’s entry age — gives exactly 0.5000% p.a. Sorted on read, because Projection.mort_rate indexes into it.

lapse_table()[source]#

The base annual rachat rates by policy year, from lapse_table.csv.

Keyed by the contractual policy year, 1-based and running from 1 to 40, so the projection reads row Projection.policy_year(t) = t // 12 + 1 for policy month t. Both columns are annual rates and reach the month through Projection.lapse_rate_mth and Projection.wd_rate_mth, which convert them at 1 - (1 - w)^(1/12).

Two columns: lapse_rate, the full surrender (rachat total), level at 2.5% p.a.; and wd_rate, the partial surrender (rachat partiel), 6% of the provision in policy years 1-2 and 3% thereafter. Both are [std] — the published mémoire observes 2%-3% and 6% then 2%-4%, and no eurocroissance lapse experience exists beyond it, because the product is too small and too young to have any. The dynamic overlays layered on the full-surrender rate in Projection.lapse_rate matter more than the level does: a saver who surrenders a 2° engagement while its guarantee is in the money gives up the entire guarantee, which is the strongest exit deterrent in the product.

scenario_table()[source]#

The gross asset return by scenario and projection year, from scenario_table.csv.

Keyed by the elapsed year end at which the return is credited: row k is the return earned over the year ending k years after issue, so policy month t, whose policy year ends duration(t) + 1 years after issue, reads row duration(t) + 1. Row 0 is the inception placeholder and is never read. It is an annual return, spread over its twelve months by Projection.asset_return_mth at (1 + r)^(1/12) - 1.

Net of asset management fees (0.20% equities, 0.10% bonds), which is the basis the notes quote r(t) on. Five paths: shock is the worked example’s — 4.00% through policy year 5, -25.00% in policy year 6 and 6.00% after — and the others are the flat and stressed paths the remaining model points run on. A scenario rather than a best estimate: the maturity guarantee is a put on the auxiliary account and its cost is convex in the asset shock, so a deterministic run understates it and the mémoire duly runs 1 000 risk-neutral scenarios.

tec_curve()[source]#

The TEC term structure by scenario, projection year and maturity, from tec_curve.csv.

Keyed by the elapsed year since issue — one published curve per year — which is how Projection.tec_rate reads it: for a month boundary m, m = 0 at issue, the row is m // 12, the most recently published curve, and a month t splits a beginning-of-month versement at boundary t and strikes its provisions at boundary t + 1.

The taux de l’échéance constante at maturities 1, 2, 5, 10, 20 and 30 years. Projection.i_pm takes 90% of the rate at the remaining maturity, interpolating linearly between the bracketing maturities and holding the longest rate beyond the curve, with a floor at zero. On a monthly grid that remaining maturity is fractionalProjection.rem_term(m) = (proj_len() - m) / 12 years — so the interpolation moves month by month while the curve row moves only at anniversaries: the provision mathématique’s time effect accrues monthly and its rate effect lands whole on the anniversary. The haircut, the interpolation and the floor are art. A. 134-1; reading the index maturity as the remaining term is [std], and Projection.tec_rate says why. Levels are [std] too: the ACPR’s revaluation study records the 10-year OAT averaging 3.0% in 2023 and 2024, and nothing more precise was retrieved. Sorted on read, because the interpolation walks the maturity index in order.