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.
- 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_factorapplied to the male age-57 rate — the worked example’s entry age — gives exactly 0.5000% p.a. Sorted on read, becauseProjection.mort_rateindexes 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 + 1for policy montht. Both columns are annual rates and reach the month throughProjection.lapse_rate_mthandProjection.wd_rate_mth, which convert them at1 - (1 - w)^(1/12).Two columns:
lapse_rate, the full surrender (rachat total), level at 2.5% p.a.; andwd_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 inProjection.lapse_ratematter 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
kis the return earned over the year endingkyears after issue, so policy montht, whose policy year endsduration(t) + 1years after issue, reads rowduration(t) + 1. Row 0 is the inception placeholder and is never read. It is an annual return, spread over its twelve months byProjection.asset_return_mthat(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:shockis 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_ratereads it: for a month boundarym,m= 0 at issue, the row ism // 12, the most recently published curve, and a monthtsplits a beginning-of-month versement at boundarytand strikes its provisions at boundaryt + 1.The taux de l’échéance constante at maturities 1, 2, 5, 10, 20 and 30 years.
Projection.i_pmtakes 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 fractional —Projection.rem_term(m) = (proj_len() - m) / 12years — 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], andProjection.tec_ratesays 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.