The Projection Space#
The by-policy projection of the ULB_UK_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 anchor cell
>>> Projection.point_id = 2 # the accumulation cell, no withdrawals
t counts policy months, 1-based. The notes index the unit fund UF(t) at the
end of month t with UF(0) = P; the library indexes av_pp() at the
start, so av_pp(t) is the notes’ UF(t-1) — the column their worked-example
table prints first — and the notes’ UF(t) is av_pp_at(t, "AFT_WD"), which equals
av_pp(t + 1).
Input data
Inputs are external files: plain CSVs living in the model folder’s parent
directory, products/unit_linked_bond/, 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
ULB_UK_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 |
surr_table_file |
data.surr_table() |
surr_table.csv |
Naming
Cells names follow lifelib’s savings.CashValue_SE and basiclife.BasicTerm_S
wherever those models have an analogue, and the account-value vocabulary the U.S. models
in this library settled on — av_pp_at(t, timing) for the fund read at a point inside
the month, check_av_roll_fwd for its identity. The technical notes use compact
symbols instead. The mapping is:
Notes symbol |
Cells |
Meaning |
|---|---|---|
t |
(the cells argument) |
Policy month |
y = ceil(t/12) |
policy_year(t) |
Policy year containing month t |
a |
age(t) |
Attained age (ALB) |
(none) |
duration(t) |
Completed policy years, y - 1 |
(none) |
duration_mth(t) |
Months elapsed at end of month t |
P |
premium() |
Single premium |
UF(t-1) |
av_pp(t) |
Unit fund at the start of month t |
UF_g(t), UF’(t), UF(t) |
av_pp_at(t, timing) |
The fund inside month t |
(per segment) |
av_per_segment_pp(t) |
The fund divided by the segments |
g |
fund_return |
Annual gross fund return |
g_m |
fund_return_mth() |
Its monthly equivalent |
G$(t) |
fund_growth_pp(t) |
Gross return credited in month t |
t_pf |
tax_provision_rate() |
Tax provision rate |
TX(t) |
tax_provision_pp(t) |
Tax provision deducted |
c, c_m |
amc_rate(), amc_rate_mth() |
Annual management charge |
AMC$(t) |
amc_pp(t) |
AMC collected in month t |
f, f_m |
further_costs_rate() |
Fund-borne further costs |
FC$(t) |
further_costs_pp(t) |
Further costs borne in month t |
u |
db_uplift() |
Death-benefit uplift, 1.001 |
W(t) |
wd_pp(t) |
Withdrawal cancelled at EOM |
(cap) |
wd_cap_pp(t) |
Rolling 12-month withdrawal cap |
(pattern) |
wd_pattern() |
none / allowance_5pct / custom |
AC(t) |
adviser_charge_pp(t) |
Ongoing adviser charge |
GC(t) |
gmdb_charge_pp(t) |
GMDB rider charge |
G(t) |
gmdb_guarantee_pp(t) |
GMDB guaranteed amount |
gmdb_flag |
gmdb_flag() |
Rider elected |
DS(t) |
death_strain_pp(t) |
Non-unit cost per death |
q_a, q_m(t) |
mort_rate(t), mort_rate_mth(t) |
Mortality rates |
w_base(y) |
surr_rate_base(t) |
Table surrender rate |
M_perf(t) |
perf_factor(t) |
Performance lapse multiplier |
M_allow(y) |
allow_factor(t) |
Allowance-exhaustion step |
w_ann(y,t), w_m(t) |
surr_rate(t), surr_rate_mth(t) |
Surrender rates applied |
l(t-1) |
pols_if(t) |
In force at the start of month t |
l(t) |
pols_if_at(t, timing) |
BEF_DECR / BEF_SURR / AFT_DECR |
(none) |
pols_death(t) |
Deaths in month t |
(none) |
pols_surr(t) |
Surrenders at the end of month t |
(none) |
pols_maturity(t) |
In force when the projection ends |
CumWD(n) |
wd_cum_pp(t) |
Cumulative withdrawals + charges |
CumAllow(n) |
allowance_cum_pp(t) |
Cumulative 5% allowable element |
ExcessGain(n) |
excess_gain_pp(t) |
Excess-event gain, policyholder side |
E(t) |
expenses(t) |
Acquisition + maintenance |
(none) |
inflation_factor(t) |
Expense inflation factor |
(unit cancellations) |
claims(t, kind) |
Gross benefit outgo by kind |
(none) |
unit_releases(t) |
Units cancelled to fund benefits |
(none) |
withdrawals(t) |
Withdrawal outgo |
AMC$ x l |
amc_charges(t) |
AMC margin collected |
FC$ x l |
further_costs(t) |
Further costs, a pass-through |
TX x l |
tax_provisions(t) |
Tax provision, a pass-through |
NUCF(t) |
net_cf(t) |
Non-unit cash flow, income positive |
Four names needed care.
UF is the unit fund, and the library calls a policyholder-owned fund
av_pp_at(). The house name wins, because one concept must not carry two names
across the library — but the docstrings and the result_cf columns keep the unit
vocabulary the product is actually discussed in, and av_pp(t) is documented
everywhere as the notes’ UF(t-1).
G is used twice in the notes: G$(t) is the gross fund return credited in the
month and G(t) the GMDB guaranteed amount. They become fund_growth_pp() and
gmdb_guarantee_pp(); nothing here is called G.
net_cf on this product is the non-unit cash flow, not a gross liability total.
That is not a departure from the library convention but the consequence of the product:
every benefit is funded by cancelling the policyholder’s own units, so a gross
presentation adds the same money to both sides. The gross flows are still published —
claims_death, claims_surrender, withdrawals and unit_releases are
result_cf columns — and check_unit_funding() asserts that they net exactly
against the fund.
pols_maturity is borrowed from the term models and, as in WOL_UK_S, means
something different: a bond has no maturity date, so this is the population still in
force when the projection ends. It pays nothing and exists only so the roll-forward
closes in the last month.
The unit fund recursion, and the order the charges come in
Per policy, within month t:
UF_g(t) = UF(t-1) x (1 + g_m(1 - t_pf)) growth, net of the tax provision
UF'(t) = UF_g(t) x (1 - c_m - f_m) AMC and further costs
UF(t) = UF'(t) - W(t) - AC(t) - GC(t) unit cancellations at end of month
The order matters and is a listed pitfall. The annual management charge accrues
daily through the unit price, so it is levied on the post-growth, pre-cancellation fund.
Charging it on UF(t-1) instead, or after the withdrawal, moves the margin by about
half a month’s growth or withdrawal — small in one month and systematic over decades.
av_pp_at() exposes all three points so the ordering is inspectable rather than
buried in one expression.
What is margin and what is a pass-through
Only two of the four amounts that leave the unit fund are insurer income:
Amount |
Insurer income |
Why |
|---|---|---|
AMC |
yes |
the charge for managing the contract |
GMDB rider charge |
yes |
the price of the guarantee |
Further costs |
no |
fund-borne expenses, paid on to the fund |
Tax provision |
no |
collected in-price, paid on as tax |
Adviser charges |
no |
post-RDR pass-throughs facilitated by cancelling units |
Booking the pass-throughs as margin is the notes’ second-listed pitfall and would
overstate the year-one non-unit result by more than the AMC itself — the tax provision
alone is about 97% of the AMC on the anchor cell, and the further costs another 10%.
further_costs() and tax_provisions() are therefore published as their own
result_cf columns and are not in net_cf(), so the exclusion is visible.
The death strain is the uplift, not the death benefit
The sum assured is u x UF, of which UF is funded by cancelling the policyholder’s
own units. The non-unit cost per death is therefore only
DS(t) = (u - 1) UF(t) + max(0, G(t) - u UF(t)) 1{gmdb}
— a tenth of a percent of the fund on the composite, plus any in-the-money guarantee. The uplift is a parameter and never a literal: 100.1% against 101% is a tenfold difference in death strain, which the notes list as a pitfall and model point 5 exercises.
The 5% allowance is policyholder tax machinery, not a product feature
allowance_cum_pp() and excess_gain_pp() track the cumulative 5% tax-deferred
allowance and the excess-event gain it produces when exceeded. Neither generates an
insurer cash flow. The allowance never caps what can be withdrawn — the product cap
is the rolling 7.5% of wd_cap_pp() — and treating it as a product feature is a
listed pitfall. It is carried because it drives behaviour: the allowance-exhaustion step
in allow_factor() raises surrender from policy year 21, once twenty years of
allowance have been drawn.
The fund runs out, and the projection ends there
A 5% withdrawal against a 5% gross return is not sustainable once the tax provision and
the charges are taken: the fund drifts down and, on the deterministic base run, reaches
zero inside the annuitant’s lifetime. wd_pp() caps the withdrawal at what the fund
can pay, so the fund never goes negative, and proj_len() ends the projection at
fund_exhaust_mth() — a bond with no units has no liability, no margin and nothing
left to project. That is a product fact worth seeing rather than an artefact to hide:
the anchor cell’s fund is exhausted around policy year 30, and every margin the insurer
was counting on stops there.
Behaviour is the whole valuation
Every margin line is proportional to the unit fund and to persistency, and a surrender costs nothing at the point of exit — the surrender value is the bid value of units, cancelled — while truncating the entire future AMC stream. Two dynamic overlays sit on the base table, both [std] and both inert in the deterministic base run:
perf_factor(),min(2, 1 + 2 max(0, g_ref - R_12m)), raising surrender after poor performance. The base run hasR_12m = g_refby construction, so it is 1;return_shockmoves the trailing return without disturbing the fund path, which is what makes the multiplier testable in isolation.allow_factor(), a step to 1.5 from policy year 21, when the tax allowance is spent.
There is no interest-sensitive dynamic lapse and no paid-up state: the product is single premium, so there is no premium obligation to stop.
Cells Descriptions#
- age_at_entry()[source]#
The issue age of the selected model point, age last birthday.
ALB is chosen to index directly into the single-year-of-age table used as the [std] mortality proxy. The anchor documents quote contractual age limits next birthday; the difference is immaterial to a product whose death strain is a tenth of a percent of the fund.
P: the single premium, net of any set-up adviser charge.
Top-ups are excluded from the base projection: a top-up is a new model point with its own premium, allowance clock and segments [std].
- n_segments()[source]#
The number of identical segments the bond is written in; 100 [std].
Per-segment values are bond values divided by this. Modelling at bond level is exact only while all segments stay identical - a segment surrender breaks the symmetry - and the composite keeps bond-level modelling and notes the approximation.
- db_uplift()[source]#
u: the death benefit as a multiple of the bid value of units; 1.001.
A parameter and never a literal: 100.1% against 101% is a tenfold difference in death strain, which is the whole non-unit cost of mortality on this product. Model point 5 is the anchor cell at 101% and exists to make that visible.
- amc_rate()[source]#
c: the annual management charge, 1.00% [std].
A snapshot of a discretionary element: per-fund charge rate cards are not published, and the insurer’s provisions allow the charge to be increased. It is the insurer’s only material income line, so its level sets the whole result.
- further_costs_rate()[source]#
f: the fund-borne further costs, 0.10% [std].
Reduces the unit fund and is not insurer income - it is paid on as a fund expense. Booking it as margin is a listed pitfall.
- tax_provision_rate()[source]#
t_pf: the life-fund tax provision taken in-price, 20% of gross return [std].
Collected through the unit price and paid on as corporation tax, so it is neutral to the insurer’s margin in this model. The real I-E position has timing differences - income as received, realised gains at the next charge date, an annual deemed disposal, settlement on full surrender - and base differences including expense relief and the minimum profits test, all of which create insurer-side tax strain or float that this proxy does not capture.
- wd_pattern()[source]#
none,allowance_5pctorcustom: the regular withdrawal pattern.allowance_5pctis the anchor: 5% of premium a year, taken monthly. That is the pattern every fetched key features document leads with, it sits inside the 7.5% product cap, and adviser charges consume the same tax allowance - so rational take-up gravitates to 5% inclusive of charges.
- wd_rate()[source]#
The annual regular withdrawal as a fraction of the premium.
Zero on
none, 5% onallowance_5pct, and the model point’s own rate oncustom.
- oac_rate()[source]#
The ongoing adviser charge as an annual rate on the unit value; 0 in the base run.
Post-RDR adviser charges are pass-throughs facilitated by cancelling units: they reduce the fund and consume the policyholder’s tax allowance, and they add nothing to the insurer’s non-unit cash flow. Treating them as income is a listed pitfall.
- gmdb_flag()[source]#
Whether the return-of-premium death benefit rider is elected; false in the base.
With it, the death strain stops being negligible and becomes market-contingent, and the rider’s charge scale is unpublished - so the [std] cost-of-insurance form used here is a guess. Enable it only with its own sensitivity set.
- duration_mth(t)[source]#
Months elapsed from issue at the end of month t; equal to t.
tis 1-based, so the identity is trivial - the cells exists so the monthly models in this library share one vocabulary.
- policy_year(t)[source]#
y = ceil(t/12): the policy year containing month t; 1 for t = 1..12.
Also the insurance year the tax allowance is tracked against.
- fund_exhaust_mth()[source]#
The first month at which the unit fund has been drawn to nothing.
horizon_mths() + 1if it never is. A 5% withdrawal against a 5% gross return is not sustainable once the tax provision and the charges are taken, so on the deterministic base run the anchor cell’s fund does run out - around policy year 30 - and every margin the insurer was counting on stops there. Searched rather than solved, so that any withdrawal pattern and charge basis resolves.
- proj_len()[source]#
Projection length in months: the mortality horizon, or the fund exhausting first.
A bond has no maturity date, so the horizon is a limiting age - but a bond with no units has no liability, no margin and nothing left to project, so the projection also ends when the fund does.
- fund_return_mth()[source]#
g_m = (1 + g)^(1/12) - 1: the monthly gross fund return [std].
Deterministic in the base run. The insurer’s margin is proportional to the unit fund, so the liability model inherits full market beta on the margin stream: a 20% market fall cuts the margin base by about a fifth and, through
perf_factor(), raises surrender at the same time.
- mort_rate(t)[source]#
q_a: the annual best-estimate mortality rate at the attained age [std].
The shipped table rate times
mort_be_factor, a crude allowance for population mortality being heavier than insured-lives experience. Nearly irrelevant to this product unless the guaranteed minimum death benefit rider is enabled.
- surr_rate_base(t)[source]#
w_base(y): the table annual full-surrender rate in month t [std].
2 / 3 / 5 / 8 / 10 percent by band. Policy years beyond the table take its last row.
- return_12m(t)[source]#
R_12m: the trailing twelve-month gross fund return driving the lapse multiplier.
fund_return + return_shock[std]. The base run leaves the shock at zero, so the trailing return equals the reference and the multiplier is 1. Keeping the shock separate fromfund_returnis deliberate: it moves the behavioural driver without disturbing the fund path, which is what makes the multiplier testable in isolation. A stochastic or scenario projection would replace this with a path.
- perf_factor(t)[source]#
M_perf(t) = min(2, 1 + 2 max(0, g_ref - R_12m)): the performance lapse multiplier.
Poor recent performance raises surrender [std]; 1 in the deterministic base run.
- allow_factor(t)[source]#
M_allow(y): the allowance-exhaustion step, 1.5 from policy year 21 [std].
After twenty insurance years the cumulative 5% allowance is fully drawn under the anchor withdrawal pattern, and continued withdrawals then generate immediate excess-event gains - which pushes policyholders towards full surrender or advice-driven restructuring.
- surr_rate(t)[source]#
w_ann: the annual full-surrender rate applied in month t.
min(cap, w_base x M_perf x M_allow). A surrender costs the insurer nothing at the point of exit - the surrender value is the bid value of units, cancelled, with no penalty - but truncates the entire future charge stream, which is why persistency rather than mortality dominates this product’s value.
- av_pp(t)[source]#
UF(t-1): the unit fund per policy at the start of policy month t.
The bid value of units, and the column the notes’ worked-example table prints first.
av_init_pp()att = 1, then the end-of-month value of the previous month. Floored at zero:wd_pp()caps the withdrawal at what the fund can pay, so the fund is drawn to nothing rather than through it.
- av_pp_at(t, timing)[source]#
The unit fund per policy at a point inside policy month t.
"BEF_GROWTH"UF(t-1), the start of the month; the same as
av_pp()."AFT_GROWTH"UF_g(t), after the gross return and the tax provision taken in price.
"AFT_CHARGE"UF’(t), after the annual management charge and the further costs. This is the base the AMC is levied on, not
UF(t-1)and not the post-withdrawal fund: the charge accrues daily through the unit price, so it sits on the post-growth, pre-cancellation fund."AFT_WD"UF(t), after the end-of-month unit cancellations - withdrawal, adviser charge and rider charge. Equals
av_pp(t + 1).
All four points are exposed so that the charge ordering is inspectable rather than buried in one expression; charging on the wrong one moves the margin by about half a month’s growth or withdrawal, which is small monthly and systematic over decades.
- av_per_segment_pp(t)[source]#
The unit fund per segment at the start of month t: the bond value over 100.
Reported only. A policyholder’s tax outcome depends on whether whole segments are cashed or a part-surrender is taken across all of them, but the insurer’s cash flow is identical either way - both cancel the same unit value.
- tax_provision_pp(t)[source]#
TX(t) = t_pf x G$(t): the tax provision taken in the unit price.
A pass-through, not insurer income: it reduces the unit fund and is paid on as corporation tax. On the anchor cell it is about 97% of the AMC, so counting it as margin roughly doubles the year-one non-unit result.
- amc_pp(t)[source]#
AMC$(t) = c_m x UF_g(t): the annual management charge collected in month t.
Levied on the post-growth, pre-cancellation fund, because it accrues daily through the unit price. The insurer’s only material income line on this product.
- further_costs_pp(t)[source]#
FC$(t) = f_m x UF_g(t): the fund-borne further costs in month t.
A pass-through, not insurer income.
- wd_cap_pp(t)[source]#
The rolling twelve-month withdrawal cap:
max(7.5% of UF, 7.5% of premium).The product cap, and the only thing that actually limits what can be withdrawn - the 5% tax allowance does not. Measured against the fund after charges and against the premium, whichever is larger, and it includes the ongoing adviser charge.
- wd_pp(t)[source]#
W(t): the regular withdrawal cancelled at the end of month t.
wd_rate x P / 12, then capped twice: by the product’s rolling twelve-month limit - a twelfth of it each month, and net of the ongoing adviser charge, which counts against the same limit - and by what the fund can actually pay after the adviser and rider charges. The second cap is what stops the fund going negative, and it is why the projection can end with the fund at exactly zero rather than through it.
- adviser_charge_pp(t)[source]#
AC(t): the ongoing adviser charge cancelled at the end of month t.
oac_rate/12of the fund after charges. A pass-through: it reduces the unit fund and consumes the policyholder’s tax allowance, and it is not insurer income.
- gmdb_guarantee_pp(t)[source]#
G(t): the guaranteed minimum death benefit, if the rider is elected.
premium - cumulative withdrawals - adviser charges: a return-of-premium guarantee that erodes as the policyholder draws the fund down. Zero without the rider.Measured before the current month’s cancellations, at
wd_cum_pp(t - 1). That is the only reading that resolves: the rider charge is itself a cancellation alongside the withdrawal, so a guarantee net of the same month’s withdrawal would make the charge depend on a withdrawal that depends on the charge. Taking the start-of-month guarantee breaks the loop and matches the contractual sense, in which the amount guaranteed is what is on the record when the month begins.
- gmdb_charge_pp(t)[source]#
GC(t): the rider charge cancelled at the end of month t [std].
q_m x max(0, G(t) - u UF'(t)), a cost-of-insurance form on the in-the-money part of the guarantee. The real charge scale is unpublished, so this is a guess with the right shape and no authority. Insurer income, unlike the other cancellations.
- death_strain_pp(t)[source]#
DS(t): the non-unit cost per death in month t.
(u - 1) UF(t) + max(0, G(t) - u UF(t)) 1{gmdb}. The sum assured isu x UF, of whichUFis funded by cancelling the policyholder’s own units, so the insurer’s cost is only the uplift - a tenth of a percent of the fund on the composite - plus any in-the-money guarantee.
- wd_cum_pp(t)[source]#
CumWD: cumulative withdrawals and adviser charges to the end of month t.
The allowance-relevant total: ongoing and ad hoc adviser charges consume the same 5% tax allowance as withdrawals do.
- allowance_cum_pp(t)[source]#
CumAllow: the cumulative 5% allowable element,
P x min(y, 20) x 5%.Policyholder tax machinery, not a product feature. It never caps what can be withdrawn - the product cap is
wd_cap_pp()- and it generates no insurer cash flow. It is carried because it drives behaviour throughallow_factor().
- excess_gain_pp(t)[source]#
ExcessGain: the excess-event gain at the insurance-year end, if any.
max(0, CumWD - CumAllow), cumulative rather than per-year, so the running total is the gain reported to date. No insurer cash flow: chargeable events are settled between the policyholder and HMRC, with the insurer issuing certificates.
- pols_if(t)[source]#
l(t-1): the number of policies in force at the start of policy month t.
pols_if_init()att = 1, then the notes’ recursionl(t) = l(t-1)(1 - q_m)(1 - w_m), deaths before surrenders [std].
- pols_if_at(t, timing)[source]#
The number of policies in force at a point inside policy month t.
"BEF_DECR"the start of the month, before any decrement;
pols_if()."BEF_SURR"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.
- pols_surr(t)[source]#
Full surrenders at the end of policy month t, from the survivors of mortality.
- pols_maturity(t)[source]#
Policies still in force when the projection ends; zero in every other month.
A bond has no maturity date, so this is either the population left at the limiting age or - on the base run - the population still holding a bond whose fund has just been drawn to nothing. It pays nothing, and it exists so the roll-forward closes.
- claims(t, kind=None)[source]#
Gross benefit outgo in policy month t, by kind; the total when kind is omitted.
"DEATH"u x UF(t)per death - the full sum assured, of which all but the uplift is funded by cancelling the policyholder’s own units - or the guaranteed amount where the rider is elected and in the money,max(u x UF(t), G(t))."SURRENDER"UF(t)per surrender: the bid value of units, with no penalty.
These are gross flows. They are published so the reader can see the whole picture, but they are not in
net_cf(), becauseunit_releases()cancels them against the fund - all except the death strain. See the Space docstring.
- withdrawals(t)[source]#
Withdrawal and adviser-charge outgo in month t, funded by cancelling units.
An owner election rather than a claim, which is why it has its own name and its own
result_cfcolumn.
- unit_releases(t)[source]#
The unit fund cancelled to fund the month’s death and surrender benefits.
UF(t) x (deaths + surrenders). The gross benefit outgo less this is exactly the death strain, which is the only part the insurer funds from its own resources.
- gmdb_charges(t)[source]#
The rider charge collected in month t; insurer income, zero in the base run.
- further_costs(t)[source]#
The fund-borne further costs in month t; a pass-through, not income.
Published as its own column so that its exclusion from
net_cf()is visible rather than merely asserted.
- tax_provisions(t)[source]#
The tax provision taken in the unit price in month t; a pass-through.
About 97% of the annual management charge on the anchor cell, which is the measure of how badly counting it as margin would mislead.
- death_strain(t)[source]#
The non-unit cost of the month’s deaths: the uplift, plus any in-the-money GMDB.
- expenses(t)[source]#
Acquisition and maintenance expense in month t [std].
£300 per policy at issue, then £60 per policy a year inflating at 2.5%. The margin is proportional to the fund while the expense inflates, so a small-fund cell goes margin-negative late in life - which the anchor cell does, as its fund is drawn down.
- net_cf(t)[source]#
NUCF(t): the non-unit cash flow of month t, income positive.
AMC + rider charge - expenses - death strain. Not a gross liability total, and that is a product fact rather than a departure: every benefit is funded by cancelling the policyholder’s own units, so a gross presentation would add the same money to both sides. The tax provision, the further costs and the adviser charges are excluded because they are pass-throughs - the notes’ second-listed pitfall is counting them as margin.The result is a fund-based margin stream: proportional to the unit fund and to persistency, with mortality contributing about a tenth of a percent of the fund times the mortality rate. Behaviour, not mortality, dominates the value.
- check_av_roll_fwd_resid(t)[source]#
The unit fund roll-forward residual in month t; zero everywhere.
UF(t) - [UF(t-1) + growth - tax - AMC - further costs - W - AC - GC], per policy. This is the identity the charge ordering has to satisfy, and it is checked rather than assumed because the ordering is the model’s most consequential convention. Skipped once the fund has been drawn to nothing, where the withdrawal cap breaks the equality by design.
- check_av_roll_fwd()[source]#
True when the unit fund roll-forward closes in every projected month.
The library-wide form of a roll-forward check: no argument, one bool over all t, so one test can call the same check across every account-value model in the library.
- check_pols_roll_fwd_resid(t)[source]#
The in-force roll-forward residual in month t; zero everywhere.
pols_if(t) - pols_if(t+1) - deaths - surrenders - the final-month residual.
- check_unit_funding_resid(t)[source]#
How much of the month’s benefit outgo the unit fund does not fund.
claims(t) - unit_releases(t) - death_strain(t), which is zero: the death benefit is the unit fund plus the uplift, the surrender benefit is the unit fund exactly, and the uplift is precisely the death strain. A model that funded benefits twice - once from the fund and once from the insurer - would show it here.
- check_unit_funding()[source]#
True when every benefit is funded by unit cancellation plus the death strain.
- result_cf()[source]#
Result table of cashflows, indexed by policy month t.
av_ppis the unit fund at the start of the month, andpols_ifthe in-force count that weights every flow on the row.net_cfis the non-unit stream - what accrues to the insurer - whileclaims_death,claims_surrender,withdrawalsandunit_releasesshow the gross picture they net against.further_costsandtax_provisionsare published precisely because they are not innet_cf: they leave the unit fund and are paid on.