The Projection Space#
The by-contract projection of the Rente_FR_S model.
The Space is parameterized by point_id, so Projection[1] is an ItemSpace
projecting model point 1:
>>> Projection[1].result_cf() # the worked-example scenario
>>> Projection.point_id = 2 # the same contract, probability-weighted
t counts months from the effective date, 0-based: t = 0 is the first
projected month — the first whole civil month of service, at the end of which the first
arrérage falls due — and the frame is t = 0, 1, …, proj_len() - 1. Month t runs
from time t to time t + 1, so duration(t) = t // 12 completed policy years have run
at its start and policy_year(t) = t // 12 + 1.
Survival is carried on the time-point clock rather than on the period one:
lives_if(k, life) is the probability that a life is alive at time k, with
k = 0 at the effective date and lives_if(0, life) = 1. Month t therefore opens
at lives_if(t, life) and closes at lives_if(t + 1, life) — which is what
payment_surv_mth() selects between — and nothing is indexed before time 0.
Input data
Inputs are external files: plain CSVs living in the model folder’s parent directory,
products/rente_viagere/, 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
Rente_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 |
reversion_coeff_file |
data.reversion_coeff_table() |
reversion_coeff_table.csv |
Naming
Cells names follow lifelib’s basiclife.BasicTerm_S and savings.CashValue_SE
wherever those models have an analogue, and follow PA_UK_S and SPIA_US_S
— this library’s UK and US payout chassis — wherever the three products share machinery,
so that the same concept has the same name whichever country’s model a reader opens. The
technical notes use compact actuarial symbols instead. The mapping is:
Notes symbol |
Cells |
Meaning |
|---|---|---|
t |
(the cells argument) |
Month from the effective date, 0-based |
(none) |
duration(t) |
Completed policy years |
(none) |
duration_mth(t) |
Months elapsed at start of month t |
(none) |
policy_year(t) |
Policy year containing month t |
C |
purchase_price() |
Capital constitutif |
Y0, M0 |
effective_year(), effective_month() |
Calendar year and civil month of the effective date |
k(t) |
cal_year_index(t) |
Completed 31 Decembers |
(none) |
calendar_year(t) |
Calendar year of month t |
(none) |
civil_month(t) |
Civil month of month t |
x_a0, x_r0 |
age_at_entry(life) |
Entry ages (ALB), life 1 or 2 |
x_a(t), x_r(t) |
age(t, life) |
Attained age (ALB) |
g_a, g_r |
birth_year(life) |
Millésimes |
(none) |
sex(life) |
M, F or mix |
rho |
annuity_rate() |
Taux de rente |
kappa |
option_coeff() |
Definitive option coefficient |
(reversion table) |
reversion_coeff() |
Réversion coefficient [S6] |
(guarantee) |
guarantee_coeff() |
Annuités garanties coefficient |
delta |
reversion_pct() |
Taux de réversion |
n |
guarantee_mths() |
Annuités garanties, in months |
i |
technical_rate() |
Taux technique |
m |
payment_freq() |
Payments per year |
timing |
payment_timing() |
arrears or advance |
T |
is_payment_mth(t) |
Month t is a payment date |
(payment point) |
payment_surv_mth(t) |
Time survival is measured at |
nu |
revalo_rate |
Revalorisation rate |
R(t) |
revalo_factor(t) |
Cumulative revalorisation index |
Pi(t) |
palier_factor(t) |
Palier step multiplier |
A0 |
annual_income_init() |
Gross annual rente at conversion |
A(t) |
annual_income(t) |
Gross annual rente in force |
A(t)/m |
annuity_pp(t) |
Scheduled arrérage |
h(t) |
mths_since_payment(t) |
Complete months since last payment |
(prorata) |
prorata_pp(t) |
Accrued prorata d’arrérages |
q(s, g, x) |
mort_rate_at_age(…) |
Generational table lookup |
q(t, life) |
mort_rate(t, life) |
Annual rate applied to a life |
q_mth |
mort_rate_mth(t, life) |
Monthly rate 1-(1-q)^(1/12) |
theta |
portfolio_male_share |
[std] portfolio mix |
(none) |
mort_basis() |
table or scenario run [std] |
(none) |
death_mth(life) |
Scenario month of death [std] |
l_a(t), l_r(t) |
lives_if(t, life) |
Survival probability to time t |
d_a(t), d_r(t) |
lives_death(t, life) |
Death density of month t |
gamma(t) |
certain_floor(t) |
Annuity-certain floor indicator |
max(gamma, l_a) |
payment_factor(t) |
Annuitant stream’s factor |
(none) |
payment_factor_life(t) |
l_a alone, before the floor |
delta(1-l_a)l_r |
reversion_factor(t) |
Réversion stream’s factor |
(prorata factor) |
prorata_factor(t) |
Death-settlement factor |
G(t) |
cum_annuity_pp(t, kind) |
Cumulative gross arrérages |
(tariff factor) |
annuity_factor(table_sex) |
Annuity factor at conversion |
(none) |
taux_rente_tariff() |
Taux de rente the table implies |
(none) |
taux_rente_own_table() |
The same on the life’s own table |
(none) |
unisex_gap() |
Cost of the unisex rule |
omega |
omega_age |
Limiting age, 120 |
(stopping rule) |
horizon_mths() |
Months to the age stop rule |
(none) |
proj_len() |
Projection length in months |
IF(t) |
pols_if(t) |
Any payment obligation open |
(none) |
pols_if_init() |
Contracts in force at outset |
E[ANN(t)] |
annuity_payments(t) |
Expected arrérages outgo |
E[PRO(t)] |
claims(t, “PRORATA”) |
Prorata d’arrérages on death |
E[FRA(t)] |
arrerage_charges(t) |
Frais d’arrérages retained |
E[EXP(t)] |
expenses(t) |
Maintenance expense |
c_e, pi |
expense_maint, inflation_rate |
Expense level and inflation |
CF(t) |
liability_cf(t) |
Total gross liability outgo |
(none) |
net_cf(t) |
-liability_cf(t), insurer sign |
Five names needed care.
kappa is one symbol for two different coefficients. The notes carry it as a model
point attribute; here it is derived, because both of its values are derivable and a
derived coefficient cannot drift out of step with the table that implies it.
reversion_coeff() reads the published [S6] age-difference table shipped as a CSV,
and guarantee_coeff() computes the certain-period loading off the tariff table.
option_coeff() is the one the conversion applies, and it is 1 when no option is
elected.
q is used for two different objects: the tariff rate, which is the more prudent
single table for every life, and the best-estimate rate, which is the life’s own
table. They are separate objects here — mort_rate_at_age() takes the table as an
argument, mort_rate() is the best estimate for a covered life, and
annuity_factor() takes the table it is priced on. Collapsing them destroys the
unisex mechanic; see below.
l denotes two different survival paths as well. lives_if() is the projection
path of a covered life on the best-estimate basis, and it honours the scenario switch;
tariff_lives() is the annuitant’s survival on a pricing table, and it does not.
They are different objects and the model keeps them apart.
pols_if is not a policy count in the usual sense. It is the notes’ IF(t), the
probability that any payment obligation remains — the guarantee certain, the annuitant
alive, or the reversion stream in payment — and it exists to carry the maintenance
expense. The name is kept because it is what the rest of the library calls the expense
weight.
There is no lapse_rate, improve_factor, improve_rate, surr_charge or
av_pp_at of any kind, and the absence of each is deliberate; see below.
The table is generational, so there is no improvement scale
mort_rate(t, life) is a pure table lookup:
q(t, life) = mort_rate_at_age(sex(life), birth_year(life), age(t, life))
with no factor of any kind applied on top of it and no calendar-year argument. TGH05 and
TGF05 are prospective generation tables: q(sex, generation, age) already gives the
rate the life will experience at that age, in calendar year generation + age. The
trend is inside the table. An improvement scale on top of it — which the UK sibling
needs, because its ONS base table is a period table — would double-count the trend, so
this model has no improve_factor cells and must not acquire one.
The millésime is a model point attribute and is never derived from the projection
year. A period-table implementation reads the rate for age 66 in calendar year 2027 and
walks diagonally across generations; a generational one reads (g = 1961, x = 66)
whatever the projection year. Two model points with the same entry age and different
millésimes must therefore give different rates at the same attained age, and the tests
assert exactly that.
The tariff table and the best-estimate table are different objects
The Code forces the single table applied to all lives to be the more prudent of the two,
which for an annuity is the female table; the shipped tariff_table_sex is "F".
The projection meanwhile decrements each life on its own table, or on a [std]
portfolio blend at portfolio_male_share where the model point carries mix. For a
male annuitant the two differ by construction, and the gap — unisex_gap(), about
13% of income on the shipped anchor — is the systematic technical surplus that the
eight-year profit-sharing rule requires to flow back to policyholders, which in this
model it does through the revalorisation rate nu. Pricing a male life on the female
table and projecting him on it makes the prudence margin invisible; projecting him on
the male table without crediting the surplus back shows a permanent retained profit the
rule does not allow.
Two mortality bases: table and scenario
The notes’ worked example is not a probability-weighted run. It is a scenario: “the
annuitant dies in month 25 — the 26th month of service — and the reversionary survives
throughout”, evaluated at l_a = 0 from time 26 and l_r = 1 throughout. The rest
of the notes projects on an expected basis. Both readings are shipped, and which one applies is a model point
column — the same device PA_UK_S and SPIA_US_S use for the same reason:
mort_basis = "table"lives_ifruns the monthly recursion off the shipped generational table. Model points 2, 4, 5, 7, 8, 9, 10 and 11; point 2 is the worked configuration on this basis and is the run to read for a realistic cash flow shape.mort_basis = "scenario"[std]lives_if(t, life)is the deterministic step function1{t <= death_mth(life)}— survival to time t, so a life dying in monthdis alive at timed, the start of that month, and gone from timed + 1— with a blank or negativedeath_mthmeaning the life survives the whole projection. Model points 1, 3 and 6, which reproduce the worked example and its variants exactly.
The scenario switch is a [std] modelling device, not a product feature; it exists
because the notes’ verification anchor is a scenario and retuning assumptions to force a
probability-weighted run onto it would be dishonest. It never reaches the pricing
side: tariff_lives() and annuity_factor() always run off the table.
Revalorisation is a calendar event, and it is pro-rated once
revalo_factor steps at each 31 December, never on a policy anniversary, and the
uplift reaches instalments payable from the following 1 January. The first step is
pro-rated nu x (13 - M0)/12 for the part-year of service, which degenerates to the
full nu for a 1 January effective date. Both halves are notes’ pitfalls: an
anniversary convention holds the annuity at its initial level for twelve months instead
of nine on the worked configuration and shifts every later step by three months, and
dropping the pro-rata overstates the annuity for the whole of its remaining life, because
R(t) is a running product. nu is floored at zero, which is the only contractual
bound any retrieved document states.
The frais sur encours de rentes appear in no recursion here, by design: they bite on
the provision mathématique and reduce the profit-sharing base — hence nu — and never
an instalment. Netting them off an instalment would cut the annuitant’s income, which no
retrieved contract does.
The guarantee is a floor, the reversion is a second stream
payment_factor(t) = max(certain_floor(t), payment_factor_life(t)) makes the annuités
garanties an annuity-certain floor rather than a second stream: while the guarantee
runs the full instalment is payable regardless of survival, and an additive construction
would pay 1 + l_a. The réversion is genuinely a second stream, gated on
(1 - l_a(t)) — the annuitant’s survival to the start of month t — and not on
(1 - l_a(t + 1)), its survival to the end: the survivor’s first instalment falls in
the month after the month of death, immediately after the prorata d’arrérages has
settled it. Gating on the end of the month pays the reversion and the prorata in the
same month, so the month of death is paid 1 + delta times.
The two options are not cumulative; check_options_xor() asserts no model point
carries both, and option_coeff() raises if one does.
The month of death is paid in full
prorata_pp(t) is the accrued instalment settled on death. With
h(t) = t mod (12/m) complete months since the last payment date it is
((h(t) + 1)/(12/m)) x A(t)/m, so at m = 12 it is exactly one full instalment —
the French rule is that instalments cease from the 1st day of the month following the
death — and at m = 4 a death in the first month of a quarter settles one third of the
quarterly instalment. The (1 - gamma(t)) gate suppresses it while the guarantee runs,
the full instalment being payable there already, and the second term of
prorata_factor() is the symmetric settlement on the reversionary’s own death. There
is no “with or without proportion” election in France: the prorata is the rule. On the
unobserved advance variant nothing has accrued unpaid at death, so the prorata is
zero [std].
The taux technique is not a discount rate
i reaches the projection only through rho: it prices the annuity at
conversion, through annuity_factor(), and thereafter functions as a lifetime
minimum guaranteed return. It appears in no cash flow recursion and is not a valuation
rate. The best estimate discounts at the risk-free term structure, which this library
does not compute at all — every technical-notes.md in it specifies gross liability
cash flows and leaves discounting and reserves to a layer that consumes them.
There is no policyholder behaviour to model
No lapse decrement, no dynamic behaviour formulas, no surrender value at any duration, no
alteration of options and no premium flexibility. That is a cited product feature — the
Code gives a rente viagère in payment no surrender value at all — and it is why this
model has no lapse_rate of any kind. The one election that survives conversion is not
a cash flow but an admission test: below the statutory commutation threshold the insurer
may pay a capital instead, with the annuitant’s agreement, so there is no annuity to
project, and check_commutation_floor() rejects such a model point rather than
projecting it. Behaviour otherwise enters the basis, not the projection: voluntary
annuitants self-select for longevity, which is already inside an annuitant-experience
table, and the unisex tariff deters male annuitants, which is the direction of
portfolio_male_share sitting below one half.
Sign convention
The notes define CF(t) as total gross liability outgo, which is
liability_cf(). net_cf() is its negative, the library-wide income-positive
convention, so a result_cf()["net_cf"] column can be summed or compared across every
model in the library. Both are published as columns rather than one being made to stand
for the other. There is no premium income in the projection at all: the capital
constitutif is a pricing input at the effective date, not a projected cash flow. The one
component that runs the other way is arrerage_charges(), which the insurer
retains out of each quittance: it is published as a positive column and
subtracted in liability_cf().
Cells Descriptions#
- purchase_price()[source]#
C: the capital constitutif actually applied to the annuity.
For a wrapper exit this is the valeur atteinte net of social and tax levies, with any entry charge taken before the model starts. A pricing input at the effective date, not a projected cash flow: the projection carries no premium income. It enters the projection only through
annual_income_init().
- effective_year()[source]#
Y0: the calendar year of the effective date.
The model carries the calendar and not merely the duration, because revalorisation and expense inflation step at 31 December.
- effective_month()[source]#
M0: the civil month of the effective date, 1 for January.
The annuity always takes effect on the 1st day of a civil month, so the effective date needs no day part. M0 fixes the length of the first partial calendar year, 13 - M0 months, over which the first revalorisation is pro-rated.
- age_at_entry(life)[source]#
x(0): the entry age of the annuitant (
life = 1) or reversionary (life = 2).Age last birthday at the effective date, chosen to index the shipped single-year-of-age generational table [std]. It is not derived from the millésime and does not derive it: the two are separate attributes, because a table keyed on year of birth and an age keyed on birthday do not determine one another inside a calendar year.
- birth_year(life)[source]#
g: the millésime of
life, the generational table’s key.Never derived from the projection year. The table is generational, so the rate a life experiences at an attained age depends on the year it was born and on nothing else about the calendar.
- sex(life)[source]#
The best-estimate table of
life:M,Formix.mixis the [std] portfolio blend of assumption (vi), for a model point that stands for a cohort rather than a person. This selects the best-estimate table only; the tariff istariff_table_sexfor every life, whatever this returns.
- annuity_rate()[source]#
rho: the taux de rente struck at conversion, per unit of capital per annum.
A model point attribute, because it is what a real barème quotes and no French insurer publishes one. Every shipped model point carries the rate the shipped tariff table implies at its own age, millésime and taux technique, rounded to six decimals;
check_taux_rente()asserts that agreement, andtaux_rente_tariff()is the derivation.
- reversion_pct()[source]#
delta: the taux de réversion; 0 where no réversion is elected.
The survivor receives delta times the rente atteinte at death, for life, from the 1st day of the month following it. Mutually exclusive with the annuités garanties in the representative design.
- guarantee_mths()[source]#
n: the annuités garanties in months, 0 or 60 to 300 in 60-month steps.
Instalments are certain for n months to the designated beneficiaries, at the same amount and rising with the same revalorisation index. No lump-sum commutation of the remaining term is offered.
- palier_scheme()[source]#
The rente par paliers scheme:
none,inc1,inc2,dec1ordec2.A rente par paliers is a level-within-step function of duration, not an escalation: nothing compounds and the steps are contractual percentages of the initial level.
- palier_step_years()[source]#
S: the length of the first palier step in years, 5 or 10; 0 when none.
The second step is “d’une durée égale” to the first, which is why one number parameterizes both.
- payment_timing()[source]#
Whether instalments fall in arrears (terme échu) or in advance.
Every retrieved French carrier pays terme échu;
advanceis retained as an unobserved model variant only. Arrears instalments require survival at the end of the payment month and advance instalments at the start of it; using end-of-period survival for advance payments understates the liability by roughly one period’s mortality per payment, which is material at annuitant ages.
- arrerage_charge_rate()[source]#
f: the frais d’arrérages retained out of each gross quittance.
A percentage of the payment, deducted per quittance d’arrérages and not off the annualised rente. At a flat percentage the two coincide; at a per-instalment cap or a flat per-instalment fee they do not, and the payment frequency then changes the total. Ranges from 0.00% to 3% across the retrieved carriers.
- technical_rate()[source]#
i: the taux technique the taux de rente was struck on.
It reaches the projection only through rho, by way of
annuity_factor(), and appears in no cash flow recursion. It is not a discount rate: the best estimate discounts at the risk-free term structure, and reusing i for that produces neither a price nor a reserve. It is carried so a reader can see which rate rho was struck on.
- mort_basis()[source]#
Whether the run is probability-weighted (table) or deterministic (scenario).
table runs the monthly recursion off the shipped generational table; scenario [std] replaces it with the step function
1{t <= death_mth(life)}so the notes’ worked example - which is a scenario, not an expectation - reproduces exactly. It never reaches the pricing side. See the Space docstring.
- death_mth(life)[source]#
The scenario month of death of
life; -1 if the life survives throughout.Read only when
mort_basis() == "scenario". The month index is on the projection frame’s own 0-based clock, so the shipped worked example dies in month 25, the 26th month of service. A blank cell in the model point table means the life never dies in the scenario and is returned as -1, a value no month can take: witht0-based, month 0 is a projectable event and cannot double as the sentinel. A death in month d is decremented at the end of that month, solives_if(t, life)- survival to time t - is 0 fromt = d + 1and the arrérage of month d is nevertheless due in full, as the prorata.
- duration(t)[source]#
Completed policy years at the start of month t:
duration_mth(t) // 12.0-based, as
durationis throughout lifelib: 0 through the first policy year.
- duration_mth(t)[source]#
Months elapsed from the effective date at the start of month t; equal to t.
tis 0-based and counted from the effective date, so the identity is trivial - the cells exists so the monthly models in this library share one vocabulary, and so thatduration(t) = duration_mth(t) // 12reads as lifelib writes it.
- policy_year(t)[source]#
The policy year containing month t; 1 for t = 0..11.
Nothing in this product happens on a policy anniversary: the paliers and the attained ages step on 12-month multiples of the effective date, and revalorisation and expense inflation step at 31 December. The cells exists for the shared vocabulary and for the palier boundaries.
- age(t, life)[source]#
The attained age (ALB) of
lifein month t:x(0) + t // 12[std].Age increments on each 12-month multiple of the effective date, not on a birthday: the model point carries an age last birthday at the effective date and a millésime, and neither fixes the birthday within the year.
- calendar_year(t)[source]#
The calendar year containing month t.
Derived from the effective date’s year and civil month,
Y0 + (M0 + t - 1) // 12,tbeing 0-based so that month 0 is the civil month of the effective date itself. It is not an argument to any mortality lookup: the table is generational.
- cal_year_index(t)[source]#
k(t): completed 31 Decembers strictly before the start of month t.
The annuity is in service for
13 - M0months of its first calendar year - monthst = 0 .. 12 - M0on the 0-based clock - sok(t) = 0whilet < 13 - M0and1 + (t - (13 - M0)) // 12afterwards. It counts 31 Decembers strictly before month t, because the uplift credited at 31 December reaches instalments payable from the following 1 January.check_calendar_index()asserts that this agrees with the calendar the model carries independently,calendar_year(t) - Y0.
- mort_rate_at_age(table_sex, gen, x)[source]#
q(s, g, x): the annual mortality rate of the shipped generational table.
A pure table lookup keyed on sex, millésime and attained age - no improvement factor, no rating factor, no calendar-year argument. Rates at and above the limiting age are 1.
table_sex = "mix"returns the [std] portfolio blendtheta q(M) + (1 - theta) q(F), which is a best-estimate device for a model point standing for a cohort and is never a tariff basis.
- mort_rate(t, life)[source]#
q: the annual best-estimate mortality rate applied to
lifein month t.mort_rate_at_age(sex(life), birth_year(life), age(t, life))and nothing else. The table is generational, so the trend is inside it and there is no improvement scale to apply on top; the projection year does not enter. This is the best-estimate rate, not the tariff rate - seeannuity_factor().
- mort_rate_mth(t, life)[source]#
q_m = 1 - (1 - q)^(1/12): the monthly mortality rate [std].
Uniform force within the year of age, which is the standard reading of an annual table on a monthly grid and is strictly below the annual rate wherever that is positive.
- lives_if(t, life)[source]#
l(t): the probability that
lifeis alive at time t, t = 0 at the outset.A time-point cells, not a period one:
l(0) = 1at the effective date, month t opens atl(t)and closes atl(t + 1), and no index is ever negative. On the table basisl(t) = l(t-1)(1 - q_m(t-1)), the rate of montht - 1decrementing over that month. On the scenario basis [std] the survival path is the step function1{t <= death_mth(life)}, with a negativedeath_mthmeaning the life survives the whole projection - which is what the notes’ worked example specifies. Returns 0 for life = 2 where no réversion is elected.
- lives_death(t, life)[source]#
d(t) = l(t) - l(t+1): the death density of
lifein month t.A period quantity built from the two time points month t runs between: the deaths of month t are decremented at its end.
- tariff_horizon_mths()[source]#
The months the tariff annuity factor runs over:
12 (omega - x_a0).The annuitant’s own horizon, not the projection’s: an annuity factor is a property of the life being priced.
- tariff_lives(t, table_sex)[source]#
The annuitant’s survival to time t on a pricing table, t = 0 at conversion.
A different object from
lives_if(): it runs on whichever table it is asked for, it always runs off the table, and it ignores the scenario switch entirely - a scenario is a statement about one realisation, and pricing is not. Likelives_if()it is a time-point cells, and the step fromt - 1tottakes the rate of montht - 1, at the attained ageage(t - 1, 1).
- annuity_factor(table_sex)[source]#
The annuity factor at conversion on
table_sex, per unit of annual rente.sum(l(t) v^t) / 12over the annuitant’s horizon, monthly in arrears, withv = (1 + i)^(-1/12)at the taux technique. This is the only place i enters the model, and it enters the pricing side, not a cash flow. At the shipped zero taux technique the factor is the residual life expectancy of the table.The sum runs over the payment times
t = 1 .. 12(omega - x_a0)on the pricing clock - the instants an arrears instalment falls due, counted from conversion - and not over the projection frame’s month indices, so it does not move with the frame.
- taux_rente_tariff()[source]#
The taux de rente the tariff table implies:
1 / (a x (1 + loading)).Struck on
tariff_table_sexfor every life, whatever the annuitant’s own sex, because the Code requires the single table applied to all lives to be the more prudent one.rate_loadingis the [std] margin between the pure factor and the quoted rate, at a level no source publishes. The shipped mortality proxy is anchored so that this reproduces the technical notes’ placeholder rho exactly at the worked configuration’s age and millésime.
- taux_rente_own_table()[source]#
The taux de rente the annuitant’s own table would imply.
The counterfactual the unisex rule forbids. It is not used in any cash flow; it exists so that
unisex_gap()can be read off the model rather than asserted.
- unisex_gap()[source]#
The income a life gives up to the unisex rule, as a fraction of what it receives.
taux_rente_own_table() / rho - 1. Positive for a male annuitant - about 13% on the shipped anchor - and it is the systematic technical surplus the eight-year profit-sharing rule requires to flow back to policyholders, which in this model it does through the revalorisation rate. Negative or zero for a female annuitant, whose own table is the tariff table.
- certain_excess_years()[source]#
The years of annuity factor the annuités garanties add, on the tariff table.
sum((1 - l(t)) v^t) / 12over the guaranteed months: the certain-period annuity factor exceeds the life factor by exactly the payments made to beneficiaries after a death inside the term. Zero where no guarantee is elected. Likeannuity_factor()this is a pricing sum over payment timest = 1 .. nand not over the projection frame.
- guarantee_coeff()[source]#
The definitive coefficient the annuités garanties cost,
a / (a + excess).[std]: no retrieved document publishes the cost of annuités garanties, so it is derived here from the tariff table by the only construction the notes give - the certain-period factor exceeds the life factor by the payments made after a death inside the term. Derived rather than carried as a model point column so that it cannot drift out of step with the table that implies it, and so that a 25-year term costs more than a 5-year one, which a single published figure cannot express. Substituting a licensed mortality basis therefore moves this coefficient too.
- reversion_coeff()[source]#
The definitive réversion coefficient, from the published [S6] table.
Keyed on the taux de réversion and on the difference in millésime between the reversionary and the annuitant, a positive difference meaning the reversionary is the younger life. The three published columns are 60%, 80% and 100%; a taux de réversion off that grid has no retrieved coefficient and raises rather than being guessed at. The reduction is definitive and applies to the annuitant’s own annuity once, at conversion: it does not also scale the reversion stream, which is delta times the already reduced annuity reached at death, and it is not released if the reversionary predeceases the annuitant.
- option_coeff()[source]#
kappa: the definitive coefficient the elected option costs; 1 with no option.
The réversion coefficient where a réversion is elected and the [std] guarantee coefficient where annuités garanties are. The options are not cumulative, so exactly one of them may be non-trivial and a model point carrying both raises rather than compounding two coefficients.
- annual_income_init()[source]#
A0 = C rho kappa: the gross annual rente at conversion.
Derived, not carried: rho and kappa are the two quantities a real barème computes, and the model shows the arithmetic rather than taking its answer as an input.
- revalo_factor(t)[source]#
R(t): the cumulative revalorisation index in month t; R = 1 while k(t) = 0.
Steps at each 31 December and reaches instalments payable from the following 1 January, never on a policy anniversary. The first step is pro-rated
nu (13 - M0)/12for the part-year of service, which degenerates to the full nu for a 1 January effective date. The uplift is floored at zero, the only contractual bound any retrieved document states, so R is non-decreasing. Thek(t) = 0branch is the base case of the recursion: it holds over the whole first partial calendar year, which always contains month 0, so no month before the first is ever read.
- palier_factor(t)[source]#
Pi(t): the rente par paliers step multiplier; 1 where no scheme is elected.
A step function of duration, not an escalation: nothing compounds, and the steps are contractual percentages of the initial level. The first step runs 12S months -
t = 0 .. 12S - 1- and the second, where the scheme has three levels, an equal further 12S months.
- annual_income(t)[source]#
A(t) = A0 R(t) Pi(t): the gross annualised rente in force in month t.
Deterministic given the assumption set: the annuity level does not depend on survival, only the payment factors do. The frais sur encours de rentes are not netted from it - they bite on the provision mathématique and reduce the profit-sharing base, hence nu, and never an instalment.
- is_payment_mth(t)[source]#
Whether month t is a payment date.
Arrears (terme échu): the k-th instalment falls at the end of month
12k/m - 1, sot = 2, 5, 8, ...at m = 4. Advance: it falls one full payment period earlier, at the start of month12(k-1)/m, so t = 0, 3, 6, … At m = 12 every month is a payment month on either convention.
- payment_surv_mth(t)[source]#
The time at which survival is measured for the instalment falling in month t.
An index into
lives_if(), which is a time-point cells. Arrears: the end of month t, timet + 1. Advance: the start of month t, timet, because an advance instalment falls at the start of the month; 0 for the first instalment, wherelives_ifis 1.
- annuity_pp(t)[source]#
A(t)/m: the scheduled arrérage per contract in month t, zero outside T.
This is the annuitant’s instalment. The reversionary’s is delta times it, on the rente atteinte - the same schedule - which is what “delta of the annuity reached at death” reduces to under a schedule that does not depend on survival.
- mths_since_payment(t)[source]#
h(t) = t mod (12/m): complete months since the last payment date, at the start of t.
The accrual base of the prorata d’arrérages. At m = 12 it is 0 for every t, so the prorata is a whole instalment; at m = 4 a death in the first month of a quarter gives h = 0 and settles one third of the quarterly instalment, the second month h = 1 and two thirds.
- prorata_pp(t)[source]#
The prorata d’arrérages accrued and unpaid at a death in month t.
((h(t) + 1)/(12/m)) A(t)/m: the arrears that have accrued up to and including the month of death, which belong to the heirs. At m = 12 that is exactly one full instalment, the French rule being that instalments cease from the 1st day of the month following the death. Zero on the unobservedadvancevariant [std], where the instalment covering the month of death was already paid at its start and nothing has accrued unpaid.
- certain_floor(t)[source]#
gamma(t) = 1{t < n}: the annuity-certain floor of the annuités garanties.
The guarantee covers the n months
t = 0 .. n - 1and is spent from month n.
- payment_factor_life(t)[source]#
l_a measured at the payment point: the annuitant’s survival factor alone.
- payment_factor(t)[source]#
max(gamma(t), l_a): the annuitant stream’s payment factor.
The
maxmakes the annuités garanties an annuity-certain floor rather than a second stream: while the guarantee runs the full instalment is payable regardless of survival, and an additive form would pay1 + l_afor the whole term.
- reversion_factor(t)[source]#
delta (1 - l_a(t)) l_r: the réversion stream’s payment factor.
The gate is
(1 - l_a(t)), the annuitant’s survival to the start of month t, and not(1 - l_a(t + 1)), its survival to the end: the survivor’s first instalment falls in the month after the month of death, immediately after the prorata d’arrérages has settled it. The only source that dates the reversion start gives it as the 1st day of the “month or quarter” following death, so a one-month gate at every payment frequency is a [std] reading of its monthly limb - exact at m = 12, and up to a quarter early on a quarterly contract. No shipped model point combines a réversion with m < 12. The survivor’s own survival is measured at the payment point. Zero where no réversion is elected.
- prorata_factor(t)[source]#
The probability-weight of a prorata d’arrérages settlement in month t.
d_a(t)(1 - gamma(t)) + delta (1 - l_a(t)) d_r(t): the annuitant’s own death, suppressed while the annuités garanties run because the full instalment is already payable there, plus the symmetric settlement on the reversionary’s death once the reversion stream is running - gated, likereversion_factor(), on the annuitant’s survival to the start of month t.
- cum_annuity_pp(t, kind)[source]#
G(t): cumulative gross arrérages per contract through month t.
"ANNUITANT"the deterministic as-if-alive annuitant schedule, which needs no path simulation precisely because it ignores survival. It is the rente the contract promises, and it is what a reader should compare a barème against.
"ALL"the expected total actually paid across both streams, including the prorata settled on death. On a probability-weighted run it is an expectation rather than a path; in a scenario run the two coincide for a surviving annuitant.
Month 0 is the first projected month, so the accumulation opens there rather than carrying a month-zero row:
G(0)is what month 0 itself pays.
- horizon_mths()[source]#
The number of months over which some covered life is still below the limiting age.
12 (omega - min x_i): the notes stop oncet/12 + x_i > omegafor every covered life, so the months projected aret = 0 .. 12(omega - min x_i) - 1, the last of them the last month of ageomega - 1for the youngest life. Stopping on the annuitant’s age alone would truncate a younger reversionary’s tail.
- proj_len()[source]#
Projection length in months: the mortality horizon, or the guarantee if longer.
The number of months projected, so the frame is
range(proj_len())and its last index isproj_len() - 1.
- annuity_payments(t)[source]#
E[ANN(t)]: expected arrérages outgo in month t.
A(t)/m x [max(gamma, l_a) + delta (1 - l_a) l_r], scaled bypols_if_init: the annuitant stream with its certain floor, plus the réversion stream.
- claims(t, kind=None)[source]#
Expected death-settlement outgo in month t, by kind; the total when kind is omitted.
"PRORATA"the prorata d’arrérages: the arrears accrued and unpaid at a death, which belong to the heirs. At m = 12 that is a whole instalment, so losing it understates the outgo by one full arrérage per death. It is the only death benefit this product has: the representative design is capital aliéné, with no death capital and no refund of any part of the capital constitutif.
- arrerage_charges(t)[source]#
E[FRA(t)]: the frais d’arrérages the insurer retains in month t.
f x (E[ANN(t)] + E[PRO(t)])- per quittance, on every payment including the prorata settled on death, and not on the annualised rente. Published as a positive column and subtracted inliability_cf(), because the insurer keeps it: it is the one component of this statement that runs the other way.
- pols_if(t)[source]#
IF(t): the probability that any payment obligation remains in month t.
min(1, max(gamma(t), l_a(t+1)) + 1{delta>0}(1 - l_a(t)) l_r(t+1))[std] - the guarantee certain, the annuitant alive, or the reversion stream in payment. Survival is read at the end of month t, the point the arrears instalment is measured at, and the reversion leg is gated on the annuitant’s survival to its start. This is the weight the maintenance expense is carried on, which is why it keeps the library’s name for the expense weight even though it is not a policy count.Note the one-month gap the notes’ own formula produces: in the month of an annuitant’s death the annuitant leg is already 0 and the reversion leg has not yet opened, so no maintenance expense is accrued in that month even though the prorata is being settled in it. The formula is implemented as the notes write it rather than smoothed, and the gap is one month of a EUR 30 a year expense.
- inflation_factor(t)[source]#
The expense inflation factor in month t:
(1 + pi)^k(t)[std].It steps at each 31 December like the revalorisation index, but without the first-year pro-rating: an expense base is restated at a full year’s inflation whenever the calendar year turns, whereas the pro-rating of the revalorisation is a contractual rule about a rente in service for part of a year.
- expenses(t)[source]#
E[EXP(t)]: maintenance expense in month t [std].
(c_e / 12)(1 + pi)^k IF(t): a round placeholder for in-payment administration, paid monthly while any payment obligation remains. No French insurer publishes expense assumptions. Note that at the composite’s 3% frais d’arrérages the charge on the worked configuration is about EUR 150 a year against this EUR 30, so the French charging structure recovers far more than in-payment administration and the balance funds distribution and margin. Acquisition cost is out of scope: the premium is single and the cost is priced in.
- liability_cf(t)[source]#
CF(t): total gross liability outgo in month t, the notes’ cash flow definition.
E[ANN] + E[PRO] - E[FRA] + E[EXP]. Outgo positive, the notes’ own sign. The frais d’arrérages carry a minus because the insurer retains them out of the payment. There is no premium income in the projection - the capital constitutif is a pricing input at the effective date - no surrender outgo, because the contract has no surrender value at any duration, and no death capital, the representative design being capital aliéné.
- net_cf(t)[source]#
Net cash flow to the insurer in month t: income less outgo, so
-liability_cf.Income positive, the sign convention every model in this library carries, kept even though this product has no projected income so that every model’s
net_cfcan be compared or summed across the library.liability_cf()carries the opposite, outgo-positive sign of the technical notes; both are published as columns ofresult_cf()rather than one being made to stand for the other.
- check_lives_roll_fwd_resid(t)[source]#
Residual between
lives_if()and an independently rebuilt survival path.Deliberately not the telescoping identity
l(t) - d(t) - l(t + 1):lives_death()is defined as that difference, so the identity is identically zero whateverlives_if()returns and constrains nothing. Each life’s survival is rebuilt here from the assumptions instead, with no reference to the recursion - on the table basis as the annual formprod (1 - q_x)over the completed years of age and(1 - q)^(rest/12)over the part-year, which the monthly recursion must telescope to. A recursion that reads the age one month early or late, or that indexes the table by the projection year instead of the millésime, breaks it.tis a time, as inlives_if(): the survival rebuilt here is the one to time t.
- check_lives_roll_fwd()[source]#
Whether the survival recursion closes at every projected month.
Takes no argument and returns a
bool, the library-wide shape of acheck_*cells, so one test can call the same check across every model. The signed residual of a failing month stays available ascheck_lives_roll_fwd_resid(). It sweeps the time points the frame reads,t = 0 .. proj_len(): the last projected month,proj_len() - 1, closes at timeproj_len().
- check_revalo_roll_fwd_resid(t)[source]#
Residual between
revalo_factor()and its closed form.The recursion steps whenever
k(t)advances; the closed form is(1 + nu (13 - M0)/12)(1 + nu)^(k - 1)fork >= 1and 1 fork = 0. The two are independent constructions of the same index, and the residual catches both of the notes’ revalorisation pitfalls: a step taken on the policy anniversary instead of at 31 December moveskby13 - M0months, and dropping the first-year pro-rating scales every later month by(1 + nu)/(1 + nu (13 - M0)/12)for the whole of the annuity’s remaining life, because R is a running product.
- check_revalo_roll_fwd()[source]#
Whether the revalorisation index closes against its closed form at every month.
- check_cum_annuity_roll_fwd_resid(t)[source]#
Residual between
cum_annuity_pp()on"ANNUITANT"and a direct sum.The recursion is rebuilt here as an explicit sum of the scheduled instalments over the payment months up to t, with no reference to the accumulation. A cumulation that starts at the wrong month, double-counts a payment month, or reads
annuity_ppatt - 1shows up here; the telescoping form would not, because it is the recursion written twice. The first payment month is12/m - 1in arrears and month 0 in advance.
- check_cum_annuity_roll_fwd()[source]#
Whether the cumulative arrérages schedule closes at every projected month.
- check_calendar_index_resid(t)[source]#
k(t) - (calendar_year(t) - Y0): two constructions of the same count.cal_year_index()counts the 31 Decembers from the length of the first partial calendar year,13 - M0;calendar_year()steps the year from the civil month of the effective date. They are independent and must agree at every month. An implementation that counts anniversaries instead of 31 Decembers disagrees from the first turn of the year.
- check_calendar_index()[source]#
Whether the calendar index agrees with the calendar at every projected month.
- check_payment_factor_resid(t)[source]#
payment_factor(t) - max(gamma(t), l_a): the guarantee double-count guard.Zero by construction, and asserted anyway: an additive floor -
gamma + l_ainstead ofmax(gamma, l_a)- is a listed pitfall and would pay1 + l_afor the whole guaranteed term. It would show up here asmin(gamma, l_a).
- check_payment_factor()[source]#
Whether the guarantee stays a floor rather than a second stream, at every month.
- check_revalo_floor()[source]#
Whether the revalorisation index is non-decreasing at every month.
The contractual floor on the uplift is zero in every retrieved formulation, so a rente in payment can rise and can stand still but can never fall. A negative
revalo_ratefed into the model must therefore leave the annuity where it was rather than cutting it. The sweep starts at month 1, month 0 having no predecessor.
- check_options_xor()[source]#
Whether the contract carries at most one of a réversion and annuités garanties.
The representative design offers one or the other, never both, and the coefficients are definitive reductions of the same annuity: compounding two of them would price an option pair no retrieved carrier sells.
- check_commutation_floor()[source]#
Whether the gross quittance clears the statutory commutation threshold.
A0 Pi(0) / m > 110 x (12/m): below it the insurer may, with the annuitant’s agreement, pay a capital instead, so there is no annuity to project. This is an admission test, not a cash flow - the one policyholder election that survives conversion - and a model point that fails it is a defect in the table rather than a projection with a small answer.
- check_taux_rente()[source]#
Whether the carried taux de rente agrees with the one the tariff table implies.
The shipped mortality proxy is anchored so that
taux_rente_tariff()reproduces the technical notes’ placeholder rho exactly at the worked configuration, and every other model point carries the rate the same construction gives at its own age, millésime and taux technique, rounded to six decimals. The check ties the model point table to the mortality table: editing one without the other fails here rather than silently pricing on a basis the projection does not use.
- result_cf()[source]#
Result table of cashflows, indexed by month t.
pols_ifis the probability any payment obligation remains, which is the expense weight rather than a policy count.arrerage_chargesis the one column the insurer retains: it is positive here and subtracted inliability_cf. Both signs of the net flow are published:net_cfis income-positive, the library-wide convention, andliability_cfis the technical notes’ outgo-positiveCF(t).The index is the 0-based month,
t = 0 .. proj_len() - 1.
- result_pols()[source]#
Result table of survival probabilities and payment factors, indexed by month t.
lives_if_1andlives_if_2are the survival probabilities to the end of month t,lives_if(t + 1, life):lives_if()is a time-point cells and this is a table of periods, so the row carries the closing value, which is also the point an arrears instalment is measured at. The index ist = 0 .. proj_len() - 1.