The Projection Space#

The by-policy projection of the EC_FR_S model.

The Space is parameterized by point_id, so Projection[1] is an ItemSpace projecting model point 1:

>>> Projection[1].result_provisions()   # the worked example, Chassis A
>>> Projection.point_id = 2             # Chassis B, same asset path

The time index

t counts policy months, 0-based, the clock basiclife.BasicTerm_S and the five monthly models of this library run on: t = 0 is the issue month, month t runs from time t to time t + 1, and the frame is range(proj_start(), proj_len()) with proj_len() = 12 x policy_term() the number of policy months projected — 120 on the worked example’s ten-year term. Its last row, proj_len() - 1, is the month that ends at the échéance. A new-business cell opens at proj_start() = 0, where the initial versement net of the entry charge creates the rights and the two provisions are first struck — that is the opening state of month 0, reached as own_assets_at(0, "BOM") and its siblings, and not a row of its own. An in-force cell opens at proj_start() = 12 x duration_ifo: elapsed time is recorded in whole policy years, so the frame always opens in the first month of a policy year (shipped point 7 at t = 48), where the extract’s assets, parts and guaranteed amount are seeded and the provision mathématique is re-derived rather than read.

Everything contractual about this product is nevertheless on an annual cycle, so the policy year is derived and used as a lookup key throughout: duration(t) = t // 12 is the completed policy years at the start of month t, policy_year(t) = duration(t) + 1 is the contractual 1-based label, and the attained age is age(t) = issue_age() + duration(t), stepping on the anniversary rather than on the birthday. The anniversary is the last month of a policy year, is_anniv(t), t 11 (mod 12). The frame is indexed by t; the policy year is never indexed by.

A handful of cells are indexed by a month boundary m rather than by a month: m = 0 at issue and m = 12 n at the échéance, and rem_term(), tec_rate(), i_pm() and disc_factor() are of that kind, so month t reads them at t for its opening and at t + 1 for its end-of-month striking. rem_term(m) = (proj_len() - m) / 12 is the remaining term in years, fractional between anniversaries.

Two speeds: annual assumptions on a monthly grid

The two-speed structure that follows is the library’s convention, asserted by tests/test_model_conventions_fr.py. mort_rate(t), lapse_rate(t), wd_rate(t) and asset_return(t) are the annual rates of the policy year containing month t — the vectors the technical notes tabulate — and mort_rate_mth(t), lapse_rate_mth(t), wd_rate_mth(t) and asset_return_mth(t) are the rates actually applied in the month: 1 - (1 - q)^(1/12) for a decrement and (1 + r)^(1/12) - 1 for a return, so twelve compound back to exactly the annual figure.

A monthly grid is not a monthly product. Every contractual mechanic stays where the contract puts it: the R. 134-3 base 4° parts levy and a scheduled versement in the first month of a policy year; the striking of the compte de participation aux résultats and with it the base 5° performance levy, a free versement and the R. 134-12 apport d’actifs on the anniversary. What the finer grid adds is what A. 134-5 already required and the annual grid could not express: the intermediate re-striking of the diversification provision in every month in which the participation account is not struck, a forward part value for an exit, mortality and rachat falling in the month they happen, and maintenance expense accruing where it is incurred.

Anniversary equivalence, and the three places it does not hold

Because the monthly decrements and the monthly return compound back to the annual ones, because the twelve monthly invest_income sum to the year’s A_a r where no cash moves inside the year, and because the parts levy and the top-up land at the same instant on both grids, every anniversary-dated value is what the annual-step model this one replaced carried on the corresponding rowown_assets, pm, prov_div, parts, part_value, mg, cum_prem_net, insurer_contribution, pgt, pcdd, provision_value, surrender_value, death_value, death_payout, maturity_value, conversion_headroom, gate_revalue_ok, parts_levy and perf_levy at t = 12k + 11, and pols_if at t = 12k. Measured against a pre-conversion snapshot of all eleven shipped model points, the largest absolute difference is 1.3e-10 EUR on figures of order 10,000.

Three things do not reproduce, and each is the point of the exercise rather than a defect:

  • the cash flows, which now fall where they happen — a claim at the end of the month of exit rather than of the year, a versement in its own month;

  • expenses(), where maintenance now accrues at one twelfth a month on the month’s own provision and in-force, and the annual grid’s extra opening-striking charge is gone: -0.2% to -1.3% over a whole projection on the shipped cells, and +0.9% on point 9, whose annual versements step the provision up at the start of each of its first five years;

  • on a cell with wd_factor > 0 — shipped point 5 alone — the asset-fed values, because wd_rate_mth() spreads the rachat partiel instead of taking it all at the start of the year, so more capital earns return early: own_assets at the anniversaries runs 0.21 to 3.05 EUR above the annual model’s, at most 0.031%, part_value at maturity is 25.097 against 25.0768, and total rachats are 0.23% higher. What still matches exactly there is everything whose run-down is purely multiplicative — mg, cum_prem_net, pm, parts and pols_if — because (1 - w_pm)^12 = 1 - w_p.

Input data

Inputs are external files: plain CSVs living in the model folder’s parent directory, products/eurocroissance/, read at run time rather than 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 EC_FR_S folder without its parent’s CSVs produces a model that reads and then fails on first evaluation.

Each table has a filename Reference and a reader Cells, both on Data, reached here through the data Reference:

Reference

Cells

File

model_point_file

data.model_point_table()

model_point_table.csv

mort_table_file

data.mort_table()

mort_table.csv

lapse_table_file

data.lapse_table()

lapse_table.csv

scenario_table_file

data.scenario_table()

scenario_table.csv

tec_curve_file

data.tec_curve()

tec_curve.csv

Naming

Cells names follow lifelib’s basiclife.BasicTerm_S wherever that model has an analogue — pols_* for policy counts, plural nouns for cash flows, *_rate for annual rates and *_rate_mth for the monthly ones derived from them, *_pp for per-policy amounts, claims(t, kind) and claim_pp(t, kind) with an uppercase kind string, and the duration_mth / duration / policy_year triple the other monthly models of this library carry. The technical notes use compact symbols instead. The mapping is:

Notes symbol

Cells

Meaning

engagement_modality

chassis()

euro_and_parts or parts_only

(the 1° test)

is_euro_leg()

True on Chassis A

t

(the cells argument)

Policy month, 0-based

m

(the cells argument)

Month boundary, m = 0 at issue

(none)

duration_mth(t)

Months elapsed at BOM, = t

dur(t)

duration(t)

Completed policy years, t // 12

y

policy_year(t)

Policy year of month t, dur + 1

(the anniversary)

is_anniv(t)

True where t % 12 == 11

(the anniversary)

anniv_mth(t)

Month that ends that policy year

n - m/12

rem_term(m)

Remaining term in years, at m

x

issue_age()

Entry age (âge atteint)

x + dur(t)

age(t)

Age entering the policy year

x + dur(t) + 1

age_anniv(t)

Age attained at the anniversary

duration_ifo

duration_inforce()

Completed years at valuation

(none)

proj_start()

First projected month, 12 x above

n

policy_term()

Years to the échéance

(none)

proj_len()

Months projected, = 12 n

g

guarantee_rate()

Share of net versements guaranteed

P_0

premium_initial_pp(t)

Initial versement, opening state

P_net,0

prem_init_after_charge()

Net of the R. 134-3 1° charge

P(t)

premium_gross_pp(t)

Scheduled versement, BOM of a year

P_net(t)

prem_after_charge_pp(t)

Net of the R. 134-3 1° charge

(free versement)

premium_top_up_gross_pp(t)

Free versement, on the anniversary

(free versement, net)

premium_top_up_net_pp(t)

Net of the entry charge

f_e

entry_charge_rate()

Entry charge, base 1°

f_p

parts_charge_rate()

Parts levy p.a., base 4°

f_perf

perf_charge_rate()

Performance levy, base 5°

f_x

exit_charge_rate()

Exit charge, base 6°

(indemnity)

surrender_indemnity(t)

  1. 132-5-3 indemnity, capped

mg(t)

mg(t)

Guaranteed amount at n

(the steps)

mg_at(t, timing)

mg inside month t

(versement split, BOM)

parts_added_bom(t)

Parts a BOM versement buys

(versement split, anniv)

parts_added_top_up(t)

Parts the free versement buys

(cumulative net premiums)

cum_prem_net(t)

Death-floor base

(opening)

cum_prem_net_at(t, “BOM”)

Its opening value

TEC(rem)

tec_rate(m)

Interpolated TEC at boundary m

i_pm(m)

i_pm(m)

90% of TEC, floored at zero

(discount factor)

disc_factor(m)

(1 + i_pm(m))^-rem_term(m)

r(t)

asset_return(t)

Annual asset return of the year

r_m(t)

asset_return_mth(t)

(1 + r)^(1/12) - 1, the month’s

A(t)

own_assets(t)

Account assets, excluding C(t)

(the steps)

own_assets_at(t, timing)

The asset roll inside month t

pm(t)

pm(t)

Provision mathématique

(opening, pre-versement)

pm_at(t, timing)

PM opening and at the striking

pd(t)

prov_div(t)

Provision de diversification

(opening, pre-versement)

prov_div_at(t, timing)

PD opening and at the striking

N(t)

parts(t)

Number of parts

(the steps)

parts_at(t, timing)

Parts inside month t

u(t)

part_value(t)

Valeur de la part

(opening, pre-versement)

part_value_at(t, timing)

u opening and at the striking

u_min

min_part_value()

Contractual floor on u

L(t)

parts_levy(t)

Levy at the BOM of a year, base 4°

I(t)

invest_income(t)

Financial performance of month t

(year to date)

invest_income_ytd(t)

I accumulated since the year opened

F(t)

perf_levy(t)

Anniversary levy, base 5°

(entry)

entry_charge(t)

Base 1° charge on a versement

C(t)

insurer_contribution(t)

  1. 134-3 outstanding contribution

G(t)

pgt(t)

Provision pour garantie à terme

D(t)

pcdd(t)

Provision collective de div. différée

(apport d’actifs)

apport(t)

  1. 134-12 contribution to the PCDD

(A. 134-3 tests)

gate_revalue_ok(t)

Whether a revaluation is permitted

(A. 134-4 headroom)

conversion_headroom(t)

Parts convertible into PM

(surrender base)

provision_value(t)

pm(t) + pd(t)

(opening)

provision_value_at(t, “BOM”)

Its opening value

surrender_value(t)

surrender_value(t)

  1. 134-5 value

death_value(t)

death_value(t)

The current provision value

death_payout(t)

death_payout(t)

After any garantie décès plancher

rider_claim(t)

rider_claim_pp(t)

The floor’s cost, outside the account

maturity_value(n)

maturity_value(t)

  1. 134-6 amount

(payouts)

claim_pp(t, kind)

Payout per claim by kind

q(x+dur(t)+1)

mort_rate(t)

Annual mortality rate

q_m(t)

mort_rate_mth(t)

The monthly rate actually applied

w(t)

lapse_rate(t)

Annual full surrender rate

w_m(t)

lapse_rate_mth(t)

The monthly rate actually applied

(table)

lapse_rate_base(t)

Table full surrender rate

(partial rachat)

wd_rate(t)

Annual partial surrender rate

w_pm(t)

wd_rate_mth(t)

The monthly rate actually applied

(partial rachat)

wd_pp(t)

Partial surrender paid

(partial rachat, gross)

wd_gross_pp(t)

Taken from the provision

(none)

pols_if(t)

In force at the start of month t

l(t)

pols_if_at(t, “AFT_DECR”)

In force at the end of month t

(none)

pols_if_at(t, timing)

BEF_DECR / BEF_LAPSE / AFT_DECR

(none)

pols_death(t)

Deaths in month t

(none)

pols_lapse(t)

Full surrenders in month t

(none)

pols_maturity(t)

Survivors reaching the échéance

(cash flows)

premiums, claims, withdrawals

Probability-weighted flows

E(t)

expenses(t)

Acquisition and maintenance

charges_taken(t)

charges_taken(t)

Insurer income, reported apart

CF(t)

liability_cf(t)

The notes’ outgo-positive stream

(none)

net_cf(t)

Its negative, income positive

(none)

model_point()

The selected row as a Series

(none)

result_cf_annual()

result_cf() summed into years

result_cf_annual() is the frame regrouped, never a second projection: every cash flow column is the total of its twelve months and pols_if is the count entering the policy year. It exists so the annual-step model this one replaced can be laid beside this one row for row — pols_if and premiums agree on it exactly, and expenses and the claim columns deliberately do not.

Nine names needed care.

pols_if is the count at the start of month t — the exposure every cash flow on that same result_cf() row is weighted by — so result_cf()["pols_if"].iloc[0] is pols_if_init() on every model point. This model first published the notes’ own l(t), the count at the end of the period, under that name, which put the exposure column one period ahead of the flows beside it: a reader dividing claims_death by that row’s pols_if to recover a per-policy payout got a stale answer, and nothing raised. The end-of-period quantity survives unchanged and unrenamed in substance — it is now reached as ``pols_if_at(t, “AFT_DECR”)``, the CashValue_SE timing form this library uses for every other intra-period state — and every number in result_cf() except the pols_if column is what it was before the change. The notes, model.md and the test module index l(t) the same way.

The *_at(t, timing) cells open on ``”BOM”``, the beginning of the month. It was "BOY" while the step was a policy year and moved with the grid; the retired spelling raises rather than being silently accepted, which is the cheapest guard against a half-finished conversion. The other timings — "AFT_LEVY", "AFT_EXIT", "AFT_PREM", "AFT_RETURN", "AFT_PERF", "AFT_STRIKE", "AFT_TOP_UP", "BEF_DECR", "BEF_LAPSE", "AFT_DECR" — are unchanged, because they name a step of the recursion rather than a length of time.

parts_added_bom and parts_added_top_up are the two versement splits, named for where they land rather than for BOY and EOY. They are two different rules, not one rule at two times: the first prices at the opening part value and splits at disc_factor(t), the second prices at the part value just struck and splits at disc_factor(t + 1).

age is the age entering the policy year of month t and age_anniv the age attained at the anniversary that closes it. The mortality assumption is read at age_anniv, which is the notes’ q(x + t + 1): the age rule belongs to the annual assumption and moving it to the entering age would shift every decrement by a year of age and break the anniversary equivalence outright. age is published anyway, because the library’s monthly vocabulary expects it and because the âge atteint convention is stated on it.

pm_ifo appears in the notes’ model point attribute table but is not an input the projection reads. The provision mathématique is re-struck from mg and the current i_pm every month, so an in-force cell that supplied one would be asserting a number the rule already determines. The column is shipped and check_pm_restruck() compares it against the re-strike — which is how a reader discovers that an extract was built by accumulating the PM instead.

lapse_rate is the full surrender (rachat total) and wd_rate the partial one (rachat partiel). They are different events with different consequences: a full surrender removes the policy, a partial one runs the guarantee, the parts and the assets down pro rata and leaves the contract in force. Sharing one name would have merged a decrement with an owner election.

wd_gross_pp and wd_pp differ by the base 6° exit charge — what leaves the provision and what reaches the saver. Both are needed, because the provision run-down keys off the gross amount and the cash flow off the net one.

provision_value is pm + pd, the base of R. 134-5 and R. 134-6. It is the same expression on both chassis because pm is identically zero on Chassis B, so the surrender and death rules are one formula rather than two.

pd is the one notes symbol this model could not keep. pd is pandas in every model in this library, and shadowing it inside the one Space that has to build a DataFrame is not worth a two-letter symmetry, so the provision de diversification is prov_div() here while the provision mathématique keeps pm(), whose symbol was free. The displayed formulas below stay in the notes’ notation; the table above is what maps them.

There is no av_pp_at in this model. A eurocroissance engagement is not an account value: the saver’s rights are a number of parts whose value is common to every engagement of the auxiliary account, plus — on Chassis A — a share of a provision mathématique that is a discounted promise rather than a fund. Naming either of them the library’s account value would assert something false about the contract.

The provision mathématique is re-struck, never accumulated

pm(t) = mg(t) (1 + i_pm(t+1))^-rem_term(t+1)   Chassis A
pm(t) = 0                                      Chassis B

pm(t) is the closing PM of month t, struck at its month end — boundary t + 1, where the remaining term is the fractional (12n - t - 1)/12 years. i_pm(m) is 90% of the TEC, floored at zero (A. 134-1), read here at the remaining maturity — a [std] reading of the article’s index maturity, which tec_rate() sets out. It is not the A. 132-1 maximum technical rate and not the A. 132-3 guaranteed rate ceiling, which are different and stricter objects. Rolling pm(t-1) forward at last year’s rate would silently remove the rate effect: in the notes’ worked example that is +587.44 of the +824.18 move over policy year 6, t = 59 to t = 71, against a time effect of only +236.74.

The monthly re-strike is a decision, and it is [std]. R. 134-2 defines the PM as the guaranteed amount discounted at the A. 134-1 rate, a definition with a value at every instant; A. 134-5 requires the diversification provision to be re-struck at an intermediate value at least monthly, which on Chassis A is impossible without a PM for the residual to be taken against. Holding the PM flat between strikings would make it a step function and push a year of time effect onto the anniversary, so the intermediate part value would be wrong in exactly the direction A. 134-5 exists to prevent. It is the same article being read at a finer frequency as the n - k remaining-term reading, and it is recorded in the same paragraph of the notes. It also makes check_guarantee_funding() hold month by month rather than only at anniversaries.

The finer grid splits the policy-year-6 move in two, which the annual grid could not: the PM accretes from 10,541.34 at t = 60 to 10,738.63 at t = 70 at the unchanged 2.25% — the time effect — and jumps to 11,346.00 at t = 71, where the TEC curve is re-published at 1.00% — the rate effect, +607.37 in one month. The year-on-year +236.74 / +587.44 decomposition is unchanged.

At the échéance the discount factor is 1, so pm(12n-1) = mg(12n-1) identically and the Chassis A guarantee is pre-funded by construction. check_guarantee_funding() asserts it at every t, and it is the model’s headline check.

The monthly re-striking and the annual participation account

L(t) = f_p pd_open(t) 1{t % 12 == 0}  A_a = A_open(t) - L(t) - W(t) + P_net(t)
I(t) = A_a r_m(t)                     F(t) = f_perf max(I_ytd(t), 0) 1{is_anniv(t)}
A(t) = A_a + I(t) - F(t)              N(t) = N_open(t)(1 - f_p 1{BOM})(1 - w_pm) + ...
pd(t) = max(A(t) - pm(t), N(t) u_min)  u(t) = pd(t)/N(t)
C(t)  = max(pm(t) + pd(t) - A(t), 0)

A_open(t) is own_assets_at(t, "BOM") and its siblings: A(t-1) in every month but the first projected one, and in that one the state the initial versement — or the in-force extract — creates. That is where the issue instant lives; it is not a row of the frame.

Two of the six R. 134-3 bases keep an annual rhythm inside this monthly recursion, and for two different reasons. The base 4° levy L(t) falls in the first month of each policy year because product-spec.md states it taken there on the opening part value: it is a contract term, not an assumption, and spreading it would change the cash it takes even though the parts count would compound back. The base 5° levy F(t) falls on the anniversary on the year’s accumulated performance I_ytd because R. 134-3 5° is a levy on the balance of the participation account and R. 134-4 strikes that account at least annually; a monthly levy would tax a positive month inside a losing year, which the asymmetric max(., 0) exists not to do, and would have taken money in policy year 6. A policy exiting between anniversaries therefore pays no performance levy for the part year, which is what the article’s annual striking implies.

The diversification provision takes the residual and stops at the parts’ contractual floor. Where the floor binds, the two provisions together exceed the assets, and the excess is exactly C(t) — the contribution the insurer must make under L. 134-3 to complete the representation. The surrender value therefore exceeds the account’s own assets by exactly ``C(t)`` while the contribution is outstanding: at the anniversary of the notes’ policy-year-6 shock, month t = 71, 12,384.73 paid against assets of 10,250.65. The monthly grid says when that starts, which the annual grid could not: the part-value floor first binds in month 66, and the contribution starts there at 141.96 and climbs to 2,134.08 by the anniversary.

C(t) carries no return to the savers: own_assets() rolls forward from A(t-1), not from pm(t-1) + prov_div(t-1). Rolling the topped-up balance forward would manufacture investment return out of the insurer’s capital, and the shipped worked example is the case that catches it — the policy-year-7 asset roll, month t = 72, starts from 10,250.65 and not from 12,384.73.

The Chassis B surrender value is not guaranteed

This is the single most important product fact. Before the échéance a 2° engagement pays parts × part value and nothing else (R. 134-5). The guarantee bites only at the échéance — the end of the last projected month, t = proj_len() - 1 — and only there does maturity_value() take max(parts × u, mg). At the anniversary of the notes’ policy-year-6 shock, month t = 71, Chassis B surrenders for 9,899.22 — 84.18% of net versements against a guarantee of 11,760.00. An implementation that floors the surrender value at the guarantee, or at the discounted guarantee, is modelling a contract that does not exist. check_own_funds_not_paid() asserts that no benefit before the term exceeds the two provisions.

The monthly grid makes the same point sharper, and honours A. 134-5 in doing so: a saver surrendering in month 65 receives 11,430.63, the striking of the month the request falls in, and not the 9,899.22 the anniversary reports. That is the forward part value the article requires, and delivering it is the one place this conversion removes a documented simplification rather than adding one.

The shortfall against the guarantee is carried instead as the provision pour garantie à terme, pgt() — the insurer’s own funds, computed per auxiliary account on the A. 132-18 tables at a rate at most 90% of the TEC, counting no cash flows other than guarantee maturities and mortality. It sits outside the participation account, it is not part of any benefit, and a model must not “improve” its deliberately narrow basis by adding lapses or expenses to it. Of the article’s two admitted drivers this model implements only the guarantee maturity: pgt() applies no survival factor, a prudent [std] simplification that the cells docstring quantifies. Struck monthly like everything else, it shows the provision being constituted: on the worked example’s Chassis B it first becomes positive in month 68, at 61.48, and reaches 1,446.78 by the anniversary at month 71.

The death benefit is not the maturity guarantee

Chapter IV of the regulatory part contains no death valuation article. The death benefit is the current provision value, and any garantie décès plancher is a complementary guarantee provisioned outside the auxiliary account (R. 134-7). death_payout() therefore floors the payout at cumulative net versements where the model point elects the rider, and rider_claim_pp() reports the difference separately — 1,860.78 on the notes’ policy-year-6 Chassis B death at month t = 71, which is not the account’s money.

The charge bases are not interchangeable

R. 134-3 permits six bases and no others, and base 3° — a levy on the encours of the diversification provision — is available only in an auxiliary account holding no 1° engagements. No base permits a levy on the provision mathématique at all. The recurring charge here is therefore base 4°, a levy in number of parts: parts_levy() is f_p × prov_div_at(t, "BOM") in the first month of each policy year, and parts_at() cancels f_p of the parts there and nothing in the other eleven months. On the worked example’s Chassis A that is 15.64 opening policy year 1, t = 0. An encours levy on pm + pd would have been 78.40 — five times as much, and unlawful in a 1° account.

Behaviour, and what is not modelled

All dynamic shapes are [std]: no eurocroissance lapse experience is public and the product is too small and too young to have any. Three overlays sit on the base full surrender rate:

  • a guarantee-imminent suppression of 0.5 over every month of the two policy years before the échéance on Chassis B, and only while the guarantee is in the money — a saver who surrenders in that state gives up the entire guarantee, which is the strongest exit deterrent the product creates. The in-the-money test is read on the current month’s striking, which is what the behavioural statement says and which on every shipped model point agrees with the anniversary reading in every month;

  • a duration-8 spike of 1.5 over policy year 8 where n > 8, because the assurance-vie annual abattement becomes available at eight years; and

  • a lock-up, where lock_up_years > 0 bars surrender entirely, the L. 132-23 hardship exits not being separately modelled.

All three are multipliers on the annual rate, applied before the monthly conversion, so each grades once a year as the behaviour it stands for does and not once a month. The non-surrender period and the échéance-year exclusion are likewise read on duration(t), not on the month, so the monthly grid opens no month the annual grid closed.

Out of scope, and held at zero or absent by design: the PCDD piloting rule (run the fund at 30 bp above the insurer’s own euro fund and carry the rest to the PCDD) and the PCDD’s release back into the participation account — pcdd() accumulates the R. 134-12 apport d’actifs and nothing else; the conversion of parts into PM under A. 134-4, whose headroom conversion_headroom() computes without exercising; the revaluation of guarantees out of the participation account, whose two A. 134-3 gates gate_revalue_ok() tests without exercising; the rente viagère option at the échéance, which annuity_option_flag() rejects by name; and the statutory arbitrage into an SRI ≤ 2 support that A. 134-6 makes the maturity default.

What a single-policy model cannot say

The part value, the PCDD, the PGT and the apport d’actifs are all account-level quantities. The part value in particular is common to every engagement of an auxiliary account, so savers with different maturities and different guarantee levels in one account earn the same rate and differentiation is possible only through the number of parts. A per-policy model can only approximate that, and the mémoire records that pooling two maturity cohorts in one account produces no mutualisation benefit at all. Model points 1 and 2 are two separate accounts on the same asset path, and their part values duly diverge; two engagements of one account would not.

The deterministic single scenario is the other limit. The maturity guarantee is a put on the auxiliary account, its cost is convex in the asset shock and in the level of rates, and a deterministic run understates it. What this model produces is exactly the per-scenario cash flow vector a market-consistent stochastic valuation consumes.

Cells Descriptions#

model_point()[source]#

The selected model point as a Series.

chassis()[source]#

euro_and_parts (1° engagement) or parts_only (2° engagement).

L. 134-1 defines exactly these two modalities, and R. 134-2 to R. 134-6 give each of them its own provision structure, surrender value and maturity amount. A value outside the pair is a data error and raises rather than defaulting to one of them.

is_euro_leg()[source]#

True on Chassis A, the 1° engagement that carries a provision mathématique.

On Chassis B pm is identically zero, the whole engagement is expressed in parts, and the guarantee is carried by the insurer’s own funds until the échéance.

issue_age()[source]#

x: the entry age of the model point, âge atteint (age last birthday) [std].

A. 335-1 applies the homologated tables with the annexed décalages d’âge rather than fixing a model age basis, so the basis is a standardization.

sex()[source]#

The sex (M / F) of the model point.

A. 132-18 requires the mortality basis to be by sex, so the decrement table is sex-distinct even though the tariff itself may not be.

policy_term()[source]#

n: the years from issue to the échéance, at which the guarantee is payable.

The retrieved market range is 8 to 40 years; the worked example uses 10.

duration_inforce()[source]#

Completed policy years at the valuation date; 0 on a new-business cell.

An in-force cell is seeded from its extract’s assets, parts and guaranteed amount and carries no accumulated provision mathématique — see pm_init().

guarantee_rate()[source]#

g: the share of cumulative net versements guaranteed at the échéance.

100% in the worked example; the retrieved market range is 80% to 100%, chosen by the saver. This is the product’s single largest dial: it sets how much of the account is locked into the guaranteed leg and therefore how much of it can bear risk.

premium_gross_init()[source]#

The initial gross versement paid at issue; nil on an in-force cell.

premium_regular_pp()[source]#

The scheduled annual gross versement paid at the start of each policy year.

Zero on a single-versement cell, which is the ordinary eurocroissance shape.

premium_regular_years()[source]#

The number of policy years over which the scheduled versement is paid.

premium_top_up_pp()[source]#

The free additional gross versement, paid at the end of a stated policy year.

The worked example’s is €2 000.00 at the end of policy year 3, and it is what makes the versement split rule visible: paid immediately after the year’s striking, it buys parts at the part value just struck and raises the guaranteed amount by g times its net amount.

premium_top_up_year()[source]#

The policy year at whose end the free additional versement is paid; 0 if none.

entry_charge_rate()[source]#

f_e: the entry charge on each versement, R. 134-3 base 1° [std].

2.00% in the reference configuration; 4.50% is the only published maximum retrieved for any eurocroissance support. It is deducted before rights are created, which is why the guarantee is a percentage of net versements — see mg().

parts_charge_rate()[source]#

f_p: the recurring levy in number of parts p.a., R. 134-3 base 4° [std].

0.80% p.a., taken at the start of the policy year on the opening part value. Base 4° is a levy in number of parts, not on an encours: R. 134-3 3° permits an encours levy only in an auxiliary account holding no 1° engagements, and no base permits any levy on the provision mathématique.

perf_charge_rate()[source]#

f_perf: the levy on positive financial performance, R. 134-3 base 5° [std].

10% of the policy year’s positive financial-management performance, taken on the anniversary, and nothing at all on a negative one — which is why the worked example’s policy-year-6 performance levy, at month t = 71, is zero.

exit_charge_rate()[source]#

f_x: the exit charge on amounts leaving the account, R. 134-3 base 6° [std].

Zero in the reference configuration — the code permits it and neither retrieved insurer shows one.

surrender_indemnity_rate()[source]#

The indemnité de rachat rate on a full surrender [std].

Zero in the reference configuration. R. 132-5-3 caps it at 5% of the present value of the mutual engagements and permits the contract to charge none at all once it has been in force more than ten years — a permission, not a prohibition. surrender_indemnity() takes that permission up unconditionally [std].

part_value_init()[source]#

u(0): the valeur de la part at the auxiliary account’s inception.

€10.0000 in the reference configuration. It is a denomination rather than an assumption: what matters is the ratio of the part value to its floor, since that is how far the diversification provision can fall before the floor binds.

min_part_value()[source]#

u_min: the contractual minimum valeur de la part, R. 134-1 [std].

€5.0000 in the reference configuration, and nowhere published for any insurer. A debit balance on the participation account may reduce the part value only within the limit of its minimum (R. 134-4), so this level sets the floor of the diversification provision — and therefore both the Chassis A maturity payout and the point at which the insurer must start contributing assets. Without it the worked example’s Chassis A prov_div would go to -1,095.35 at the anniversary of policy year 6, t = 71.

lock_up_years()[source]#

The non-surrender period in years; capped at min(n, 8) by R. 134-5.

Zero in the reference configuration. The L. 132-23 hardship exits are not separately modelled [std].

death_floor_flag()[source]#

Whether the model point carries a garantie décès plancher.

A complementary guarantee provisioned outside the auxiliary account (R. 134-7), floored at cumulative net versements. It is not the maturity guarantee and does not become payable because the saver died before the échéance.

annuity_option_flag()[source]#

Whether the model point elects conversion into a rente viagère at the échéance.

R. 134-6 permits it, and it is out of scope here: an annuity conversion needs the TGH05 / TGF05 generational tables, which are cited by arrêté and never shipped, and a projection that continues past the échéance. No shipped model point elects it, and a cell that did raises rather than being paid a lump sum in silence.

wd_factor()[source]#

The multiplier on the table partial-surrender rate; 0 switches rachats partiels off.

The worked example takes none, which is what lets its provision path be read as the cash flow path.

apport_rate()[source]#

The R. 134-12 apport d’actifs, as a fraction of the diversification provision.

Capped at 10% by the article. It enters the auxiliary account at realisation value and endows the PCDD; it is never credited to the savers’ diversification provision, and switching it on changes no policyholder value by one cent.

apport_year()[source]#

The policy year at whose end the apport d’actifs is made; 0 if none.

decrement_basis()[source]#

table or none: whether mortality and full surrender are applied.

none is not a modelling shortcut but the worked example’s stated configuration: with mort_rate = 0 and lapse_rate = 0 the in-force probability stays at 1 and the per-policy provision path is the cash flow path, which is what makes the notes’ two tables readable as provisions rather than as expected values.

scenario()[source]#

The asset-return path and TEC curve the model point runs on.

Both the return and the discount curve are drawn from the same scenario name, because the two move together: the policy-year-6 double shock in the worked example — months t = 60 to 71, equities down and rates down at once — is what makes the rebalancing visible, and pairing an equity fall with an unchanged curve would understate the provision mathématique by the whole rate effect.

own_assets_init()[source]#

A: the auxiliary-account assets attributable to the policy at the valuation date.

At realisation value (R. 134-8) and excluding any outstanding insurer contribution, which is not the savers’ money. Zero on a new-business cell, where own_assets() seeds from the initial net versement instead.

parts_init()[source]#

N: the number of parts de provision de diversification at the valuation date.

mg_init()[source]#

mg: the guaranteed amount payable at the échéance, at the valuation date.

pm_init()[source]#

The provision mathématique the in-force extract reports; not an input.

It is read by check_pm_restruck() alone. R. 134-2 makes the PM the guaranteed amount discounted at the current A. 134-1 rate, so an extract cannot supply it and a projection cannot roll it forward — it is re-derived every year, and comparing the two is how a reader discovers an extract built by accumulation.

cum_prem_net_init()[source]#

The cumulative net versements at the valuation date: the death-floor base.

pols_if_init()[source]#

Initial number of policies in force; 1.0 on a single-policy model point.

proj_start()[source]#

The first projected month: 12 x duration_inforce().

Zero on a new-business cell, where month t = 0 is the issue month and opens on the initial versement, which creates the rights and strikes both provisions. On an in-force cell duration_ifo is the completed policy years at the valuation date — an elapsed count, already 0-based — so twelve times it is the first month of the policy year the extract opens. Shipped point 7 has duration_ifo = 4 and therefore opens at t = 48. Elapsed time is recorded in whole policy years, so the frame always opens in the first month of a policy year.

proj_len()[source]#

The number of policy months projected: 12 x policy_term().

The exclusive end of the frame, so the frame is range(proj_start(), proj_len()) and the last projected month is proj_len() - 1, the month that ends at the échéance. 120 on the worked example’s ten-year term. The projection stops there because that is where the contract’s guarantee is discharged. A model point electing the rente viagère option would have to run past it, which is why annuity_option_flag() rejects one.

policy_term() stays in years, because the contract states the échéance in years; the 12 lives here and in proj_start().

duration_mth(t)[source]#

Months elapsed from issue at the start of month t; equal to t.

t is 0-based and counts from the contract’s inception even on an in-force cell, whose frame simply opens later at proj_start(), so the identity is trivial - the cells exists so the monthly models in this library share one vocabulary.

duration(t)[source]#

Completed policy years at the start of month t: duration_mth(t) // 12.

0-based, as duration is throughout lifelib: 0 through the first policy year. It is what the versement schedule, the lock-up, the surrender indemnity and the last-policy-year exclusions are read on, all of which the contract states in years.

policy_year(t)[source]#

y: the contractual policy year containing month t; 1 for t = 0..11.

The 1-based label duration(t) + 1, derived from the 0-based t and never indexed by. It is what lapse_table.csv is keyed by - both the rachat total and the rachat partiel columns - and what premium_top_up_t and apport_t name.

is_anniv(t)[source]#

True in the last month of a policy year, duration_mth(t) % 12 == 11.

The policy anniversary, and the date every contractually annual event of this product lands on: the striking of the compte de participation aux résultats and with it the R. 134-3 base 5 deg performance levy, the free versement, and the R. 134-12 apport d’actifs. The base 4 deg parts levy is the exception and falls at the opening of the policy year, duration_mth(t) % 12 == 0, because product-spec.md states it taken at the start of each policy year.

anniv_mth(t)[source]#

The month index of the anniversary that ends the policy year containing t.

12 x duration(t) + 11, which is t itself where is_anniv() is true. It is what a reader lays beside the annual-step model this one replaced: the anniversary month’s closing values are that model’s row for the same policy year.

rem_term(m)[source]#

The remaining term to the échéance, in years, at month boundary m.

(proj_len() - m) / 12, and fractional between anniversaries: A. 134-1’s index maturity really does shorten continuously, so the maturity the TEC curve is interpolated at and the exponent the guarantee is discounted over both move month by month. Zero at m = proj_len(), the échéance, where the discount factor is 1.

A month boundary rather than a month: month t opens at m = t and is struck at m = t + 1, which is the argument disc_factor() is read at for the striking.

age(t)[source]#

x + dur(t): the attained age (âge atteint) entering the policy year of month t.

Age steps on the policy anniversary, not on the birthday and not monthly, which is the âge atteint convention the whole model is built on [std]. The decrement is read one year later, at age_anniv(), because the annual mortality assumption this model converts is the rate of the age attained at the year’s end - see mort_rate().

age_anniv(t)[source]#

x + dur(t) + 1: the age attained at the anniversary that closes the policy year.

The age mort_rate() reads the notes’ q(x + t + 1) at. Keeping the annual assumption on the end-of-year age rather than moving it to the entering age is what makes the in-force at every anniversary reproduce the annual-step model exactly; the age rule is part of the assumption, and the finer grid changes only when within the year the decrement falls.

asset_return(t)[source]#

r(t): the annual gross asset return of the policy year containing month t [std].

Net of asset management fees, which is the basis the notes quote it on. A scenario level rather than a best estimate: the guarantee is a put on the account and its cost is convex in this number, so a deterministic run understates it.

The annual return of the policy year containing month t; asset_return_mth() is the return actually credited in the month. scenario_table.csv is keyed by the elapsed year end at which the return is credited, so the policy year of month t, which ends at duration(t) + 1 years after issue, reads row duration(t) + 1; its row 0 is the inception placeholder and is never read. The last row is held beyond the table.

asset_return_mth(t)[source]#

r_m(t) = (1 + r(t))^(1/12) - 1: the return credited in month t [std].

Derived geometrically from the policy year’s annual return and not by dividing by twelve, so that twelve months of it compound back to exactly that return - which is what makes the account assets at every anniversary identical to the annual-step model’s. On the worked example’s path 4.00% a year is 0.327374% a month, -25.00% is -2.368842% and 6.00% is 0.486755%.

The conversion is [std]: no retrieved French source states a within-year shape for a scenario return, and a constant force is the only shape that leaves the annual figure intact.

tec_rate(m)[source]#

The taux de l’échéance constante at month boundary m, at maturity rem_term(m).

A month boundary: m = 0 at issue and m = 12 n at the échéance, and month t reads tec_rate(t) for its opening and tec_rate(t + 1) for its end-of-month striking.

The curve is read in two speeds, and the split is the point. tec_curve.csv publishes one curve per elapsed year, so the row is m // 12 — the most recently published curve, which is what A. 134-1’s dernier TEC publié says [std]; interpolating a curve level between published years would invent data the file does not contain. The remaining maturity, by contrast, shortens continuously, so the interpolation across maturities runs at the fractional rem_term(m). The consequence is that the provision mathématique’s time effect accrues month by month and its rate effect lands whole on the anniversary.

From A. 134-1: linear interpolation between the two bracketing maturities of the published curve, the longest rate held beyond the end of the curve, and the shortest held below its start — which is what k = n reaches, where the discount factor is 1 and the rate is immaterial. The method choice is irreversible per auxiliary account under the article; this model uses method 1°, the per-engagement one, throughout.

The index maturity is [std]. The article as retrieved fixes it as the holder’s guarantee maturity (method 1°) or the auxiliary account’s 1°-engagement duration (method 2°), and says nothing about how that maturity is re-read at valuation dates after inception. This model takes the remaining term n - k, the horizon the guarantee is actually discounted over; holding n fixed at the original term would discount a one-year promise at a ten-year constant-maturity rate on the échéance. technical-notes.md states the reading and is the source of truth for it; product-spec.md states the article as retrieved. The two readings differ only on a sloped curve, which is what shipped model point 10 exercises.

i_pm(m)[source]#

The A. 134-1 discount rate for the provision mathématique: 90% of the TEC, floored.

max(0, 0.90 x TEC(rem_term(m))) at month boundary m, the 90% haircut and the zero floor from the article and the remaining-maturity reading of n [std] — see tec_rate(). A month boundary, like the curve it reads: month t is struck at i_pm(t + 1). This is not the A. 132-1 maximum technical rate and not the A. 132-3 guaranteed-rate ceiling; those are different and stricter objects that apply to a tariff, while this one is a valuation ceiling for a provision the saver has no right to withdraw at. The zero floor matters where the curve is negative: a model that let i_pm go negative would report a provision mathématique larger than the guarantee it discounts.

disc_factor(m)[source]#

(1 + i_pm(m))^-rem_term(m): the factor that turns the guarantee into the PM.

A month boundary, like the rate it is built from: a beginning-of-month versement splits at disc_factor(t) and the end-of-month striking of month t discounts at disc_factor(t + 1). The exponent is the fractional remaining term in years, so the PM is re-struck at an intermediate value in every month, which is what A. 134-5 requires and an annual grid could not express [std]. It is one at m = proj_len(), the échéance, by construction, which is the whole point — see check_guarantee_funding().

premium_initial_pp(t)[source]#

The initial gross versement, paid at the contract’s inception.

It falls at the very start of the first projected month on a new-business cell — month 0, the issue month — where it creates the rights, and is nil on an in-force cell, whose versement was paid before the valuation date.

It is the first projected month’s opening state rather than a step in its roll forward: own_assets_at(t, "BOM"), mg_at(t, "BOM") and their siblings already carry it net of the entry charge, which is why own_assets_at(t, "AFT_PREM") adds only the scheduled versement. It is nonetheless a cash flow of month t, so it is in total_premium_pp() and reaches premiums(), entry_charge() and expenses() from there.

prem_init_after_charge()[source]#

The initial versement net of the R. 134-3 1° entry charge.

The account’s opening balance on a new-business cell, and g times it is the opening guaranteed amount — see mg().

premium_gross_pp(t)[source]#

P(t): the scheduled gross versement, received in the first month of a policy year.

A contractual annual term, so it lands whole on the policy-year opening rather than being spread: product-spec.md gives no modal factor and no retrieved eurocroissance document shows one, so there is no fractionnement to collect it on. A premium_mode column with sourced modal factors is the natural extension, and is exactly what WholeLife_US_S added when its own sources published the factors.

Payable in the months where duration_mth(t) % 12 == 0 for premium_regular_years() policy years from the first projected one, so the last is the first month of policy year duration_inforce() + premium_regular_years(). On shipped point 9 that is months 0, 12, 24, 36 and 48. The initial versement is not here: it is the opening state of the first projected month, premium_initial_pp().

prem_after_charge_pp(t)[source]#

P_net(t): the beginning-of-month scheduled versement net of the R. 134-3 1° charge.

This is the base of the guarantee. The guarantee is a percentage of versements net of the entry charge, so a 2.00% charge on €12 000.00 of gross versements leaves a guaranteed amount of 11,760.00 and not 12,000.00.

premium_top_up_gross_pp(t)[source]#

The free additional gross versement, paid on the anniversary of a policy year.

premium_top_up_t is the contractual policy year at whose end it falls, so on the monthly grid the test is is_anniv(t) and policy_year(t) == premium_top_up_year() — the single month t = 12 x premium_top_up_year() - 1, 35 on the worked example. Zero is the “no top-up” sentinel and matches no month.

It stays whole on the anniversary because it is a discrete contractual act and because the split rule prices it on the striking it immediately follows, disc_factor(t + 1) and part_value_at(t, "AFT_STRIKE"); on the anniversary that striking is the participation account’s own. A mid-year variant would need a premium_top_up_month column the composite has no basis to populate.

premium_top_up_net_pp(t)[source]#

The free additional versement net of the entry charge.

total_premium_pp(t)[source]#

Every gross versement of month t: the initial one, the scheduled one and any free one.

entry_charge(t)[source]#

The R. 134-3 1° charge taken from the year’s versements: insurer income.

parts_added_bom(t)[source]#

The parts a beginning-of-month versement buys, at the opening part value.

The versement splits under R. 134-2: g P_net discounted at the i_pm of the month’s own opening — the m = t boundary — goes to the provision mathématique and the remainder buys parts at the opening part value. On Chassis B nothing goes to the PM, so the whole net versement buys parts.

parts_added_top_up(t)[source]#

The parts a free versement buys on the anniversary, at the part value just struck.

Paid immediately after the month’s striking, so it is priced on u(t) before the top-up rather than on the opening value, and split at the striking’s own disc_factor(t + 1). In the worked example the top-up on the anniversary that closes policy year 3 — month t = 35 — pays 1 960.00 net, which splits into 1 960.00 x 1.0225^-7 = 1,677.31 of PM and 282.69 of diversification provision, buying 282.69 / 12.8688 = 21.9672 parts on Chassis A — and the whole 1 960.00 buying 1 960.00 / 11.1193 = 176.2694 parts on Chassis B.

wd_rate_base(t)[source]#

The table annual partial-surrender rate of the policy year containing month t [std].

6% of the provision in policy years 1-2 and 3% thereafter; the mémoire observes 6% then 2%-4%. lapse_table.csv is keyed by the contractual policy year, read here at policy_year(t); policy years beyond the table take its last row.

wd_rate(t)[source]#

w_p(t): the annual rachat partiel rate of the policy year containing month t.

The rate the technical notes tabulate; wd_rate_mth() is the rate actually applied in the month. Nil while the decrements are switched off, inside a non-surrender period — policy years 1 to lock_up_years, so duration(t) < lock_up_years() — and in the whole of the last policy year, which ends at the échéance, where the contract is discharged in full instead. That last exclusion is on duration(t) >= policy_term() - 1 and not on the final month: the annual grid never let a rachat out of the échéance year, and restating the test on the month would quietly move a year of exits.

Expressed as a fraction of the provision rather than as a cash amount, because a partial surrender takes the same proportion of every element of the engagement — the parts, the guaranteed amount and the death-floor base all run down pro rata with it.

wd_rate_mth(t)[source]#

w_pm = 1 - (1 - w_p)^(1/12): the rachat partiel rate applied in month t [std].

Derived geometrically and not by dividing by twelve, so that twelve months of it run the engagement down by exactly the annual factor 1 - w_p: 6.00% a year is 0.514301% a month. That is what keeps mg(), cum_prem_net() and parts() — whose run-down is purely multiplicative — identical to the annual-step model at every anniversary.

Spreading is a decision, and it is the one that costs exact equivalence. The sourced basis is a rate on average encours [R13], a partial surrender is an owner election that can be made in any month, and A. 134-5 prices such an exit on the next intermediate value, which the monthly grid now strikes. What it costs is that the exit cash leaves in twelve instalments rather than one, so more capital earns return early: on shipped point 5, the only cell with wd_factor > 0, the anniversary own_assets run 0.21 to 3.05 EUR above the annual model’s, at most 0.031%, and total rachats over fifteen years are 0.23% higher. Gating it to the first month of the policy year at the full annual rate would restore bit-exactness and lose the realism; the technical notes record the choice.

wd_gross_pp(t)[source]#

W(t): the amount a rachat partiel takes out of the provision, before the exit charge.

Taken at the beginning of month t, so it is a fraction of the opening provisions, at the month’s own rate wd_rate_mth().

wd_pp(t)[source]#

The rachat partiel actually paid to the saver, net of the base 6° exit charge.

An owner election, not a claim: the contract stays in force and the engagement continues on the reduced parts and the reduced guarantee. It has its own cash flow column for that reason.

mg_at(t, timing)[source]#

mg at a point inside month t.

"BOM"

the opening guaranteed amount: mg(t - 1) in every month but the first projected one, and in that one g times the initial net versement on a new-business cell or the extract’s mg_ifo on an in-force one. This is where the issue instant lives.

"AFT_EXIT"

after a rachat partiel has run the guarantee down pro rata, at the month’s own wd_rate_mth().

"AFT_PREM"

after a beginning-of-month scheduled versement has raised it by g P_net. This is the amount the month-end striking discounts.

"AFT_TOP_UP"

after any free versement on the anniversary; the closing guaranteed amount, and the same number as mg().

mg(t)[source]#

mg(t): the amount guaranteed at the échéance, as at the end of month t.

g times cumulative versements net of the R. 134-3 1° entry charge, run down pro rata to any rachat partiel. It is constant between versements in a single-policy expected-value projection: full surrenders and deaths remove the policy rather than the guarantee, so they reach the cash flows through pols_if instead.

cum_prem_net_at(t, timing)[source]#

The cumulative net versements at a point inside month t.

"BOM"

the opening base: cum_prem_net(t - 1) in every month but the first projected one, and in that one the initial net versement on a new-business cell or the extract’s cum_prem_net_ifo on an in-force one.

cum_prem_net(t)[source]#

The cumulative net versements at the end of month t: the death-floor base.

Equal to mg(t) / g here, and kept separate because they are different objects: the guaranteed amount is a contractual promise at a stated term and this is the base of a complementary death guarantee provisioned outside the account. They coincide only because g is constant.

parts_levy(t)[source]#

L(t): the R. 134-3 base 4° levy, taken in the first month of the policy year.

f_p x prov_div_at(t, "BOM") in the months where duration_mth(t) % 12 == 0, and nil in the other eleven — a levy in number of parts, valued at the opening part value. It is a contractual annual term and not an assumption: product-spec.md states it “0.80% p.a. of parts, taken at the start of each policy year on the opening part value”, so it lands whole where the contract puts it. Spreading it at (1 - f_p)^(1/12) would leave the parts count unchanged over a year but would change the cash, because the levy is valued on the then-current prov_div.

Base 3°, a levy on the encours of the diversification provision, is available only in an auxiliary account holding no 1° engagements, and no base permits a levy on the provision mathématique at all. On the worked example’s Chassis A the levy opening policy year 1 — month t = 0 — is 15.64; an encours levy on pm + pd would have been 78.40.

invest_income(t)[source]#

I(t): the month’s financial performance, on the balance after its opening steps.

own_assets_at(t, "AFT_PREM") x r_m(t). Twelve of these compound the account up by exactly the policy year’s annual return, and — where no cash moves inside the year — they sum to the annual model’s A_a r, which is what makes the anniversary performance levy reproduce it.

invest_income_ytd(t)[source]#

The financial performance accumulated since the policy year opened, through month t.

I(t) in the first month of the policy year and I(t) + invest_income_ytd(t - 1) in the other eleven, so it resets on every duration_mth(t) % 12 == 0. It exists because the R. 134-3 base 5° levy is struck once a year on the compte de participation aux résultats, so the levy needs the year’s balance and not the month’s.

perf_levy(t)[source]#

F(t): the R. 134-3 base 5° levy on positive financial performance, on the anniversary.

f_perf x max(invest_income_ytd(t), 0) where is_anniv(), and nil in the other eleven months. It is an event of the striking, not of the month: R. 134-3 5° is a levy on the balance of the compte de participation aux résultats, R. 134-4 strikes that account at least annually, and R. 134-12 III fixes affectations to the striking dates. A monthly levy would also be a different charge — it would tax a positive month inside a losing year, which the asymmetric max(., 0) exists not to do, and would have taken money in the worked example’s policy year 6.

Nothing at all in a year of negative performance, which is why the worked example’s performance levy on the anniversary closing policy year 6, month t = 71, is zero on both chassis. A policy exiting between anniversaries pays no performance levy for the part year, which is what the article’s annual striking implies.

own_assets_at(t, timing)[source]#

The account assets at a point inside month t.

"BOM"

the opening assets: A(t-1) in every month but the first projected one, and in that one the initial versement net of the entry charge on a new-business cell or own_assets_ifo on an in-force one. This is where the issue instant lives — it is not a row of the frame.

"AFT_LEVY"

less the base 4° parts levy, which falls only in the first month of a policy year.

"AFT_EXIT"

less any rachat partiel, taken gross of the exit charge because the charge stays in the account.

"AFT_PREM"

plus a beginning-of-month scheduled versement net of the entry charge. This is the balance the month’s return accrues on.

"AFT_RETURN"

after the month’s asset return, at asset_return_mth().

"AFT_PERF"

after the base 5° performance levy, which is nil except on the anniversary; the balance the two provisions are struck against.

"AFT_TOP_UP"

plus any free versement on the anniversary; the closing assets, and the same number as own_assets().

The steps are exposed individually because their order is fixed by R. 134-4 and R. 134-12 III — asset affectations completing the representation are made on the dates the participation account is struck, after its balance has been allocated — rather than being arithmetic convenience.

own_assets(t)[source]#

A(t): the auxiliary-account assets attributable to the policy at the end of month t.

At realisation value (R. 134-8) and excluding any outstanding L. 134-3 contribution, which is the insurer’s capital and not the savers’ money. That exclusion is the point of the cells: own_assets_at() rolls forward from A(t-1) and never from pm(t-1) + prov_div(t-1), so the contribution earns nothing for the savers. In the worked example the policy-year-7 roll — month t = 72 — starts from 10,250.65, not from the 12,384.73 the contract would have surrendered for.

parts_at(t, timing)[source]#

The number of parts at a point inside month t.

"BOM"

the opening count: N(t-1) in every month but the first projected one, and in that one the parts the initial net versement buys at part_value_init() on a new-business cell, or parts_ifo on an in-force one.

"AFT_LEVY"

N_open(t)(1 - f_p) in the first month of a policy year, where the base 4° levy falls, and N_open(t) unchanged in the other eleven. The levy cancels parts rather than reducing their value.

"AFT_EXIT"

after a rachat partiel has cancelled its pro-rata share, at the month’s own wd_rate_mth().

"AFT_PREM"

plus the parts a beginning-of-month versement bought. This is the count the month-end striking divides by.

"AFT_TOP_UP"

plus the parts a free versement bought on the anniversary at the just struck part value; the closing count, and the same number as parts().

parts(t)[source]#

N(t): the number of parts de provision de diversification at the end of month t.

The saver’s rights are expressed in a number of parts, and R. 134-2 makes the insurer’s commitment the number and not the value. On the worked example the count closes on 212.8127 x 0.992^7 = 201.1774: seven years of the base 4° levy and nothing else.

pm_at(t, timing)[source]#

The provision mathématique inside month t: opening, and at the month-end striking.

"BOM"

the opening PM: pm(t-1) in every month but the first projected one, and in that one the opening guaranteed amount discounted at the boundary-t rate. On an in-force cell this is the number check_pm_restruck() compares against the extract’s pm_ifo.

"AFT_STRIKE"

the guaranteed amount as it stands after the month’s opening steps, discounted at the rate of the month-end striking, i_pm(t + 1). In eleven months of twelve this is A. 134-5’s valeur intermédiaire; in the twelfth it is the striking of the participation account itself.

"AFT_TOP_UP"

the same after any free versement on the anniversary; the closing PM, and the same number as pm().

Identically zero on Chassis B, where the guarantee is not provisioned inside the account at all.

pm(t)[source]#

pm(t): the provision mathématique at the end of month t.

mg(t) (1 + i_pm(t+1))^-rem_term(t+1) on Chassis A and identically zero on Chassis B (R. 134-2) — the closing guaranteed amount discounted from the échéance back to the month’s end, boundary t + 1, over a fractional number of years. Re-struck every month, never accumulated. Rolling pm(t-1) forward at last year’s rate removes the rate effect: in the worked example that is +587.44 of the +824.18 move over policy year 6, t = 59 to t = 71, against a time effect of +236.74.

The monthly re-strike splits that move in two, which the annual grid could not: the PM accretes from 10,541.34 at t = 60 to 10,738.63 at t = 70 at the unchanged 2.25% — the time effect — and jumps to 11,346.00 at t = 71, where the TEC is re-published at 1.00% — the rate effect, +607.37 in one month.

In the last projected month the discount factor is 1, so pm equals mg identically and the Chassis A guarantee is pre-funded by construction.

prov_div_at(t, timing)[source]#

The provision de diversification inside month t: opening, and at the striking.

The residual of the account’s assets over the provision mathématique, floored at the parts’ minimum value: R. 134-4 permits a debit balance to reduce the part value only within the limit of its minimum. Where the floor binds the two provisions together exceed the assets, and the excess is exactly the L. 134-3 contribution.

"BOM" is the opening provision — prov_div(t-1) in every month but the first projected one, and in that one the residual the initial versement, or the in-force extract, leaves over the opening PM. It is the base of the parts levy in the months that carry one.

prov_div(t)[source]#

prov_div(t): the provision de diversification at the end of month t.

The savers’ individualised rights (R. 343-3 9°), and the only part of the engagement that bears investment risk. On the worked example’s policy-year-6 shock, at the anniversary month t = 71, the raw residual on Chassis A is 10,250.65 - 11,346.00 = -1,095.35 and the floor binds instead at 207.7460 x 5.0000 = 1,038.73. The monthly grid shows where that starts: the floor first binds in month 66, not at the anniversary.

part_value_at(t, timing)[source]#

The valeur de la part inside month t: opening, and at the month-end striking.

"BOM" is the opening value — u(t-1) in every month but the first projected one, and in that one part_value_init() on a new-business cell (the initial versement buys parts at it) or the extract’s pd / N on an in-force one. It is what a beginning-of-month versement buys parts at. "AFT_STRIKE" is what a free versement on the anniversary buys parts at.

part_value(t)[source]#

u(t): the valeur de la part at the end of month t, prov_div(t) / N(t).

Struck every month: in the eleven months of a policy year in which the participation account is not struck this is the valeur intermédiaire A. 134-5 requires, and it is what an exit falling in the month is priced on.

Common to every engagement of the auxiliary account (R. 134-2), so savers with different maturities and different guarantee levels in one account earn the same rate; differentiation is possible only through the number of parts or through a differentiated PCDD distribution. A per-policy model can only approximate that — the shipped model points are separate accounts, and their part values diverge as separate accounts’ would.

provision_value(t)[source]#

pm(t) + prov_div(t): the base of the R. 134-5 and R. 134-6 values.

One expression on both chassis, because pm is identically zero on Chassis B. It equals pm(t) + N(t) u(t), which is how the articles write it.

provision_value_at(t, timing)[source]#

pm + prov_div at a point inside month t.

"BOM"

the opening provisions — the base a beginning-of-month rachat partiel takes its pro-rata share of.

insurer_contribution(t)[source]#

C(t): the outstanding L. 134-3 contribution completing the representation.

max(pm(t) + prov_div(t) - A(t), 0), and nil on Chassis B, where the shortfall against the guarantee is carried as a PGT instead. It is the insurer’s capital: it carries no return to the savers and is releasable as soon as the account’s own assets cover the two provisions. The surrender value exceeds the account’s own assets by exactly this amount while it is outstanding — 2,134.08 at the anniversary month t = 71 of the worked example’s policy-year-6 shock.

Struck monthly, which is where the finer grid earns its keep on this cell: the contribution starts at 141.96 in month 66, the month the part-value floor first binds, and climbs to 2,134.08 by the anniversary. The annual grid could only report the anniversary figure.

apport(t)[source]#

The R. 134-12 apport d’actifs, made on the anniversary of a stated policy year.

Capped at 10% of the diversification provision by the article. It enters the account at realisation value and endows the PCDD; it is never credited to prov_div, so it changes no policyholder value by one cent. apport_t is the contractual policy year at whose end it falls, so the test is is_anniv(t) and policy_year(t) == apport_year() — the single month t = 12 x apport_year() - 1; zero is the “no apport” sentinel and matches no month. It stays on the anniversary because R. 134-12 III fixes asset affectations to the dates the participation account is struck. On the worked example’s Chassis B a statutory-maximum apport on the anniversary closing policy year 6 — month t = 71 — is 989.92 and cuts the PGT from 1,446.78 to 456.86.

pcdd(t)[source]#

D(t): the provision collective de diversification différée (R. 343-3 10°).

Collective, with no individual rights, and released into the participation account within fifteen years (A. 132-16) — against eight for a euro fund’s provision pour participation aux bénéfices. This model accumulates the apport d’actifs into it and nothing else: the mémoire’s piloting rule, which runs the fund at 30 bp above the insurer’s own euro fund and carries the rest here, is a fund-level discretion a single-policy model cannot express, and holding it at zero understates the smoothing the real product delivers.

pgt(t)[source]#

G(t): the provision pour garantie à terme (A. 134-2, R. 343-3 11°).

max(mg(t) (1 + i_pm(t+1))^-rem_term(t+1) - prov_div(t) - D(t), 0) on Chassis B and nil on Chassis A, struck at the end of every month. Funded from the insurer’s own funds and held outside the participation account, on a deliberately narrow basis: the A. 132-18 mortality tables at a rate at most 90% of the TEC, counting no cash flows other than guarantee maturities and mortality. A model must not “improve” that basis by adding lapses or expenses to it, and must not let the provision reach a benefit or feed the profit-sharing computation.

Mortality, one of the article’s two admitted drivers, is not implemented [std]. The present value above applies no survival factor, so it is the amount for a guarantee certain to be reached: prudent, since it overstates the provision, and invisible on the worked example, where mort_rate is zero. It is live on the decrement-bearing cells — on model point 6 at the anniversary closing policy year 7, month t = 83, this returns 2,739.35 against 2,477.36 with the five-year survival factor 0.972660 the shipped table gives. In a single-policy model the decrement reaches the projection through pols_if in result_cf() instead; a fund-level implementation should carry the survival factor inside the present value, summed over the account’s 2° engagements.

On the worked example’s Chassis B the monthly striking shows the provision being constituted: the PGT first becomes positive in month 68, at 61.48, and reaches 1,446.78 by the anniversary at month 71.

gate_revalue_ok(t)[source]#

Whether both A. 134-3 tests permit revaluing the guarantees out of the account.

Test 1: the diversification provision exceeds 1.5 times the excess of the guaranteed amounts over the provision mathématique. Test 2: the diversification provision less the parts at their minimum value exceeds 10% of the provision mathématique. Both must pass. Informational here — the reference credit-balance route raises the part value instead — but computed, because a model that revalued guarantees without testing the gates would be exercising a discretion the article does not allow. Evaluated on the month’s own striking, so it is answered every month rather than once a year. On the worked example both pass at t = 59, the anniversary closing policy year 5, and the second fails at t = 71, where the part-value floor has taken the headroom to nil.

conversion_headroom(t)[source]#

The parts convertible into provision mathématique under A. 134-4.

The article requires the diversification provision, net of the conversion and of the parts at their minimum value, to remain at least 15% of the resulting PM, and imposes a five-year cooling period besides. Solving pd - C - N u_min = 0.15 (pm + C) gives C = (pd - N u_min - 0.15 pm) / 1.15 — 474.52 on the worked example at t = 59. Computed, never exercised: the conversion is out of scope, and the 0.50% frais de conversion the market shows would take 2.37 of that.

surrender_indemnity(t)[source]#

The R. 132-5-3 indemnité de rachat applying to a full surrender in month t.

The article caps the indemnity at 5% of the present value of the mutual engagements (20% or 10% in narrow unlisted-asset cases) and permits the contract to provide for no indemnity at all once it has been in force more than ten years. That is a permission, not a prohibition: the article does not forbid an indemnity beyond ten years. The reference contract charges none at any duration, and this model returns zero once indemnity_max_years policy years have elapsed — from duration(t) = indemnity_max_years on, the eleventh policy year, month t = 120 on a longer term — unconditionally, which is the permission taken up [std] rather than the article applied. The test is on the completed policy years, because that is the unit R. 132-5-3 states.

surrender_value(t)[source]#

The R. 134-5 valeur de rachat at the end of month t.

pm(t) + N(t) u(t) on Chassis A and N(t) u(t) on Chassis B, less the base 6° exit charge and any surrender indemnity.

There is no guarantee in it before the *échéance*. On the worked example’s policy-year-6 shock, at the anniversary month t = 71, Chassis A surrenders for 12,384.73 — 105.31% of net versements, because its provision mathématique has already been marked up by the fall in rates — while Chassis B surrenders for 9,899.22, 84.18%, against a guarantee of 11,760.00 that does not apply. A model that floors this at g times premiums, or at the discounted guarantee, is modelling a contract that does not exist.

A surrender is priced on the forward part value A. 134-5 requires — the striking of the month in which the request falls, which is the next striking or intermediate value after it. The monthly grid delivers that rather than standardizing it away: a Chassis B saver surrendering in month 65 receives 11,430.63, not the 9,899.22 the anniversary striking at month 71 reports.

death_value(t)[source]#

The current provision value, which is what a death before the échéance pays.

Chapter IV contains no death valuation article, so the death benefit is the value of the engagement and the maturity guarantee does not apply. No exit charge: the base 6° charge is on amounts the saver elects to take out.

death_payout(t)[source]#

The death benefit actually paid, after any garantie décès plancher.

max(death_value(t), cum_prem_net(t)) where the model point carries the rider. The floor is a complementary guarantee provisioned outside the auxiliary account (R. 134-7), not the maturity guarantee arriving early: it happens to equal mg here only because g is 100%, and at g = 80% the two would differ.

rider_claim_pp(t)[source]#

The part of the death benefit the garantie décès plancher funds, per claim.

death_payout(t) - death_value(t) — 1,860.78 on the worked example’s policy-year-6 Chassis B death, month t = 71. Reported apart from the auxiliary-account columns because it is not the account’s money: R. 134-7 puts complementary guarantees outside it.

maturity_value(t)[source]#

The R. 134-6 amount payable at the échéance; nil at every other t.

The échéance is the end of the last projected month, t = proj_len() - 1, and it really is one month rather than a policy year — which is why this test stays on the month while the rachat exclusions in lapse_rate() and wd_rate() moved to the policy year. pm + N u there on Chassis A — more than the guarantee whenever the parts retain any value, 12,765.89 against 11,760.00 in the worked example — and max(N u, mg) on Chassis B. The max exists only in that last month and only on Chassis B; applying it earlier, or on Chassis A at all, invents a guarantee the contract does not give.

The statutory default at the échéance is in fact an arbitrage into an SRI <= 2 support unless the holder decides otherwise (A. 134-6); this model pays the amount out and stops, and a “roll into a low-risk support” variant is the natural extension.

claim_pp(t, kind)[source]#

The payout per claim in month t, by kind.

"DEATH"

death_payout() — the current provision value, floored at cumulative net versements where the rider is carried.

"LAPSE"

surrender_value() — the R. 134-5 value, with no guarantee before the échéance.

"MATURITY"

maturity_value() in the last projected month and nil elsewhere.

mort_rate(t)[source]#

q(x + dur(t) + 1): the annual mortality rate of the policy year of month t [std].

The rate the technical notes tabulate; mort_rate_mth() is the rate actually applied in the month. Read at age_anniv(), the âge atteint at the end of the policy year, which is the notes’ own q(x + t + 1) restated on the monthly grid: the age rule belongs to the annual assumption, and the finer grid changes only where within the year the decrement falls. The shipped table rate times mort_be_factor. Both are placeholders: the homologated tables TH 00-02 / TF 00-02 are cited by arrêté and never shipped, A. 132-18 permits an insurer’s own certified table besides, and the proxy is INSEE-shaped population mortality with a factor for insured lives being lighter than the population. Nil where the model point switches the decrements off, which is the worked example’s configuration.

mort_rate_mth(t)[source]#

q_m = 1 - (1 - q)^(1/12): the monthly mortality rate applied in month t [std].

Derived geometrically and not by dividing by twelve, which is what makes (1 - q_m)^12 = 1 - q hold exactly - so twelve months of it leave the in-force at the anniversary identical to the annual-step model’s. On shipped point 6 the annual 0.00243005 becomes 0.00020273 a month, and twelve of those compound back to 0.00243005.

The conversion carries [std] because no retrieved French source states a conversion convention for any decrement.

lapse_rate_base(t)[source]#

The table annual full-surrender (rachat total) rate of the policy year of month t [std].

2.5% p.a. level; the mémoire observes 2%-3%. lapse_table.csv is keyed by the contractual policy year, read here at policy_year(t); policy years beyond the table take its last row.

guarantee_imminent(t)[source]#

0.5 in the two policy years before the échéance on Chassis B, while the guarantee bites.

The policy year of month t is policy_year(t), so the years still to run at its close are n - policy_year(t) and the suppression applies over every month of a policy year where that is at most guarantee_imminent_years. On shipped point 6 that is months 108 to 143.

The in-the-money test is read on the current month’s striking rather than on the anniversary’s, because the behavioural statement is that a saver deciding to surrender looks at whether N u < mg now. It is a faithful reading rather than a free one: on every shipped model point the two readings agree in every month, the gate’s state being slow-moving relative to a month.

The gate matters. A saver who surrenders a 2° engagement gives up the entire guarantee (R. 134-5), so the deterrent exists precisely while N u < mg and is worth nothing otherwise; applying it unconditionally would invent behaviour where there is none. This is the strongest exit deterrent the product creates, and it is [std] — no eurocroissance lapse experience is public.

duration8_spike(t)[source]#

1.5 over policy year 8 — every month with policy_year(t) == 8 — where n > 8 [std].

The assurance-vie annual abattement becomes available at eight years, so surrender incentive spikes there — and only where the contract still has years to run, since a contract maturing at eight has no such choice to make. It is a multiplier on the annual rate, applied before the monthly conversion, so it grades once a year as the behaviour it stands for does and not once a month.

lapse_rate(t)[source]#

w(t): the annual full-surrender rate of the policy year containing month t.

The rate the technical notes tabulate — the table rate times both behavioural overlays, capped at 1; lapse_rate_mth() is the rate actually applied at the end of the month. Nil while the decrements are switched off, inside a non-surrender period — policy years 1 to lock_up_years, so duration(t) < lock_up_years() — and over the whole of the last policy year, which ends at the échéance, where the survivors take the maturity amount instead — the base run assumes 100% of them do, as the mémoire also assumes.

That last exclusion is duration(t) >= policy_term() - 1 and not the last month. The annual grid never let a survivor lapse out of the échéance year; restating the test as t >= proj_len() - 1 would open eleven months of it and move the whole maturity claim, which is the single most likely place for the conversion to introduce a silent error.

lapse_rate_mth(t)[source]#

w_m = 1 - (1 - w)^(1/12): the full-surrender rate applied at the end of month t [std].

Derived geometrically from the policy year’s annual rate, so (1 - w_m)^12 = 1 - w exactly and the in-force at every anniversary is the annual-step model’s. The same conversion mort_rate_mth() takes, and [std] for the same reason: no retrieved French source states one.

pols_if_at(t, timing)[source]#

The number of policies in force at a point inside month t.

"BEF_DECR"

the start of the month, before any decrement — and the exposure every cash flow of the month is weighted by; pols_if().

"BEF_LAPSE"

after deaths, before surrenders: the processing order is death before surrender [std].

"AFT_DECR"

the notes’ l(t): the end-of-month count, and zero in the last projected month, where the survivors mature. This is the timing the end-of-month quantity is reached through, because the bare name pols_if belongs to the start-of-month count.

Both decrements are applied at their monthly rates, so a death or a surrender falls in the month it happens rather than at the anniversary — and twelve months of them compound back to exactly the policy year’s annual factors, which is why pols_if(12k) is what the annual-step model carried for policy year k + 1.

pols_if(t)[source]#

The number of policies in force at the start of month t.

This is the library’s shared vocabulary: pols_if(t) is the exposure at the start of month t and the weight on that same result_cf() row’s cash flows, so the opening row is pols_if_init() exactly — no decrement has been applied when a month opens.

pols_if(12k) is the count the annual-step model this one replaced carried on its row for policy year k + 1, to floating point, because the monthly decrements compound back to the annual ones.

The notes’ l(t) is the end-of-month count, nil on the last projected row because everyone has matured by the end of it. That quantity is unchanged and is reached as pols_if_at(t, "AFT_DECR"); it is no longer published under this name, because doing so put the exposure column one period ahead of the flows printed beside it.

pols_death(t)[source]#

Deaths in month t, against the start-of-month in force, at mort_rate_mth.

pols_lapse(t)[source]#

Full surrenders at the end of month t, from the survivors of that month’s mortality.

pols_maturity(t)[source]#

Survivors reaching the échéance; nil outside the last projected month.

The base run assumes 100% of them take the maturity amount [std], as the mémoire also assumes; it notes that modelling annuitisation or reinvestment instead could amplify or damp its results.

premiums(t)[source]#

Versement income in month t, an inflow.

The initial versement where the month is the first projected one, a scheduled versement in a month that opens a policy year and a free one on an anniversary, gross of the entry charge — the charge is reported as insurer income in charges_taken() rather than netted out of the premium line.

withdrawals(t)[source]#

Rachats partiels paid at the beginning of month t.

An owner election rather than a claim, which is why it has its own name and column: the contract stays in force and continues on the reduced parts and reduced guarantee.

claims(t, kind=None)[source]#

Benefit outgo in month t, by kind; the total when kind is omitted.

Deaths and surrenders are now settled in the month they happen, priced on that month’s own striking, rather than at the anniversary — which is what the finer grid was for, and why the claim columns do not reproduce the annual-step model.

"DEATH", "LAPSE" and "MATURITY" weight claim_pp() by the corresponding decrement. The death line includes the garantie décès plancher’s contribution, which rider_claims() reports separately; it is a decomposition of this column and not a fourth kind.

rider_claims(t)[source]#

The garantie décès plancher’s share of the month’s death outgo.

A memo line: it is already inside claims_death, and it is published apart because R. 134-7 puts complementary guarantees outside the auxiliary account, so it is not paid out of the savers’ provisions.

expenses(t)[source]#

E(t): the insurer’s own expenses in month t [std].

Acquisition at 5% of versements plus an acquisition commission of 2% of the initial one, and maintenance at 0.20% p.a. of the two provisions. All three levels come from the published mémoire, which is the only complete public parameterisation of this product. They are the insurer’s costs, not charges to the saver: the charges the contract permits are the six R. 134-3 bases, reported in charges_taken().

The acquisition components fall in the month their versement does and are weighted by the start-of-month exposure like every other flow. Maintenance accrues at one twelfth a month, 0.20% / 12 of the month’s own striking of the two provisions, weighted by the count on the books at it, pols_if_at(t, "AFT_DECR").

This is the one place the conversion changes an answer rather than its resolution, and it changes it in two ways. Twelve monthly accruals cover exactly the year the annual grid charged for once at its year end, so the extra opening-striking charge the annual model needed — n + 1 point-in-time charges to cover n years of service — is gone; and the charge is now borne by the in-force and the provision of each month rather than of the anniversary, so a decrementing block carries less of it and a block whose provision grows through the year carries less again. Over a whole projection that is -0.2% to -1.3% on the shipped cells, and +0.9% on point 9, whose five annual versements step the provision up in the first month of each of its first five policy years.

charges_taken(t)[source]#

The R. 134-3 charges the insurer takes in month t: income, reported apart.

The base 4° parts levy, the base 5° performance levy, the base 1° entry charge and the base 6° exit charge together with any surrender indemnity. They are not in net_cf: they are transfers inside the account from the savers’ provisions to the insurer, and the benefits they reduce are already net of them. Publishing them beside the flows is what makes the charge structure auditable against R. 134-3.

liability_cf(t)[source]#

CF(t): the month’s liability cash flow, outgo positive, as the notes print it.

Claims and rachats partiels and expenses out, versements in. The two provisions appear nowhere in it — they are state variables, not cash flows — and neither do the insurer’s own-funds items, which are capital rather than benefit.

net_cf(t)[source]#

The net cash flow of month t, income positive: -liability_cf(t).

The library’s sign convention, so that result_cf()["net_cf"] can be compared and summed across products without checking which one it came from.

check_assets_roll_fwd_resid(t)[source]#

The account-asset recursion residual in month t; zero everywhere.

A(t) - {[A_open(t) - L(t) - W(t) + P_net(t)](1 + r_m) - F(t) + top-up}, rebuilt in one expression rather than through own_assets_at(), so that a mis-ordered step shows up here: a parts levy taken after the return instead of before it, a versement credited after the return rather than at the beginning of the month, or a performance levy struck on the wrong balance. The opening quantities are the "BOM" timings, so the check is live in the first projected month too, where they carry the initial versement or the in-force extract.

Two of the three charges now enter by name rather than being rebuilt from their rates, and that is the conversion showing through: the base 4° levy falls only in the first month of a policy year and the base 5° levy only on the anniversary, on the year’s accumulated performance, so neither can be reconstructed from a month’s own balance. What the check still rebuilds independently is the order and the base, and the companion identity invest_income(t) == base x asset_return_mth(t) — asserted in tests/test_eurocroissance_fr.py — pins the balance the return accrues on.

check_assets_roll_fwd()[source]#

True when the account-asset recursion closes in every projected month.

check_parts_roll_fwd_resid(t)[source]#

The parts recursion residual in month t; zero everywhere.

N(t) - {N_open(t)(1 - f_p 1{BOM of a policy year})(1 - w_pm) + parts bought at BOM + parts bought at the top-up}, rebuilt in one expression. The base 4° levy cancels parts rather than reducing their value, so a levy applied to the part value instead of to the count would leave the count unchanged and show up here; so would a versement priced at the wrong striking, or a levy taken in a month that carries none. On the worked example the count closes on 212.8127 x 0.992^7 = 201.1774.

check_parts_roll_fwd()[source]#

True when the parts recursion closes in every projected month.

check_guarantee_roll_fwd_resid(t)[source]#

The guaranteed-amount recursion residual in month t; zero everywhere.

mg(t) - {mg_open(t)(1 - w_pm) + g x net versements of the month}, rebuilt in one expression. It is the check that catches the guarantee being computed on gross versements: with a 2.00% entry charge, mg after the worked example’s top-up on the anniversary closing policy year 3 is 11,760.00 and not 12,000.00, and a model that used the gross figure would fail here in the month the top-up is paid rather than silently over-guaranteeing for seven years.

check_guarantee_roll_fwd()[source]#

True when the guaranteed-amount recursion closes in every projected month.

check_pols_roll_fwd_resid(t)[source]#

The in-force roll-forward residual in month t; zero everywhere.

The month opens at pols_if(t) and closes at pols_if_at(t, "AFT_DECR"), the notes’ l(t); the difference is the month’s deaths, full surrenders and maturities.

check_pols_roll_fwd()[source]#

True when the in-force roll-forward closes in every projected month.

check_guarantee_funding_resid(t)[source]#

pm(t) accumulated to the échéance less the guaranteed amount; zero on Chassis A.

This is the model’s headline identity: the provision mathématique accumulated at the regulated rate reaches the guarantee exactly at the échéance, so pm(t)(1 + i_pm(t+1))^rem_term(t+1) = mg(t) at every t — month t closes at boundary t + 1, which is (T - t - 1)/12 years short of the échéance — and the two coincide in the last projected month. On the monthly grid it holds month by month and not only at anniversaries, which is one of the reasons the PM is re-struck at a fractional remaining term rather than held flat between strikings.

It is zero by construction under R. 134-2’s re-strike rule, and that is the point of publishing it: an implementation that accumulates the PM instead — rolling pm(t-1) forward at last year’s rate — breaks it the first year the rate moves. On the worked example’s path pm(59) x 1.0225 = 10,758.56 against the 11,346.00 the re-strike gives, and the 587.44 difference is the rate effect it has silently dropped.

Identically zero on Chassis B, where both sides are nil: a 2° engagement funds nothing inside the account against its guarantee, which is exactly why an early rachat there can pay less than the guaranteed amount. The Chassis B funding statement is check_pgt_covers_guarantee() instead.

check_guarantee_funding()[source]#

True when the provision mathématique funds the guarantee exactly, at every t.

check_pgt_covers_guarantee_resid(t)[source]#

pd + G + D less the discounted guarantee on Chassis B; non-negative in every month.

A. 134-2 makes the PGT the shortfall of the diversification provision and the PCDD against the discounted guarantees, floored at zero, so the three together always cover it — exactly where the PGT is positive and with a surplus where it is not. It is the Chassis B counterpart of check_guarantee_funding(), and what it asserts is that the guarantee is funded somewhere: on the savers’ side before a shock, and out of the insurer’s own funds after one.

Identically zero on Chassis A, which constitutes no PGT: there the L. 134-3 contribution plays the analogous role and insurer_contribution() carries it.

check_pgt_covers_guarantee()[source]#

True when the diversification provision and the own-funds provisions cover the guarantee.

check_part_value_floor_resid(t)[source]#

u(t) - u_min: how far the part value sits above its contractual floor.

Non-negative everywhere. R. 134-4 permits a debit balance on the participation account to reduce the part value only within the limit of its minimum, and an implementation that omits the floor takes the worked example’s Chassis A diversification provision to -1,095.35 at the anniversary of policy year 6, t = 71 — a negative provision, and with it a negative surrender value that every downstream number stays plausible enough to read past.

check_part_value_floor()[source]#

True when the part value stays at or above its contractual minimum, every month.

check_own_funds_not_paid_resid(t)[source]#

The excess of the month’s largest benefit over the two provisions; non-positive.

Before the échéance every benefit is bounded by pm(t) + prov_div(t): the surrender value is that less charges and the death value is exactly it, the garantie décès plancher being funded outside the account and excluded here. Neither the L. 134-3 contribution nor the PGT may reach a policyholder, and on the shipped Chassis B cells the PGT is positive for four consecutive years, so the check is live rather than decorative: an implementation that floored the Chassis B surrender value at the guarantee would pay 11,760.00 against a bound of 9,899.22 and fail here.

The residual is zero in the last projected month, the one that ends at the échéance, and that is not a gap. The maturity guarantee on Chassis B legitimately exceeds the account’s provisions, and paying it out of the PGT is precisely what the PGT was constituted for.

check_own_funds_not_paid()[source]#

True when no benefit before the échéance exceeds the savers’ two provisions.

check_pm_restruck_resid(t)[source]#

The in-force extract’s provision mathématique against the re-strike; zero.

R. 134-2 makes the PM the guaranteed amount discounted at the current A. 134-1 rate, so an extract cannot supply it independently: this compares what the extract reports against what the rule requires, at the valuation date.

Zero by construction on a new-business cell, where there is no extract to check, and on Chassis B, which has no PM at all. On the in-force cell it is a live cross-check, and what it catches is an extract built by accumulating the PM from issue rather than re-striking it — the same error check_guarantee_funding() catches inside the projection, arriving from the data side instead.

The comparison is against pm_at(t, "BOM"), the opening PM of the first projected month — the extract’s own valuation date — and not against that month’s closing PM. proj_start() is a month now, 48 on shipped point 7, and disc_factor(48) discounts over rem_term(48) = 6 years, so the re-strike lands on the extract’s 8,575.24 exactly as it did on the annual grid.

check_pm_restruck()[source]#

True when a shipped in-force provision mathématique agrees with R. 134-2.

result_cf()[source]#

Result table of cashflows, indexed by the 0-based policy month t.

The frame is range(proj_start(), proj_len()): one row per projected policy month — 120 on the worked example and 72 on the in-force point 7 — the first of which carries the initial versement and the acquisition costs it draws.

pols_if is the start-of-month count, which is the exposure the flows on that same row are weighted by; the notes’ end-of-month l(t) is pols_if_at(t, "AFT_DECR") and is not published here. charges_taken and rider_claims are memo lines outside net_cf — the first is a transfer inside the account from the savers to the insurer, and the second is already inside claims_death.

result_cf_annual()[source]#

result_cf() summed into policy years, indexed by policy_year.

Every cash flow column is the total of its twelve months; pols_if is the count at the start of the policy year, pols_if(12 x (policy_year - 1)), which is the number the annual-step model this one replaced carried on the same row. It is the monthly frame regrouped, never a second projection, which is what lets the notes’ annual figures be laid beside the monthly ones and the difference read off.

Laid beside the annual-step model this one replaced, the columns split three ways. pols_if and premiums agree exactly on every shipped model point: the opening in-force is what the monthly decrements compound back to, and a versement is collected in a month the contract names and weighted by the in-force of that month under either grid. withdrawals agrees on every cell that takes no rachat partiel, which is all of them but point 5. charges_taken agrees wherever the decrements are switched off (points 1-4); elsewhere its surrender-indemnity term is weighted by the month of exit and moves with the claims.

The rest do not agree, and are not meant to. expenses moves because maintenance now accrues at one twelfth a month on the month’s own provision and in-force and the annual grid’s extra opening-striking charge is gone — the largest single-year gap on the shipped cells is 33.28 EUR, on point 9. The claims_* columns move because a claim now falls at the end of the month of exit rather than of the year. liability_cf and net_cf inherit both. That gap is the point of the finer grid.

The frame opens on the policy year the model point’s first projected month opens, so an in-force cell is indexed from duration_inforce() + 1 rather than from 1.

result_provisions()[source]#

Result table of the provision machinery, indexed by the 0-based policy month t.

Every column is a closing value of month t, or a rate applied within it. The account’s assets against the two provisions, the parts and their value, and the insurer’s own-funds items beside them — reported, and never in a benefit. The notes’ two worked-example tables are the anniversary rows of this frame, t = 11, 23, ..., 119, whose opening state is own_assets_at(t, "BOM") and its siblings rather than a row of the frame. The other eleven rows of each policy year are the A. 134-5 intermediate valuations, which the annual grid could not report at all.

i_pm is the rate the month’s provisions are struck at: i_pm(t + 1), the A. 134-1 rate at the month-end boundary, since i_pm() is indexed by a month boundary.