The Projection Space#
The by-policy projection of the WP_UK_A 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's scenario A
>>> Projection.point_id = 2 # scenario B, the down market
t counts policy years from issue, 1-based, so an in-force model point at
duration 5 starts at t = proj_start() = 6 — which is the year the notes’ worked
example projects. State carried into that year (the asset share, the smoothed payout,
the unit price) is indexed at proj_start() - 1.
Input data
Inputs are external files: plain CSVs living in the model folder’s parent
directory, products/with_profits/, 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
WP_UK_A 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 |
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
rates, *_pp for per-policy amounts, claims(t, kind) and claim_pp(t, kind)
with an uppercase kind string. The technical notes use compact symbols instead. The
mapping is:
Notes symbol |
Cells |
Meaning |
|---|---|---|
chassis |
chassis() |
UWP_bond or CWP_endowment |
t |
(the cells argument) |
Policy year |
x |
age_at_entry() |
Entry age (ANB) |
x + t - 1 |
age(t) |
Attained age (ANB) |
duration_ifo |
duration_inforce() |
Completed years at valuation |
(none) |
proj_start() |
First projected policy year |
n |
policy_term() |
Endowment term |
(none) |
proj_len() |
Last projected policy year |
(none) |
fund_exhaust_year() |
Year the bond’s units run out |
(none) |
is_forced_encashment() |
Whether the run ends there |
P(t) |
premium_pp(t) |
Premium received at BOY |
W(t) |
wd_pp(t) |
Partial withdrawal at BOY |
W_AS(t) |
wd_as_pp(t) |
Asset-share reduction for it |
r(t) |
fund_return() |
Earned fund return |
c_amc |
amc_rate |
Annual management charge |
c_g |
guar_charge_rate(t) |
Guarantee/smoothing charge |
CumGC(t) |
guar_charge_cum_pp(t) |
Cumulative guarantee charge |
AS(t) |
asset_share(t) |
Asset share at end of year t |
(the steps) |
asset_share_at(t, timing) |
The asset share inside year t |
M(t) |
misc_surplus_pp(t) |
Estate distributions; 0 in base |
Q(t) |
unit_price(t) |
With-profits unit price |
U(t) |
units(t) |
Units held |
FV(t), G(t) |
guar_benefit_pp(t) |
Guaranteed benefit |
(pre-MVR value) |
policy_value_pp(t) |
Guaranteed benefit + final bonus |
b(t), b_rev(t) |
bonus_rate(t) |
Declared bonus rate |
b_supp |
bonus_supportable(t) |
Rate the guarantee-fill implies |
theta, kappa |
guar_fill_target, bonus_speed |
Bonus-rule parameters |
CB(t) |
cost_of_bonus_pp(t) |
Cost of the declared bonus |
ST(t) |
shareholder_transfer_pp(t) |
CB/9, the 90:10 transfer |
MC(t) |
mort_charge_pp(t) |
Mortality charge to the AS |
DB_g(t) |
death_guar_pp(t) |
Guaranteed death benefit |
q(x+t-1) |
mort_rate(t) |
Mortality rate |
w(t) |
surr_rate(t) |
Surrender rate, all multipliers |
(table) |
surr_rate_base(t) |
Table surrender rate |
sigma |
smooth_cap |
Year-on-year smoothing cap |
S(t) |
smoothed_payout(t) |
Smoothed target payout |
(cap step) |
smoothed_payout_capped(t) |
After the cap, before the corridor |
FB(t), TB(t) |
final_bonus_pp(t) |
Final or terminal bonus |
MVR(t) |
mvr_pp(t) |
Market value reduction, unapplied |
(applied) |
mvr_applied_pp(t) |
Zero where the exit is MVR-free |
(guarantee dates) |
is_guarantee_date(t) |
MVR-free anniversary |
g_db |
death_benefit_factor |
Bond death uplift, 1.01 |
i_sv, v_sv |
surr_disc_rate |
Endowment surrender discount |
l(t) |
pols_if(t) |
In force at the start of year t |
(none) |
pols_if_at(t, timing) |
BEF_DECR / BEF_SURR / AFT_DECR |
(none) |
pols_death(t) |
Deaths in year t |
(none) |
pols_surr(t) |
Surrenders at the end of year t |
(none) |
pols_maturity(t) |
Maturities, or the truncation |
(payouts) |
claim_pp(t, kind) |
Payout per claim by kind |
SM(t) |
smoothing_account(t) |
Cumulative smoothing cost |
(cash flows) |
premiums, claims, withdrawals |
Probability-weighted flows |
E(t) |
expenses(t) |
Maintenance expense |
ST x l |
shareholder_transfers(t) |
Transfer outgo |
(none) |
net_cf(t) |
Net cash flow, income positive |
Four names needed care.
G is the guaranteed benefit on the endowment chassis and FV the unit face value
on the bond chassis, but every rule that consumes them — the bonus cost, the mortality
charge sum at risk, the final bonus, the MVR — treats them identically. They are
therefore one cells, guar_benefit_pp(), and the chassis decides how it rolls
forward. Keeping two names would have duplicated five rules to no purpose.
MVR is computed whether or not it applies: mvr_pp() is the scale and
mvr_applied_pp() is what an exit actually bears, which is zero on death and on a
guarantee date. Both are needed, because the behavioural deterrent keys off the scale
being positive while the payout keys off what is applied.
FB and TB are the same quantity — the non-guaranteed top-up from the smoothed
payout to the guaranteed benefit — under two names, one per chassis. They are
final_bonus_pp() here.
There is no av_pp_at in this model, and that is a product statement rather than
an omission. The asset share is a shadow retrospective accumulation that the
policyholder never owns and is never paid; the guaranteed benefit is not a fund either.
Naming either of them the library’s account value would assert something false about
the contract.
The asset share is a state variable, not a cash flow
AS(t) = [AS(t-1) + P(t) - W_AS(t)] (1 + r(t)) (1 - c_amc - c_g) - ST(t) - MC(t) + M(t)
Every item in it is a recorded deduction from or addition to a retrospective
accumulation, and none of them is a policy cash flow. The policy’s actual flows are
premiums, claims, withdrawals, expenses and the shareholder transfer; the asset share
reaches them only through the bonus, smoothing and MVR rules, and the difference between
what is paid and what the asset share says is absorbed by the estate. That difference is
tracked in smoothing_account(), which the base model accumulates without recycling.
asset_share_at() exposes the recursion one step at a time — BEF_RETURN,
AFT_RETURN, AFT_CHARGE, AFT_ST, AFT_MC — because the order is
contractual discipline rather than arithmetic convenience: the shareholder transfer is
charged to asset shares after the charges and before the mortality charge, and the
mortality charge’s sum at risk is measured on the balance after the transfer.
The bonus hardens, and that is what makes guarantees expensive
A declared regular bonus increases the guaranteed benefit permanently. The unit price
therefore never falls — b(t) >= 0 is a contractual floor, not a modelling choice —
and every declaration converts non-guaranteed final bonus into guaranteed benefit
without changing the target payout. That is the whole tension the discretion manages,
and it is why guar_fill_target and bonus_speed are genuine modelling
choices with no public calibration rather than parameters someone measured.
The base projection holds the model point’s snapshot rate level, as the notes specify.
bonus_supportable() and the smoothed setting rule are implemented and switched off
behind bonus_rule_on, so the revision module is available for scenario work
without disturbing the reproduction of the worked example.
Smoothing: the cap, then the corridor
S_raw = AS(t)
S_cap = clamp(S_raw, (1-sigma) S(t-1), (1+sigma) S(t-1))
S(t) = clamp(S_cap, 0.80 AS(t), 1.20 AS(t))
The year-on-year cap is applied first and the target corridor second, and the
order matters: the cap is what stops a market shock reaching payouts in one step, and
the corridor is what stops the cap holding a payout indefinitely away from the asset
share. In the notes’ down scenario the cap binds at -10% and the corridor then does
not, which is exactly the pattern the two rules are designed to produce.
The corridor implements the 80-120% target range at model-point level. The regulatory test is a portfolio property — a proportion of policies within the range — and a single-policy model cannot express it, so the deterministic corridor is a [std] reading of it.
Two things the cap cannot say. It is skipped in the first projected year of a
new-business cell, where S(t-1) = 0 would clamp the payout to nil. And on a
premium-paying policy it is only loosely meaningful: a firm’s ±10% discipline is a
like-for-like comparison between successive maturity cohorts — this year’s payout on a
25-year endowment against last year’s — not a comparison of one policy’s own payout
across its own durations. A regular-premium asset share grows far faster than 10% a year
in early durations because premiums, not investment return, dominate it, so the cap
binds throughout and the corridor floor is what actually sets the payout. On the
endowment cell shipped here that persists to about duration 14, after which the asset
share is large enough that the cap stops binding and the payout converges to it: 80% of
the asset share at duration 1, 100.0% at maturity. The single-premium bond the worked
example uses has no such problem, which is why the notes can state the cap plainly.
Final bonus and MVR are never simultaneous
FB > 0 requires S > FV and MVR > 0 requires S < FV, so the two cannot
both be positive. check_fb_mvr_exclusive() asserts it, because an implementation
that computed them independently could produce both and would then pay a final bonus and
deduct a market value reduction on the same exit.
The MVR also carries a contractual bound: it may not exceed the excess of the unit
value over the underlying asset value, which is max(0, FV - AS).
check_mvr_bound() asserts that too. In the notes’ down scenario the bound is
2,704.05 and the MVR actually applied is 1,328.04 — comfortably inside it, which is the
point of checking rather than assuming.
The MVR is unitised only. It is an adjustment to a unit value, and the notes
define it for the unitised chassis alone; a conventional endowment has no units to
reduce. Applying the same arithmetic there would be arithmetically harmless — it happens
to collapse the surrender payout onto the asset share — but it would report a £19,575
“market value reduction” in policy year 1 of a 25-year endowment, which is not a thing
that exists. mvr_pp() returns zero on that chassis and claim_pp() sets the
surrender value on a surrender basis instead.
What a deterministic run cannot do
This is a deterministic single-scenario projection, and it materially understates the
cost of guarantees, because guarantee cost is convex in the fund return: the average
of the cost over scenarios exceeds the cost at the average scenario. The c_g charge
in the asset share recursion is a charging proxy — a deduction firms make — and not a
valuation of anything. What this model produces is exactly the per-scenario cash flow
vector a market-consistent stochastic valuation consumes; the stochastic layer is out of
scope and is the reason the notes list a deterministic base run as the central model
risk.
Behaviour, where the anti-selection lives
Three multipliers sit on the base surrender rate, all [std] and all rationalized from the incentive structure rather than measured:
an MVR deterrent of 0.6 while an MVR would be applied — an active MVR penalizes exit;
a guarantee-date spike of 2.5 in a guarantee-date year, applied only when the guarantee is in the money (
GB > AS), because MVR-free encashment is worth exercising precisely then and worth nothing otherwise; anda guarantee-imminent suppression of 0.8 in the year before a guarantee date, policyholders waiting for the MVR-free window.
The second is the one that matters. Anti-selective exit when guarantees are in the money is the dominant behavioural risk on with-profits business, and dynamic assumptions of this kind are a regulatory expectation for the best estimate rather than an optional refinement.
The MVR deterrent applies on the bond chassis only, because there is no MVR on the other
one. That falls out of mvr_applied_pp() rather than being coded as a special case.
A withdrawal election is not unconditional
The MVR-free allowance is 5% of the original premium a year, and the withdrawing cell
takes the whole of it every year. Against a fund whose growth is only the declared
bonus, that exhausts the fund: the shipped cell cancels its last unit in policy year 34.
wd_pp() therefore caps the withdrawal at the unit fund it comes out of, and
proj_len() stops the projection the year before exhaustion, where
is_forced_encashment() marks the ending as a real contractual event and the
survivors are paid FV + FB rather than nothing. check_fund_nonneg() asserts the
result, because the failure mode here is silent: an uncapped election turns the unit
holding negative and every number downstream of it stays plausible enough to read past.
What is out of scope, and why
The smoothed-fund (PruFund-style) chassis is not implemented. Its mechanics are
daily and quarterly — a 5% daily and 10% quarterly smoothing limit with a 2.5% gap
trigger — and an annual grid smooths away the very limits that define the design.
Implementing it here would produce something that ran and meant nothing, so
chassis() accepts the two chassis the annual grid can carry and says so.
Also out of scope, per the notes: paid-up conversion on the endowment chassis, the guaranteed annuity option module on legacy pension cells (long interest-rate optionality that needs the stochastic layer to mean anything), estate reattributions and special bonuses, and the fund-level excess of actual expenses over capped charges, which a single-policy model cannot see.
Cells Descriptions#
- chassis()[source]#
UWP_bond(unitised) orCWP_endowment(conventional).The smoothed-fund chassis of the notes is not implemented: its smoothing limits are daily and quarterly, and an annual grid smooths away the very mechanics that define it. See the Space docstring.
- is_unitised()[source]#
True on the unitised bond chassis, where the guaranteed benefit is a unit value.
- duration_inforce()[source]#
Completed policy years at the valuation date; 0 on a new-business cell.
The worked example is an in-force bond at duration 5, so its projection starts in policy year 6 and the state it carries in is indexed at year 5.
The single premium of the bond chassis; a pricing input on an in-force cell.
The regular annual premium of the endowment chassis.
- sum_assured()[source]#
The basic sum assured of the endowment chassis; the guarantee floor at outset.
- attaching_bonus()[source]#
Reversionary bonuses already attaching at the valuation date (endowment chassis).
- unit_price_init()[source]#
Q: the with-profits unit price at the valuation date (bond chassis).
On the anchor cell it is
1.02^5 = 1.104081- five declarations at 2% on a unit seeded at £1.0000.
AS: the asset share carried into the projection.
A retrospective accumulation, and the model point’s most important number: it is what the payout machinery is measured against, and nobody is ever paid it.
- smoothed_payout_init()[source]#
S: the smoothed payout carried in; the benchmark the year-on-year cap works from.
On the anchor cell it is £29,500 against an asset share of £30,000 - the payout is already a little below the asset share, which is what the smoothing cap does after a good year.
- guarantee_years()[source]#
The policy anniversaries at which an exit is MVR-free, as a tuple of years.
Read from a semicolon-separated model point column, empty on the endowment chassis.
- wd_rate()[source]#
The partial withdrawal taken each year, as a fraction of the original premium.
Zero in the base run; the MVR-free allowance is 5% a year, and a withdrawing cell takes the whole of it.
- tax_basis()[source]#
life_netorpension_gross.The fund return is quoted net of life-fund tax on a
life_netcell and gross on a pension one; asset shares are accumulated on the basis that applies to the policy. The distinction is carried on the model point and is applied by supplying the return already on the right basis, rather than by grossing up inside the model.
- fund_return()[source]#
r: the earned fund return, on the model point’s tax basis [std].
A scenario level rather than a best estimate: the notes’ worked example is a one-year, two-scenario comparison, and the shipped scenario cells hold their own return for the whole projection, so the first projected year reproduces the notes exactly and the remainder shows what that scenario implies if sustained.
Asset shares, final bonuses and MVR incidence all key off this one number, and a deterministic run understates guarantee cost because the cost is convex in it.
Read the down cell’s tail for what it is. A single year at -15% is a market shock; sixty consecutive years at -15% is not a scenario anyone would value against, and the cell duly exhausts its asset share and leaves the guarantee entirely estate-funded. That end of the projection is a demonstration of the machinery under stress, not a result.
- bonus_rate_init()[source]#
The declared regular or reversionary bonus rate the model point carries.
2.00% on the bond chassis and 1.50% compound on the endowment, both [std]: declarations are not published in firms’ principles and practices documents.
- fund_exhaust_year()[source]#
The first projected year in which the bond’s unit fund is exhausted; 0 if never.
A level withdrawal election runs the unit holding down, and against a fund whose growth is only the declared bonus it eventually cancels the last unit. This locates that year so
proj_len()can stop there. Zero on the endowment chassis and on any bond cell taking no withdrawals, which is the ordinary case.
- proj_len()[source]#
The last projected policy year.
The endowment’s term, or the whole-of-life bond’s limiting age - cut short where a withdrawal election has exhausted the unit fund, since a bond with no units is not a bond.
- is_forced_encashment()[source]#
Whether the bond’s projection ends because the unit fund has been exhausted.
It changes what the survivors at the end of the projection are paid. A fund- exhaustion ending is a real contractual event - the last units are cancelled and the bond is encashed - so the survivors are paid out. A limiting-age ending is a modelling truncation, and paying anything there would invent a claim.
- is_guarantee_date(t)[source]#
Whether policy year t ends on a contractual guarantee date.
An exit there is MVR-free and pays the full guaranteed benefit plus final bonus, which is the option the guarantee-date surrender spike is exercising.
P(t): the premium received at the start of policy year t.
The regular premium on the endowment chassis, plus the single premium in policy year 1 - which an in-force cell never reaches, so it is not double counted.
- wd_pp(t)[source]#
W(t): the partial withdrawal paid at the start of policy year t.
Taken as a fraction of the original premium, which is how the MVR-free allowance is expressed, and zero in the base run. Within the allowance it is MVR-free.
Capped at the unit fund it is cancelled out of,
FV(t-1). The cap is a physical constraint rather than a product rule: a level withdrawal against a fund that is being run down eventually exhausts it, and an uncapped election drives the unit holding - and with it the guaranteed benefit - negative. Note what the cap is measured against: the unit fund, not the pre-MVR policy value, because a partial withdrawal cancels units and the final bonus is only paid on full encashment. The residual final bonus reaches the policyholder in the encashment thatis_forced_encashment()marks.
- wd_as_pp(t)[source]#
W_AS(t): the asset-share reduction for the year’s withdrawal.
Pro rata to the pre-MVR policy value, so a withdrawal takes the same proportion of the asset share as it takes of what the policy is worth - not the same cash amount. Zero where nothing is withdrawn or the policy value is nil.
- guar_charge_rate(t)[source]#
c_g: the guarantee and smoothing charge on the asset share in year t [std].
0.10% a year, and it stops once cumulative deductions reach the lifetime cap of 2% of the asset share. The cap test is measured on the previous year’s asset share rather than the year’s own, which is what keeps the charge from depending on the balance it is being deducted from.
Note what the cap is a fraction of. The notes set it against the current asset share, not against a level struck once at first breach, so on a fund that keeps growing the threshold grows with it: the charge stops the year the cumulative overtakes it and resumes the year after, when the larger asset share has moved the threshold back above. That is the rule as written; a cap frozen at first breach would be a different rule and a materially different charge.
- guar_charge_cum_pp(t)[source]#
CumGC(t): cumulative guarantee and smoothing deductions to the end of year t.
- misc_surplus_pp(t)[source]#
M(t): estate distributions credited to the asset share; zero in the base run.
Miscellaneous surplus and estate distributions are allocated annually where a firm operates them; the base model allocates none.
The asset share at a point inside policy year t.
"BEF_RETURN"AS(t-1) + P(t) - W_AS(t), after the start-of-year premium and withdrawal."AFT_RETURN"after the year’s fund return.
"AFT_CHARGE"after the annual management charge and the guarantee charge.
"AFT_ST"after the shareholder transfer, which is charged to asset shares. This is the balance the mortality charge’s sum at risk is measured against.
"AFT_MC"after the mortality charge and any estate distribution; the year-end asset share, and the same number as
asset_share().
The steps are exposed individually because their order is contractual discipline rather than arithmetic convenience.
AS(t): the asset share at the end of policy year t.
A shadow retrospective accumulation: nobody owns it and nobody is paid it. It drives claim amounts only through the bonus, smoothing and MVR machinery, and the difference between what is paid and what it says is absorbed by the estate.
Floored at zero. The charges and the mortality charge are deductions that do not stop when the balance runs out, so a sustained adverse scenario - the shipped down cell holds its -15% for the whole projection - drives the raw recursion negative. A negative asset share would make the payout target negative and invert the corridor, whose bounds are
0.80 ASand1.20 AS. What a nil asset share actually means is that the fund backing the policy is exhausted and the guarantee is being met entirely by the estate, which is whatsmoothing_account()then records.
- unit_price(t)[source]#
Q(t): the with-profits unit price at the end of policy year t.
Q(t) = Q(t-1)(1 + b(t)), and it never decreases - the non-negative bonus is a contractual floor, which is what makes a declaration irreversible. Constant on the endowment chassis, where the guarantee is carried as an amount rather than a price.
- units(t)[source]#
U(t): the units held at the end of policy year t.
Bought with the year’s premium at the previous price and cancelled to fund the year’s withdrawal. Constant in the base run, where the bond is single premium and nothing is withdrawn. Nil on the endowment chassis, which carries its guarantee as an amount rather than as units at a price.
- guar_benefit_pp(t)[source]#
The guaranteed benefit at the end of policy year t.
The unit face value
U(t) Q(t)on the bond chassis, and the sum assured plus attaching reversionary bonusesG(t) = G(t-1)(1 + b(t))on the endowment. Two contractual forms, one cells: every rule that consumes it - the bonus cost, the mortality charge’s sum at risk, the final bonus, the MVR - treats them identically, so keeping two names would duplicate five rules to no purpose.
- policy_value_pp(t)[source]#
The pre-MVR policy value: the guaranteed benefit plus any final bonus.
What a withdrawal is taken pro rata to, and what an MVR is measured against.
- bonus_supportable(t)[source]#
b_supp: the level bonus rate that fills the guarantee to the target [std].
Project the asset share to the horizon at the expected net return, take
guar_fill_targetof it, and solve for the level rate that grows the current guaranteed benefit to that amount:b_supp = [theta AS_proj / GB(t)]^(1/m) - 1
with
mthe remaining endowment term or the bond’s bonus-setting horizon. Future premiums are accumulated to the horizon at the same net return. Read only whenbonus_rule_onis set.
- bonus_rate(t)[source]#
b(t): the regular or reversionary bonus rate declared for policy year t.
The model point’s snapshot rate, held level, which is what the notes’ base projection does. With
bonus_rule_onset, the smoothed setting rule applies instead:b(t) = max(0, b(t-1) + clamp(kappa (b_supp - b(t-1)), -1%, +1%))
The floor at zero is contractual - a declared bonus can be nil but never negative - and the plus or minus one percent is the gradual-change discipline firms state in their principles and practices.
- cost_of_bonus_pp(t)[source]#
CB(t): the cost of the bonus declared in policy year t [std].
On the bond chassis it is the face-value uplift the declaration delivers,
b(t) FV(t-1). On the endowment it is the declared addition to the guarantee discounted to the declaration date at the surrender-basis rate, since the addition is not payable until maturity; the survivorship discount is omitted [std].
ST(t) = CB(t)/9: the 90:10 shareholder transfer, charged to the asset share.
One ninth of the cost of bonus, so that shareholders receive a tenth of each distribution and policyholders nine tenths. It is a real cash outflow from the fund and is reported as its own line, not netted into anything.
- mort_rate(t)[source]#
q(x+t-1): the annual best-estimate mortality rate in policy year t [std].
The shipped table rate times
mort_be_factor. Both are placeholders: CMI tables issued after March 2013 are subscriber-restricted, so the table is an ONS-shaped proxy and the factor a crude allowance for population mortality being heavier than insured experience.
- death_guar_pp(t)[source]#
DB_g(t): the guaranteed death benefit, the mortality charge’s sum-at-risk top.
g_db x FV(t)on the bond chassis, where the death benefit carries a 101% uplift, andG(t)on the endowment. Only the guaranteed element enters the sum at risk [std]: the final bonus is not guaranteed, so charging for it would charge the asset share for a benefit the fund has not promised.
- mort_charge_pp(t)[source]#
MC(t): the mortality charge deducted from the asset share in policy year t.
q x max(0, DB_g(t) - AS_after_ST): the mortality rate times the sum at risk, measured on the balance after the shareholder transfer. Differences between charged and actual mortality accrue to the estate, which is why this is a charge rather than a claim.
- smoothed_payout_capped(t)[source]#
S_cap: the asset share after the year-on-year smoothing cap, before the corridor.
clamp(AS(t), (1 - sigma) S(t-1), (1 + sigma) S(t-1))atsigma = 10%. This is what stops a market shock reaching payouts in one step, and it is applied before the corridor.The cap is skipped where there is no previous payout to compare against - the first year of a new-business cell, where
S(t-1) = 0would otherwise clamp the payout to nil and leave the corridor to do all the work. See the Space docstring on what the cap can and cannot say about a premium-paying policy.
- smoothed_payout(t)[source]#
S(t): the smoothed target payout at the end of policy year t.
The capped value, then clamped into the target corridor of 80% to 120% of the asset share. The corridor is what stops the cap holding a payout indefinitely away from the asset share; the order - cap first, corridor second - is what produces the pattern the two rules are designed for.
The regulatory target-range test is a portfolio property, a proportion of policies within the range, which a single-policy model cannot express; the deterministic corridor is a [std] reading of it.
- final_bonus_pp(t)[source]#
FB(t) or TB(t): the final or terminal bonus,
max(0, S(t) - GB(t)).The non-guaranteed top-up from the guaranteed benefit to the smoothed payout, and the form the discretion keeps a substantial proportion of the payout in - precisely because it is the part that has not hardened.
- mvr_pp(t)[source]#
MVR(t): the market value reduction scale in policy year t.
min(max(0, FV - S), max(0, FV - AS)). The first argument recovers the shortfall of the smoothed payout below the unit face value; the second is the contractual bound - the reduction may not exceed the excess of the unit value over the underlying asset value.This is the scale, not what an exit bears:
mvr_applied_pp()is zero where the exit is MVR-free. Both are needed, because the behavioural deterrent keys off the scale being positive while the payout keys off what is applied.Unitised chassis only. A market value reduction is a unit-linked-style adjustment to a unit value, and the notes define it for the unitised chassis alone; a conventional endowment has no units to reduce, and its surrender value is set on a surrender basis instead - see
claim_pp(). Returning zero here rather than applying the same arithmetic keeps the result table honest: on a conventional policy the face value is a maturity guarantee decades away, soFV - Sis a large number that means nothing.
- mvr_applied_pp(t)[source]#
The market value reduction an exit in policy year t actually bears.
Zero on a guarantee date, where the contract promises the full guaranteed benefit without reduction, and zero on death. The death case is handled in
claim_pp()rather than here, because only the surrender payout reads this. Zero throughout on the endowment chassis, which has no units to reduce.
- claim_pp(t, kind)[source]#
The payout per claim in policy year t, by kind.
"DEATH"g_db (FV + FB)on the bond chassis - the 101% uplift applies to the whole payout - andG + TBon the endowment. Never MVR’d: an MVR is not applied on death on either chassis."SURRENDER"FV + FB - MVR_appliedon the bond chassis, the non-guaranteed exit. On the endowment chassis there is no MVR, and the surrender value targets the smoothed payout - the asset share under the smoothing discipline - capped at the prospective valueG + TB, which is what the policy would be worth if it ran to maturity. Early surrender values are therefore well below the guaranteed maturity benefit, as they are on a real conventional policy."GUARANTEE"GB + FB, what a guarantee-date exit pays. Computed at everytso the two can be compared, but paid only whereis_guarantee_date()- on those years it equals the surrender payout, because the MVR is not applied. The endowment chassis has no guarantee dates, so this is informational there."MATURITY"G(n) + TB(n)at the end of the endowment term. On the bond chassis, which is whole of life, this is zero unless the projection ends in a forced encashment - the withdrawal election has cancelled the last unit - in which case the survivors are paidFV + FB, the residual final bonus included. A limiting-age ending pays nothing, because it is a modelling truncation rather than a contractual event.
- smoothing_cost_pp(t, kind)[source]#
The excess of a payout over the asset share, borne by the estate.
Positive where the smoothing or a guarantee has paid more than the policy earned, negative where it has paid less. Intended broadly neutral over time; the base model accumulates the balance in
smoothing_account()without recycling it.
- surr_rate_base(t)[source]#
The table annual surrender rate in policy year t [std].
Read from the chassis’s own row of the lapse table; policy years beyond the table take its last row. A drafting construction: no public UK with-profits lapse experience was retrieved.
- mvr_deterrent(t)[source]#
0.6 while an MVR would be applied, 1 otherwise [std].
An active market value reduction penalizes exit, and firms may consider exit volumes when setting reductions inside the contractual bound.
- guarantee_spike(t)[source]#
2.5 in a guarantee-date year when the guarantee is in the money [std].
The gate matters: MVR-free encashment is worth exercising precisely when the guaranteed benefit exceeds the asset share and worth nothing otherwise, so applying the spike unconditionally would invent anti-selection where there is none. This is the dominant behavioural risk on with-profits business.
- guarantee_imminent(t)[source]#
0.8 in the year before a guarantee date [std]: policyholders wait for it.
- surr_rate(t)[source]#
w(t): the annual surrender rate applied at the end of policy year t.
The table rate times all three behavioural multipliers, capped at 1.
- pols_if_at(t, timing)[source]#
The number of policies in force at a point inside policy year t.
"BEF_DECR"the start of the year, before any decrement;
pols_if()."BEF_SURR"after deaths, before surrenders - the processing order is death before surrender [std].
"AFT_DECR"the end-of-year count, and zero in the final projected year, where the endowment matures and the bond projection is truncated.
- pols_maturity(t)[source]#
Survivors at the end of the projection: maturities, or the bond’s truncation.
On the endowment chassis these are genuine maturities and are paid. On the bond chassis they are paid where the projection ends in a forced encashment and pay nothing where it ends at the limiting age - see
is_forced_encashment().
Premium income at the start of policy year t, an inflow.
- withdrawals(t)[source]#
Partial withdrawals paid at the start of policy year t.
An owner election rather than a claim, which is why it has its own name and column.
- claims(t, kind=None)[source]#
Benefit outgo in policy year t, by kind; the total when kind is omitted.
"DEATH","SURRENDER"and"MATURITY"weightclaim_pp()by the corresponding decrement."GUARANTEE"is not a separate outgo: a guarantee-date exit is a surrender, paid MVR-free, so it is already inside the surrender line.
- expenses(t)[source]#
E(t): the maintenance expense in policy year t [std].
£30 a policy a year inflating at 3%. Where a fund’s actual expenses exceed the capped charge taken from asset shares, the excess falls to the estate - a fund-level flow a single-policy model cannot see.
The 90:10 shareholder transfer paid out of the fund in policy year t.
The transfer on the year’s declared bonus, weighted by the in force, plus a ninth of the final bonus actually paid on the year’s claims - the same 90:10 split applied at the point the non-guaranteed part is handed over.
- smoothing_cost(t)[source]#
The estate’s smoothing and guarantee cost on the year’s exits.
Each exiting policy is paid its smoothed payout while the asset share it earned is released; the difference falls on the estate. Positive in a year when guarantees or smoothing pay more than the policies earned.
- smoothing_account(t)[source]#
SM(t): the cumulative smoothing and guarantee cost borne by the estate.
Intended broadly neutral over time. The base model tracks the balance without recycling it into credited returns; one insurer operates that recycling, feeding it back subject to a maximum annual deduction from asset shares [S5].
- net_cf(t)[source]#
The net cash flow of policy year t, income positive.
Premiums less claims, withdrawals, expenses and shareholder transfers. The asset share appears nowhere in it: it is a state variable, not a cash flow, and the payouts it drives are already in
claims.
- check_pols_roll_fwd_resid(t)[source]#
The in-force roll-forward residual in policy year t; zero everywhere.
The asset share recursion residual in policy year t; zero everywhere.
AS(t) - max(0, {[AS(t-1) + P - W_AS](1 + r)(1 - c_amc - c_g) - ST - MC + M}), rebuilt in one expression rather than throughasset_share_at(), so that a mis-ordered step - a shareholder transfer taken before the charges, say, or a mortality charge measured on the wrong balance - shows up here. The outermax(0, ...)is the zero floorasset_share()applies; the check still validates the ordering in every year the floor is not binding, which is every year of every cell shipped here except the tail of the sustained down scenario.
True when the asset share recursion closes in every projected year.
- check_fb_mvr_exclusive()[source]#
True when no year carries both a final bonus and a market value reduction.
FB > 0requiresS > GBandMVR > 0requiresS < GB, so the two cannot both be positive. An implementation that computed them independently could produce both, and would then pay a final bonus and deduct a reduction on the same exit.
- check_mvr_bound()[source]#
True when the market value reduction stays inside its contractual bound.
It may not exceed the excess of the unit value over the underlying asset value,
max(0, GB - AS). The bound is a conduct rule, not a modelling nicety.
- check_fund_nonneg()[source]#
True when the unit holding and the asset share stay non-negative throughout.
This is the way a with-profits projection goes wrong quietly. A level withdrawal election runs the unit fund down; uncapped, the unit holding turns negative and the guaranteed benefit turns negative with it, and every downstream number - the bonus cost, the mortality charge’s sum at risk, the smoothed payout - stays plausible enough to read past.
wd_pp()caps the withdrawal at the fund andproj_len()stops at exhaustion; this asserts that they worked.
- check_payout_corridor()[source]#
True when the smoothed payout stays inside the 80-120% target corridor.
Deterministic at model-point level; the regulatory test is a portfolio property that a single-policy model cannot express.
- result_cf()[source]#
Result table of cashflows, indexed by policy year t.
pols_ifis the start-of-year count that weights every flow on the row. The asset share is published beside them asasset_sharebecause it is what the payouts are measured against - but it is a state variable, not a cash flow, and it is not part ofnet_cf.