The Projection Space#

The by-contract projection of FIA_US_S.

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, by month
>>> Projection[1].result_cf_annual()   # the same, summed into contract years
>>> Projection.point_id = 2            # or switch the default

Input data

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

The readers and the filename References live on the sibling Data Space, reached here through the data Reference, so each file is read once per model rather than once per model point:

Reference (on Data)

Cells

File

model_point_file

data.model_point_table()

model_point_table.csv

mort_table_file

data.mort_table()

mort_table.csv

surr_charge_file

data.surr_charge_table()

surr_charge_table.csv

rollup_file

data.rollup_table()

rollup_table.csv

payout_rate_file

data.payout_rate_table()

payout_rate_table.csv

rate_scenario_file

data.rate_scenario()

rate_scenario.csv

withdrawal_file

data.withdrawal_table()

withdrawal_table.csv

Projection basis

t is the month index and it is 0-based: t = 0 is the first month of a model point projected from issue, duration(t) = t // 12 is the completed contract years at the start of month t, and the contractual label is policy_year(t) = duration(t) + 1. The frame is t = entry_mth() proj_len() - 1, and proj_len() is 12 * policy_term(). The grid matches MYGA_US_S, Term_US_S and the rest of the library, and it is what the product assignment table records for this product.

The monthly grid does not make the product monthly. The technical notes make the anniversary the single event date — “All transactions occur at the anniversary and are processed as the last events of the contract year ending there” [std] — and that is still true here: every contractual event is an annual event landing in the anniversary month, is_anniv(t), the last month of each contract year. What the finer grid buys is everything that is not a contractual event: mortality and surrender now fall in the month they happen rather than all at the anniversary, the Model #805 floor and the fixed account accrue month by month so a mid-year death or surrender is valued on the balance it actually has, and maintenance expense accrues where it is incurred. Because the monthly decrement rates compound to the annual ones and every contractual step still happens once a year, every state value at an anniversary is identical to the annual grid’s — account value, benefit base, rollup base, lifetime withdrawal, Model #805 floor, phase and pols_if at t = 12k. What changes is the timing and the weighting of the cash, which is the point.

Three vocabulary cells carry the split, and the whole model is written in terms of them: duration() for anything keyed by contract year, is_anniv() for anything that happens once a year, and year_open_mth() for the month whose closing value opens the contract year — because on a monthly grid the previous month is no longer the previous year, and reaching for t - 1 where a year is meant is the mistake this conversion exists to prevent.

age(t) = age_at_entry() + duration(t) is the attained age opening the contract year of month t, so mortality over that year reads age(t) itself — the library convention of Term_US_S and MYGA_US_S. Age changes on the anniversary, not on the birthday and not monthly. The transactions at the closing anniversary are one year older, and that age is exercise_age(), covered_age(t) + 1: it is what reads the lifetime-withdrawal percentage table and what clears the contractual minimum exercise age.

There is no issue-instant row. The single premium, the premium bonus and the acquisition expense are beginning-of-month flows of month 0 on a new-issue model point, whose first projected month is t = 0. A model point may instead enter in force after entry_year() completed contract years, on balances stated in model_point_table.csv — which is what the worked example does, its anniversary-7 balances being explicitly “illustrative … broadly consistent with a seven-year deferral” [std] rather than derived. entry_year() is an elapsed count of whole years, so entry_mth() = 12 * entry_year() is the index of the model point’s first projected month and the frame always opens on an anniversary. Every recursion opens there on the *_init cells, read through the opening timing (av_pp_at(t, "BEF_INV"), benefit_base_pp_at(t, "BEF_ROLLUP"), phase_open(), rider_in_force_open()), and result_cf() is indexed from entry_mth().

The anniversary’s eight processing steps are the notes’ own, all eight still annual and all eight landing in the anniversary month, and each intermediate value is reachable through a timing argument rather than being buried inside one formula:

  1. index credit and fixed interest — index_credit_pp(), fixed_interest_ann_pp()

  2. rider charge on the opening benefit base — rider_charge_pp()

  3. benefit base: rollup, stack, step-up — rollup_pp(), stack_pp()

  4. lifetime and excess withdrawal — lw_pp_at(), wd_pp()

  5. charges on the excess and the proportional reduction — wd_reduction_rate()

  6. guaranteed minimum value roll — mgsv_pp()

  7. phase transition including the depletion test — phase()

  8. decrements — pols_if_at() at "AFT_DECR"

Steps 1–3 are skipped in DEPLETED, steps 1–7 in TERMINATED. Step 8 is the one that is not annual: it runs every month, on mort_rate_mth() and lapse_rate_mth(). Steps 1 and 6 keep a monthly component — the fixed account accrues at (1 + i_F)^(1/12) - 1 and the Model #805 floor at (1 + i_nf)^(1/12) — while their annual figures, fixed_interest_ann_pp() and the anniversary deduction, stay whole.

Between anniversaries the contract is quiet: no withdrawal, no rider charge, no index credit, no rollup and no step-up. The indexed account is therefore flat between anniversaries, because the annual point-to-point segment locks only at maturity and “the credit … cannot be lost to later declines” [S1] is a statement about a credit that has not yet been earned. That is the sourced rule “withdrawals are not credited with index interest in the year they are taken” [S1] carried to mid-year deaths and surrenders [std]; crediting a part year would be the daily interim value variant the notes exclude.

Naming

Cells names follow MYGA_US_S — the deferred annuity chassis this product sits on — and through it lifelib’s basiclife.BasicTerm_S and savings.CashValue_SE: pols_* for policy counts, av_* for account values, plural nouns for cash flows, *_rate for rates, *_pp for per-contract amounts, *_at(t, timing) for a quantity read at a point inside the anniversary. The technical notes use compact actuarial symbols instead. The mapping is:

Notes symbol

Cells

Meaning

t

(the projection index)

Month index, 0-based

(elapsed years)

duration(t)

Completed contract years at month t

(elapsed months)

duration_mth(t)

Completed contract months; = t

(the anniversary month)

is_anniv(t)

Last month of a contract year

(that month’s index)

anniv_mth(t)

Anniversary closing month t’s year

(the year’s opening month)

year_open_mth(t)

Anniversary that opened it

(contract year)

policy_year(t)

Contract year containing t; duration + 1

x

age_at_entry

Issue age (ANB)

x + duration(t)

age(t)

Attained age opening month t’s contract year

(younger covered life)

covered_age(t)

Younger covered age opening that year

x + duration(t) + 1

exercise_age(t)

Covered age at the closing anniversary

(none)

policy_term

Contract years from issue to maturity_age

(none)

proj_len

Number of projected months; 12 x policy_term

(in-force cell entry)

entry_year

Completed years at entry

(none)

entry_mth

First projected month; 12 x entry_year

av_initial

av_pp_init

AV at entry, else P x (1 + b)

bb_initial

benefit_base_pp_init

BB at entry, else P

mgv_initial

mgsv_pp_init

MGV at entry, else 0.875 x P

(RB at entry)

rollup_base_pp_init

RB at entry, else P

(LW at entry)

lw_pp_init

LW at entry, 0 before exercise

(pi at entry)

payout_rate_init

Payout percentage locked at entry

(phase at entry)

phase_init

Phase at entry

P

premium_pp

Single premium

b

bonus_rate

Premium bonus rate (7% [S5])

b/(1+b)

bonus_factor

Clawback factor – not b [S10]

v(t)

vest_rate(t)

Vested bonus percentage [S5]

alloc_indexed/alloc_fixed

alloc_indexed / alloc_fixed

Account allocation at issue

I(y)

index_level(y)

Index level at anniversary number y

R(t)

index_return(t)

I(y+1)/I(y) - 1 over month t’s year

c

cap_rate

Declared annual cap (5.25% [S2])

c_min

cap_rate_min

Guaranteed minimum cap (0.25% [S4])

c(t)

cap_rate_in_force()

Cap actually applied

f

floor_rate

Index credit floor (0%)

p, s, d

par_rate, spread_rate, trigger_rate

Participation rate [R1]; spread and trigger, levels [std]

cr(t)

credit_rate(t)

Credit rate for month t’s contract year

(cr on a return)

credit_rate_on(r, method)

The crediting engine itself

IC(t)

index_credit_pp(t)

Index credit, in the anniversary month

FI(t) monthly

fixed_interest_pp(t)

Fixed account interest for month t

FI(t) annual

fixed_interest_ann_pp(t)

The contract year’s, for the stack

i_F, i_F,min

fixed_rate, fixed_rate_min

Declared / guaranteed fixed rate

kappa

av_int_factor

Share of IC reaching the AV [S3]

(IC x kappa + FI)

inv_income_pp(t)

Interest credited to the AV

A(t)

av_indexed_pp(t)

Indexed account balance

F(t)

av_fixed_pp(t)

Fixed account balance

AV(t)

av_pp(t)

Account value closing month t

AV(0), AV(1), AV(2), AV(t)

av_pp_at(t, timing)

BEF_INV / BEF_FEE / BEF_WD / EOY in order; BEF_PREM opens the month

AV(0) of the contract year

av_pp_year_open(t)

Balance opening month t’s contract year

F(0), A(0) of that year

av_fixed_pp_year_open(t), av_indexed_pp_year_open(t)

The fixed and indexed openings: the credit base and the fixed-interest base

l(t) x AV(t)

av_at(t, timing)

In-force weighted account value

(shortfall at exhaustion)

av_depletion_pp(t)

Withdrawal the AV could not fund

phi

rider_charge_rate

Rider charge rate (0.95% [S9])

Phi(t)

rider_charge_pp(t)

Rider charge amount

BB(t)

benefit_base_pp(t)

GLWB benefit base, closing

BB(3), BB(4)

benefit_base_pp_at(t, timing)

BEF_ROLLUP/BEF_STEP_UP/BEF_WD/EOY

RB(t)

rollup_base_pp(t)

Rollup base

g(t)

rollup_rate(t)

Guaranteed simple rollup rate [S2]

rollup(t)

rollup_pp(t)

Rollup dollar increment

m

stack_factor

Stacking factor (1.50 [S8][S9])

stack(t)

stack_pp(t)

Stacking credit

T_g

in_growth_period(t)

Benefit base still growing

(the step-up)

step_up_applies(t)

Whether the ratchet is tested

LW(t)

lw_pp(t)

Lifetime withdrawal amount, closing

LW before/after step 5

lw_pp_at(t, timing)

BEF_WD / EOY

pi(a, basis)

payout_rate(a, basis)

Payout percentage by age band [S3]

(pi locked at exercise)

payout_rate_locked(t)

The percentage actually in force

h(a)

activation_rate(a)

Activation incidence, reported only

G(t)

wd_pp(t)

Gross withdrawal requested

min(G, LW)

wd_guar_pp(t)

Guaranteed portion

E(t)

wd_excess_pp(t)

Excess above the guaranteed amount

(unpayable withdrawal)

wd_unfunded_pp(t)

Requested but neither funded nor guaranteed, on TERMINATED

(guaranteed portion paid)

wd_guar_paid_pp(t)

min(G, LW) less its share of it

(excess portion paid)

wd_excess_paid_pp(t)

E(t) less its share of it

FW(t)

free_wd_allow(t)

Free withdrawal amount

0.10 x AV(0)(t)

free_wd_base(t)

Base of the free amount

(FW consumed)

wd_free_pp(t)

Free-allowance portion of the withdrawal

(FW remaining)

free_wd_remain(t)

Free amount left for the excess

X(t) on a withdrawal

wd_charge_base_pp(t)

Amount exposed to charge and MVA

X(t) on a surrender

surr_charge_base_pp(t)

Same, at a full surrender

sc(t)

surr_charge_rate(t)

Surrender charge percentage [S5]

SC(t) on a withdrawal

wd_charge_pp(t)

Surrender charge on the excess

SC(t) on a surrender

surr_charge_pp(t)

Surrender charge on a surrender

CB(t) on a withdrawal

wd_clawback_pp(t)

Non-vested bonus recovery

CB(t) on a surrender

surr_clawback_pp(t)

Same, at a full surrender

(the clawback formula)

bonus_clawback_on(…)

(1-A) x [B/(1+B)] x C [S10]

MVA(t) on a withdrawal

wd_mva_pp(t)

Collared MVA on the excess

MVA(t) on a surrender

mva_pp(t)

Collared MVA on a surrender

(the MVA collar)

mva_pp_on(t, base, gross)

The collared MVA on any base

(the MVA rate)

mva_rate(t)

[(1+i0)/(1+it)]^(n/12) - 1 [S10]

i0

mva_ref_yield_at_issue

Reference index at issue

i_y

mva_ref_yield(y)

Reference index at anniversary number y

n/12

mva_term(t)

Years left in the MVA period

(MVA applies at all)

mva_in_force(t)

Inside the MVA period

rho(t)

wd_reduction_rate(t)

Proportional reduction factor

(rho’s formula)

wd_reduction_rate_on(…)

The [S9] worked construction

Wcum(t)

wd_cum_pp(t)

Cumulative gross withdrawals

G - SC - CB + MVA

wd_payment_pp(t)

Cash paid on the withdrawal

(pre-floor surrender value)

surr_value_pp(t)

AV - SC - CB + MVA

CSV(t)

surr_benefit_pp(t)

Surrender benefit paid

MGV(t)

mgsv_pp(t)

Model #805 guaranteed minimum

i_nf

mgsv_rate

Nonforfeiture accumulation rate

(the $50 charge)

mgsv_charge_pp(t)

Annual contract charge, 0 [std]

(statutory i_nf)

mgsv_rate_statutory(…)

Model #805 4B/4C indexed rate

phase(t)

phase(t)

Closing ACCUM/INCOME/DEPLETED/TERMINATED

(phase during the year)

phase_open(t)

Phase steps 1-7 are processed under

(first exercise)

is_exercise(t)

The first lifetime withdrawal

rider_in_force(t)

rider_in_force(t)

Rider still alive closing month t [S9]

rider_in_force(k-1)

rider_in_force_open(t)

Rider alive opening the contract year

depletion_cause(t)

depletion_cause(t)

Excess / charge / negative MVA flag

q(t)

mort_rate(t)

Annual mortality of month t’s contract year

1 - (1-q)^(1/12)

mort_rate_mth(t)

The monthly rate actually applied

(A/E deviation)

mort_ae_factor

Mortality A/E factor, 100% [std]

w(t)

lapse_rate(t)

Annual surrender rate of that year

1 - (1-w)^(1/12)

lapse_rate_mth(t)

The monthly rate actually applied

w_base(t)

lapse_rate_base(t)

Base surrender table

w_shock

shock_lapse_rate(t)

33% / 10% / 5% by rider state [R8]

M_money(t)

lapse_moneyness_factor(t)

Moneyness suppression [std]

l(k) at t = 12k

pols_if(t)

In-force opening month t

(closing the month)

pols_if_at(t, “AFT_DECR”)

In-force at the end of month t

l(0)

pols_if_init

In-force at entry

(the four readings)

pols_if_at(t, timing)

BEF_DECR/BEF_MORT/BEF_LAPSE/AFT_DECR

l q_m

pols_death(t)

Deaths in the month

l (1-q_m) w_m

pols_lapse(t)

Full surrenders in the month

(none)

pols_maturity(t)

Survivors at the horizon

P at t = 0

premiums(t)

Premium income

min(G, LW) x l(t)

wd_guar(t)

Guaranteed withdrawal outgo, paid

(E - SC - CB + MVA) l(t)

wd_excess(t)

Excess withdrawal outgo, paid

LW while DEPLETED

income_payments(t)

Post-depletion income outgo

(all three together)

withdrawals(t)

Total withdrawal outgo

(ledger benefit lines)

claims(t, kind) claim_pp(t, kind) claims_from_av(t, kind) claims_over_av(t, kind)

Benefit outgo by kind Benefit per contract by kind Account value released by a claim Benefit paid above the account value

(no separate commission)

commissions(t)

Acquisition commission, 0 [std]

0.06 P; (80/12) x 1.025^(t/12)

expenses(t)

Acquisition and monthly maintenance

premium tax

premium_taxes(t)

Premium tax, 0% [std]

NetCF(t)

net_cf(t)

Net cash flow for the month

(the annual summary)

result_cf_annual()

result_cf() summed into contract years

Six names needed care.

pols_if(t) is the notes’ l(k) at the twelfth months. Across this library pols_if(t) is the number in force at the start of t and is the weight carried by that same row’s cash flows — Term_US_S has pols_if(0) == pols_if_init() and savings.CashValue_SE has pols_if(t) equal to pols_if_at(t, "BEF_MAT"). The notes define l(k) as the in-force probability at the end of contract year k, which is anniversary k, which is where contract year k + 1 opens: on the monthly index that is month 12k, so pols_if(12k) = l(k) with pols_if(entry_mth()) = l(0) = 1 for a new issue. Because the monthly rates compound to the annual ones this equality is exact, not approximate.

The one place the finer grid changes an answer rather than its resolution is the weight on the anniversary’s own cash. A withdrawal, a rider charge or a surrender charge falls in month 12k + 11 and is weighted by pols_if(12k + 11) — the contracts that actually reach the anniversary — where the annual grid weighted it by l(k), the contracts that entered the year. The second is an approximation the annual grid could not avoid: it pays a full year’s guaranteed withdrawal to a contract that died in month three. The reconciliation the convention buys is unchanged, and now holds row by row on the finer grid: the pols_if column of result_cf() is the divisor of the cash flows on its own row — result_cf()["wd_guar"][t] / wd_guar_paid_pp(t) is pols_if(t).

MGV is MGSV. The Model #805 floor is called MGV in these notes, after [S10], and MGSV in products/fixed_deferred_annuity/, after its own specimen. Both source files say in terms that this is one quantity under two labels and must not be modeled as two. The chassis name mgsv_pp() is used here, so the two annuity models share it. The recursion, however, is not the chassis’s: this product accretes and then deducts, MGV(t) = max(0, MGV(t-1) x (1 + i_nf) - G(t)), while the chassis deducts and then accretes, MGSV(t) = [MGSV(t-1) - d(t) - c(t)] x g. The worked example pins the FIA ordering — 93,811.84 x 1.01 - 10,144.16 = 84,605.80 — and the chassis notes explicitly warn against carrying their recursions across unexamined. Same name, same concept, different arithmetic, on purpose.

E(t) is not the chassis’s E(t). On the chassis E(t) is the amount exposed to the surrender charge and the MVA, and is named wd_excess_pp / surr_excess_pp. Here E(t) is the notes’ excess withdrawal — the part of a gross withdrawal above the guaranteed lifetime amount, the quantity that permanently reduces the guarantee and, at exhaustion, destroys it. That is the product’s headline concept and it keeps the name wd_excess_pp(). The chargeable amount, the notes’ X(t), becomes wd_charge_base_pp() on the withdrawal path and surr_charge_base_pp() on the surrender path. Reading a chassis formula across without renaming would silently charge a surrender charge on the wrong base.

age(t) opens the contract year; exercise_age(t) closes it. age(t) = age_at_entry() + duration(t) is the attained age at the anniversary that opens month t’s contract year, exactly as in Term_US_S and MYGA_US_S, and mort_rate() reads it directly — the annual rate it returns is then converted for the month by mort_rate_mth(). Every transaction, however, falls at the closing anniversary, one year older, and the payout percentage depends on that age: the anchor cell’s first lifetime withdrawal, in contract year 8 from an issue age of 62, is taken at attained age 70 and reads pi(70, single) = 5.20%. exercise_age() carries that age — covered_age(t) + 1 — so the + 1 is named once rather than spelled at each use.

M_shock is absorbed into lapse_rate_base. The notes write w(t) = min(0.35, w_base(t) x M_shock(t) x M_money(t)) but state the shock as three absolute rates (33% / 10% / 5%) rather than as multipliers on the 6% ultimate. Carrying M_shock as a separate factor would require inventing a denominator, so lapse_rate_base() returns shock_lapse_rate() in the shock year and lapse_rate() multiplies only by lapse_moneyness_factor(). The name shock_lapse_rate follows Term_US_S — but the timing does not, and the contrast is worth stating. There the notes put the shock “in full at the end of the final level-period month”, a contractual date, so Term_US_S drops the whole shock in that one month. Here the notes state it as the surrender rate of contract year 11, a year that carries no surrender charge in any of its months, so there is no date inside the year for the decision to cluster on and lapse_rate_mth() spreads it like any other annual rate. The one surrender that is not spread is the deemed full surrender at termination, which is a contractual event and lands whole on the anniversary.

d and c collide across the library. d is the performance-trigger rate in these notes, deaths in Term_US_S and the floor’s withdrawal deduction on the chassis; c is the declared cap here and the floor’s contract charge on the chassis. The names trigger_rate, pols_death(), mgsv_charge_pp() and cap_rate() keep the four apart.

One name is reused deliberately and reads across cleanly. credit_rate() is the chassis’s declared effective annual rate i_cr(t) and here the index credit rate cr(t) = max(f, min(c, R(t))). Different formulas, but the same concept — the rate at which interest is credited for the contract year — so the name is kept rather than split.

Timing arguments

Account value, following CashValue_SE’s av_pp_at and named for the processing step each precedes:

"BEF_PREM"

the balance carried into month t before the single premium: zero at t = 0 on a new-issue model point, and the same as "BEF_INV" everywhere else. It exists so that the account-value roll-forward closes on the first month too, the premium being a beginning-of-month flow rather than a row of its own.

"BEF_INV"

the balance opening month t after the premium: av_pp_init() at the first projected month and AV(t-1) after it. In an anniversary month this is the notes’ AV(0) only when the fixed allocation is zero; in general the notes’ AV(0) of the contract year is av_pp_year_open(), eleven months earlier.

"BEF_FEE"

AV(1), after the month’s interest — the fixed accrual every month, plus the index credit in an anniversary month — and before the rider charge.

"BEF_WD"

AV(2), after the rider charge, before the withdrawal. Not floored at zero — the 0% floor is on the index credit, not on the account value [S7].

"EOY"

AV(t), after the withdrawal, floored at zero. Equal to av_pp().

Away from an anniversary the last three coincide, at the opening balance plus the month’s fixed accrual, because no contractual transaction falls there.

Benefit base, named for the same steps: "BEF_ROLLUP" is the base opening the contract year, "BEF_STEP_UP" is BB(3) after rollup and stack, "BEF_WD" is BB(4) after the annual step-up, and "EOY" is BB(t) after the proportional reduction. All four steps are annual, so away from an anniversary every timing returns the opening base. Lifetime withdrawal: "BEF_WD" is the amount payable at the contract year’s anniversary, "EOY" the closing value after the same reduction; away from an anniversary both carry the amount set at the last one, which is the income in force.

Policy counts, following the chassis: "BEF_DECR" is the count entering the month and therefore equals pols_if() itself, "BEF_MORT" is the same (this product has no annuitization decrement), "BEF_LAPSE" is after that month’s deaths, and "AFT_DECR" is the closing count, which is also pols_if(t + 1). Both decrements are the monthly rates.

Benefit kind arguments are "DEATH", "LAPSE" and "MATURITY". Any other value of any of these raises ValueError.

The three phases, and why the cause of depletion matters

ACCUM is deferral. INCOME starts at the first lifetime withdrawal and lasts while an account value remains. DEPLETED is what the product is sold for: the account value is gone, the insurer pays LW from its own funds for the rest of the covered life, no rider charge is deducted, no index credit is computed, the surrender value and the death benefit are zero, and lapse is impossible, so lapse_rate() returns zero and l(t+1) = l(t)(1 - q_m(t)). TERMINATED is the same exhaustion with the guarantee destroyed — reached when the account value is driven to zero by an excess withdrawal, a surrender charge or a negative MVA rather than by guaranteed withdrawals and rider charges [S1][S5][S9]. depletion_cause() carries the attribution and is evaluated before the depletion test, exactly as the notes require. A model testing only AV <= 0 would either give the guarantee away after an excess withdrawal or destroy it after a legitimate one. When the contract terminates, the survivors leave as a deemed full surrender — lapse_rate() returns 1.0 for that contract year, and lapse_rate_mth() puts the whole of it in the anniversary month rather than spreading it, because the contract “as well as the rider will be considered Surrendered” [S5] on that date — so the in-force roll-forward still closes.

The transition itself is annual and is tested only at the anniversary, which costs nothing: the account value can reach zero only where a withdrawal or a charge is taken, and both fall there. Between anniversaries phase() simply carries phase_open().

The asymmetry reaches the cash, not only the phase label. On the exhaustion anniversary itself the withdrawal requested exceeds what the account value can meet. In DEPLETED the insurer funds the whole shortfall, because that is the guarantee. On the TERMINATED branch it funds none of it: the balance is gone and the rider that would have covered the rest is destroyed by the very withdrawal being paid, and [S5] treats the contract as surrendered at that point. wd_unfunded_pp() is that unpayable part, and wd_payment_pp(), wd_guar() and wd_excess() are all net of it. The cap is on the payment only — wd_pp() still carries the amount requested, so the excess still sets depletion_cause(), still drives rho to 1 and still takes the benefit base to zero.

pols_maturity and the projection horizon

The technical notes state no projection horizon; the DEPLETED liability is a life annuity, so the model runs through the final month of the contract year entered at attained age maturity_age = 120 [std], the terminal age of the shipped mortality table — proj_len() = 12 * policy_term() months. The survivors of the last month leave through pols_maturity(), zero at every other t, so that

pols_if(t) - pols_if(t+1) = pols_death(t) + pols_lapse(t) + pols_maturity(t)

holds for every t, including the last, t = proj_len() - 1, where pols_if(proj_len()) is zero. Under the shipped table q reaches 1.000000 at age 120, hence a monthly rate of 1.0 in the first month of that contract year, so the projection closes itself and the term is numerically zero; it is kept because a substituted table with no terminal age would make it bite, and because without it the last month would appear to lose lives with no cause. The name follows BasicTerm_S.pols_maturity and the construction follows Term_US_S.

Cells Descriptions#

model_point()[source]#

The selected model point as a Series.

policy_id()[source]#

The contract identifier of the selected model point.

age_at_entry()[source]#

x: the issue age (ANB) of the selected model point.

sex()[source]#

The sex of the selected model point, M or F.

tax_status()[source]#

Tax status of the contract, NQ or Q.

Reported only. The base run is non-qualified [std]; no RMD module is implemented, though the notes make the RMD age a behavioural input through activation_rate() and the rmd_age Reference.

premium_pp()[source]#

P: the single premium per contract.

pols_if_init()[source]#

l(0): in-force probability at entry, 1 for a single-contract model point.

entry_year()[source]#

The completed contract years behind the model point when it enters the projection.

An elapsed count of whole years, which entry_mth() turns into the index of the model point’s first projected month, 12 * entry_year(). 0 for a new issue, whose month 0 carries the premium, the acquisition expense and the notes’ Initialisation block as its opening state and beginning-of-month flows. A positive value enters the contract in force on balances stated in the model point table, which is what the worked example does: its anniversary-7 balances are described as illustrative and broadly consistent with a seven-year deferral [std], not derived from one, so deriving them would mean retuning assumptions to force a match. Every recursion opens here.

entry_mth()[source]#

The model point’s first projected month, 12 * entry_year().

The frame is t = entry_mth() proj_len() - 1. 0 for a new issue, whose month 0 carries the premium, the acquisition expense and the notes’ Initialisation block as its opening state and beginning-of-month flows. Elapsed time is stated in completed contract years, so the frame always opens on an anniversary and every recursion opens here.

bonus_rate()[source]#

b: the premium bonus rate, 7% of the single premium [S5].

alloc_indexed()[source]#

The share of the account value allocated to the indexed account; 100% [std].

alloc_fixed()[source]#

The share allocated to the fixed account, 1 - alloc_indexed().

glwb_elected()[source]#

Whether the GLWB rider is elected at issue [S5][S9][S11].

glwb_basis()[source]#

single or joint.

The joint column of the payout table is the single column less 0.50%, read on the younger covered person [S1][S3]. Joint-life survivorship is not implemented: the notes specify the joint payout percentage but no second-life mortality basis.

joint_age()[source]#

The issue age (ANB) of the joint covered person; the annuitant’s own if blank.

income_start_age()[source]#

The attained age at which the base run takes its first lifetime withdrawal.

The deterministic run activates here rather than on the h(a) incidence table, which the notes say clusters at the RMD age but which cannot be applied to a single deterministic cell; see activation_rate().

utilization_intensity()[source]#

The fraction of LW actually withdrawn once income starts.

1.00 in the base run [std]; the majority of users withdraw 95%-105% of the maximum [R1]. The 1.05 case is an excess withdrawal and routes through the proportional reduction of step 5, which is why efficiency and excess-withdrawal assumptions cannot be set independently.

credit_method()[source]#

The crediting method: cap, par, par_cap, spread or trigger.

All five are implemented in credit_rate_on(). The notes’ sixth variant, monthly sum with a monthly cap, is not implemented: it needs a monthly grid, which the notes exclude, and its floor convention is itself flagged as ambiguous [S4][R1].

cap_rate()[source]#

c: the declared annual cap, 5.25% [S2].

A non-guaranteed element captured as of 07/01/2022, revisable under ASOP No. 2 [R6][REG-R26]. Holding it constant for forty years is a strong implicit assumption; re-declaration against the option budget is not implemented because the notes give the target but no option-pricing function.

stack_factor()[source]#

m: the stacking factor on realised dollar credits, 150% [S8][S9].

Zero gives the pure-rollup design (a); with rollup_rate zero it gives the pure stacking design (b), where one carrier’s 250% election credits 250% to the benefit base [S3][S4].

av_int_factor()[source]#

kappa: the share of the index credit that reaches the account value [S3][S4].

1.00 on the blended baseline. 0.50 is the 250% election documented at one carrier, which credits 250% of index interest to the benefit base but only 50% to the account value, deliberately starving the account value. The stacking factor m applies to the gross index credit, not to kappa x IC.

rollup_id()[source]#

The guaranteed rollup schedule, a key into rollup_table.csv.

mva_ref_yield_at_issue()[source]#

i0: the MVA reference index level locked at issue, 3.00% [std].

scenario_id()[source]#

The index and reference-yield scenario, a key into rate_scenario.csv.

wd_schedule_id()[source]#

The ad hoc withdrawal programme, a key into withdrawal_table.csv.

av_pp_init()[source]#

AV at entry: the stated in-force balance, else P x (1 + b) [S5].

The bonus is credited to the account value at issue and earns index credits from day one [S5]; it does not enter the benefit base [S9] or the nonforfeiture base [S10].

av_fixed_pp_init()[source]#

F at entry: the fixed account share of av_pp_init(), allocated as premium.

benefit_base_pp_init()[source]#

BB at entry: the stated in-force base, else P – the bonus stays out [S9].

rollup_base_pp_init()[source]#

RB at entry: the stated in-force rollup base, else P [S2][S9].

mgsv_pp_init()[source]#

MGV at entry: the stated in-force floor, else 87.5% of premium [S10][R2].

87.5% of the single premium excluding the bonus [S10]; the worked example’s 93,811.84 = 87,500 x 1.01^7 is that value rolled forward seven years.

lw_pp_init()[source]#

LW at entry: zero before exercise, else the stated in-force income amount.

payout_rate_init()[source]#

The payout percentage already locked at entry; zero before exercise.

phase_init()[source]#

The phase at entry: ACCUM unless the model point enters in force in income.

policy_term()[source]#

Contract term in years [std]: through the year entered at maturity_age.

maturity_age = 120 is the terminal age of the mortality basis, where the annual rate is 1.000000, so the projection closes itself rather than being truncated: the last contract year is the one entered at that age, hence the + 1. The notes state no horizon, and the DEPLETED liability is a life annuity, so stopping earlier would silently drop the tail the product is sold for.

proj_len()[source]#

The number of contract months projected, 12 * policy_term().

The exclusive end of the frame counted from t = 0: result_cf() runs t = entry_mth() proj_len() - 1, so its last row is the final month of the contract year entered at attained age maturity_age.

policy_year(t)[source]#

The contract year month t falls in: duration(t) + 1, the 1-based label.

Derived from t, never indexed by. It is the key that reads the surrender charge, the vesting vector and the rollup schedule, all of which the sources state by contract year, and it is the year whose annual decrement rates mort_rate() and lapse_rate() return.

duration_mth(t)[source]#

Completed contract months at the start of month t: t itself.

The cells exists so the monthly vocabulary reads the same here as in Term_US_S and savings.CashValue_SE; an in-force model point opens the frame at entry_mth() rather than re-basing t.

duration(t)[source]#

Completed contract years at the start of month t, duration_mth(t) // 12.

Every contractual schedule in this product is annual, so this is what the attained age, the surrender charge, the vesting vector, the rollup schedule and the scenario tables are read at.

is_anniv(t)[source]#

True in the last month of a contract year, where every annual event lands.

duration_mth(t) % 12 == 11. All eight of the notes’ processing steps are contractually annual — the point-to-point index credit [S2][S4][S10], the rider charge [S9], the benefit base rollup, stack and step-up [S9], the lifetime withdrawal, the charges and the phase transition — so all eight happen here and nowhere else. What the finer grid carries between anniversaries is the fixed-account accrual, the Model #805 roll, the decrements and the maintenance expense.

anniv_mth(t)[source]#

The month that closes the contract year of month t: 12 * duration(t) + 11.

Equal to t itself when is_anniv() is true. It is where the annual quantities of that contract year are evaluated — which is what makes lapse_rate() a property of the year rather than of the month.

year_open_mth(t)[source]#

The month whose closing value opens the contract year of month t.

12 * duration(t) - 1, the anniversary closing the previous contract year. It falls below entry_mth() in the first projected contract year, where the notes’ Initialisation values stand in — which is the test every annual state cells makes.

age(t)[source]#

x + duration(t): the attained age (ANB) opening the contract year of month t.

The library convention of Term_US_S and MYGA_US_S, so mort_rate() reads this age directly for the contract year of mortality it covers. Age changes on the anniversary, not on the birthday and not monthly. The transactions of that contract year fall at its closing anniversary and are one year older: that age is exercise_age(), which is what reads the lifetime withdrawal percentage table.

covered_age(t)[source]#

The attained age of the covered life opening the contract year of month t.

The annuitant’s own age on a single-life basis; the younger covered person’s on a joint-life basis [S1][S3]. Timed like age(), at the opening anniversary; the payout percentage and the minimum exercise age are read at exercise_age().

exercise_age(t)[source]#

covered_age(t) + 1: the covered age at the anniversary closing the contract year.

All transactions occur at the anniversary and are processed as the last events of the contract year ending there [std], so a lifetime withdrawal taken in the contract year of month t is taken one year older than the age opening it. This is the age that reads the payout percentage band and that clears the contractual minimum exercise age of 50: the anchor cell’s exercise in contract year 8, from issue age 62, is at attained age 70 and reads the 70-79 band.

scenario_value(y, name)[source]#

Step-function lookup of column name in the model point’s scenario at anniversary y.

The argument is an anniversary number, not the frame’s month index. The t column of rate_scenario.csv is an anniversary number, 0 at issue, and the file’s keys are read unshifted; callers pass duration(t) for the anniversary opening the contract year of month t and duration(t) + 1 for the one closing it. Each row states the level that holds from its own anniversary until the next row of the same scenario, so a flat path is one row. Anniversaries before the first row take the first row’s level.

index_level(y)[source]#

I(y): the index level at anniversary y.

Keyed by anniversary number, not by the frame’s month index — a time-point reading, so the return over a contract year differences this level against the next anniversary’s. The S&P 500 price index in the composite; dividends are excluded [S2][S6][R1].

index_return(t)[source]#

R(t) = I(y+1)/I(y) - 1 with y = duration(t): the return over the contract year.

The point-to-point segment runs from the anniversary opening the contract year of month t to the one closing it, where it matures. The return is a property of the contract year, not of the month, and is the same for every month inside it.

cap_rate_in_force()[source]#

c(t): the annual cap actually applied.

The declared 5.25% snapshot held level [std], never below the guaranteed minimum cap of 0.25% [S4]. Set use_guaranteed_scale = True to run the contract on its guaranteed minimums instead — the notes insist the guaranteed and current scales are different assumption classes and “must not be mixed in the code”.

fixed_rate_in_force()[source]#

i_F: the fixed account rate applied, 2.30% declared over a 1.00% guarantee.

Declared [S2] and guaranteed minimum [S10]; use_guaranteed_scale switches to the guarantee, as for the cap.

credit_rate_on(r, method)[source]#

cr: the credit rate for an index return r under method.

The engine the notes require, all branches floored at f = 0%:

cap [std]

max(f, min(c, R)) [S2][S4][S10][R1] — the composite’s baseline.

par

max(f, p x R) [S4][S10][R1].

par_cap

max(f, min(c, p x R)), worked at [R1] as min(80% x 10%, 6%) = 6% — which is where par_rate = 80% comes from, and the case test_crediting_engine_reproduces_the_R1_worked_case asserts.

spread

max(f, p x R - s), the index-margin form [S8][R1].

trigger

d x 1{R >= 0}, a declared rate credited whenever the return is non-negative [R1]; the floor applies when it is not.

The notes declare the two levels these last branches need nowhere, so both are [std]. They print the formss an index margin, d a performance-trigger rate — and neither technical-notes.md nor product-spec.md states a value for either, unlike the cap (5.25% [S2]) and the participation rate (80% [R1]). The shipped spread_rate = 2.00% [std] and trigger_rate = 4.50% [std] are illustrative levels chosen only to exercise the branch, and must not be read as sourced parameters of the composite.

index_cost_rate is deducted from R before any cap or participation rate, which is where a volatility-controlled index’s embedded servicing cost belongs [S2][S10]; it is 0 in the base run [std]. The monthly-sum method is not implemented — see credit_method().

credit_rate(t)[source]#

cr(t): the credit rate the segment of month t’s contract year matures at.

A property of the contract year, like index_return(); it is applied once, at the anniversary where the segment matures.

index_credit_pp(t)[source]#

IC(t) = A(0) x cr(t): the index credit on the segment maturing at month t.

One annual segment per indexed account, created at the anniversary opening the contract year with the balance opening it and maturing at the anniversary closing it; the credit locks at maturity and cannot be lost to later declines [S1], and it is not earned before then — which is why this is zero in every month but the anniversary, and why the indexed balance is flat between anniversaries. That is the sourced rule “withdrawals are not credited with index interest in the year they are taken” [S1], carried to mid-year deaths and surrenders [std]; crediting a part year would be the daily interim value variant the notes exclude.

The credit base is the segment’s opening balance less withdrawals from that account during the segment — the “Interest Credit Basis” [S6] — which collapses to the opening balance here because every transaction happens at an anniversary [std]. Zero in DEPLETED and TERMINATED, where steps 1-3 are skipped.

fixed_interest_pp(t)[source]#

FI(t): interest credited to the fixed account in month t.

The declared rate is an annual effective one, so the month accrues at (1 + i_F)^(1/12) - 1 and twelve of them compound back to exactly i_F — nothing else touches the fixed account between anniversaries, because every contractual transaction falls at one. fixed_interest_ann_pp() is the annual figure the benefit-base stack is computed on.

fixed_interest_ann_pp(t)[source]#

FI over the contract year closing at the anniversary in month t: F(0) x i_F.

On the balance opening that contract year. The twelve monthly accruals of fixed_interest_pp() compound to exactly this, so the annual figure is computed directly rather than accumulated — which is what keeps the benefit-base stack, and through it the whole guarantee, identical to the annual grid’s.

inv_income_pp(t)[source]#

Interest credited to one contract’s account value in month t.

kappa x IC(t) + FI(t): the fixed accrual every month, and the index credit only in the anniversary month where the segment matures. kappa starves the account value in the pure-stacking design [S3]; the benefit-base stack in stack_pp() uses the gross index credit, which is the whole point of that design.

rider_charge_pp(t)[source]#

Phi(t) = phi x BB(0): the GLWB rider charge, deducted once a year [S9].

On the benefit base, not the account value, and taken after index credits are added — the notes’ ordering, which is why the base in the formula is the opening base. In the worked example the base closes at 1.59 times the account value, so charging on the account value would understate the deduction by a growing margin. Deducted from the fixed account first and then proportionately across indexed accounts [S9]. phi is fixed for fifteen contract years and never exceeds 1.50% [S9]; re-declaration after that is not implemented. Once the account value is exhausted the charge stops — there is nothing to deduct it from and income continues [S9].

The charge is annual by contract [S9], so on the monthly grid it lands whole on the anniversary rather than being spread over the year; the monthly deduction documented at one carrier is a variant this composite does not adopt [std].

av_pp_at(t, timing)[source]#

AV per contract in month t, read at the point given by timing.

"BEF_PREM" is the balance carried into the month before the single premium, zero at t = 0 on a new issue; "BEF_INV" is the balance opening the month after it, which is av_pp_init() at the first projected month and AV(t-1) after that; "BEF_FEE" is after the month’s interest — the fixed accrual every month, plus the index credit in an anniversary month; "BEF_WD" is after the rider charge; "EOY" is after the withdrawal. In a month that is not an anniversary the last three coincide, at the opening balance plus the fixed accrual, because every contractual transaction falls at an anniversary.

Only "EOY" is floored at zero: the 0% floor is on the index credit, not on the account value [S7], so "BEF_WD" may legitimately sit below the opening balance or below zero when the rider charge exceeds the credit. Flooring it would silently remove the charge drag that produces depletion.

av_pp(t)[source]#

AV(t): the account value per contract closing month t, after all processing.

av_pp_year_open(t)[source]#

AV(0): the account value opening the contract year containing month t.

The balance the annual index credit and the free withdrawal allowance are computed on: the closing value of the anniversary before, or the Initialisation value in the first projected contract year. On an annual grid this would simply be the previous row’s close; on the monthly grid the previous month is not the previous year, which is what this cells exists to keep straight.

av_fixed_pp_year_open(t)[source]#

F(0): the fixed account balance opening the contract year containing month t.

av_indexed_pp_year_open(t)[source]#

A(0): the indexed balance opening the contract year — the segment’s credit base.

av_fixed_pp_at(t, timing)[source]#

F per contract in month t; see av_pp_at() for timing.

The fixed account is the one balance that moves between anniversaries: it accrues at (1 + i_F)^(1/12) - 1 every month. The rider charge comes out of it first and only then proportionately across the indexed accounts [S9]. The notes give no allocation rule for withdrawals, so they are taken pro rata across the two accounts [std]; with the baseline 100% indexed allocation neither rule is visible.

av_fixed_pp(t)[source]#

F(t): the fixed account balance per contract closing month t.

av_indexed_pp_at(t, timing)[source]#

A per contract in month t: AV - F at the same timing, by construction.

av_indexed_pp(t)[source]#

A(t): the indexed account balance per contract closing month t.

Flat between anniversaries — the segment credits only at maturity — and stepped at each anniversary by that year’s index credit net of its share of the charge and the withdrawal.

av_depletion_pp(t)[source]#

The part of month t’s withdrawal the account value could not fund.

Zero until the account value runs out, then the shortfall between the withdrawal requested and the balance available to meet it. It is the term that makes the account value roll-forward close across the depletion anniversary, where AV(t) is floored at zero.

It is not the same thing as an amount paid. Whether the shortfall is a payment depends on which exhaustion branch the anniversary closes on: in DEPLETED the guarantee is alive and the insurer funds it from its own funds [S1][S3][S9][R1], on the TERMINATED branch there is neither balance nor promise behind it and it is not paid at all. wd_unfunded_pp() carries that half, and only that half is kept out of the ledger.

surr_charge_rate(t)[source]#

sc(t): the surrender charge percentage of month t’s contract year [S5].

9.1, 9, 8, 7, 6, 5, 4, 3, 2, 1% over the ten-year charge period and zero thereafter; the last row of the table holds for every later year. The table is keyed by the contractual 1-based contract year, policy_year(t), so the rate is the same in every month of a year.

vest_rate(t)[source]#

v(t): the vested percentage of the premium bonus in month t’s contract year [S5].

0, 10, …, 100% over eleven contract years, read at policy_year(t). On death 100% vests immediately [S5], which is why the death benefit carries no clawback.

rollup_rate(t)[source]#

g(t): the guaranteed simple rollup rate of month t’s contract year [S2][S9].

Read as a step function of the contract year policy_year(t) from the model point’s schedule: the blended baseline is 5.00% in years 1-10, 2.00% in years 11-20 and zero after [S2]; another documented design is a flat 3.00% over fifteen anniversaries [S9].

payout_rate(a, basis)[source]#

pi(a, basis): the lifetime withdrawal percentage at attained age a [S3].

Five inclusive attained-age bands; the joint column is the single column less 0.50% [S1][S3]. Zero below the first band, because no lifetime withdrawal is permitted there; the last band holds above its upper age.

payout_rate_locked(t)[source]#

The payout percentage in force in the contract year of month t.

Locked at the attained age of the first lifetime withdrawal, exercise_age(), and not re-read afterwards [std]. is_exercise() is a property of the contract year, so the lock holds for every month of the year it is set in and is then carried from anniversary to anniversary. Documented alternatives: one carrier reads the band from the age at the most recent anniversary, letting it step up [S3]; another makes it depend on both issue age and the youngest covered person’s age at exercise [S9]; a third uses sex-distinct factors [S5].

activation_rate(a)[source]#

h(a): the GLWB activation incidence at attained age a [std].

0 below 60, 5% from 60 to the RMD age, 40% at it and 15% above [R1][REG-R64]. rmd_age is a configurable Reference and must not be hard-coded: the statutory age is set by IRC 401(a)(9) as amended by SECURE 2.0 and finalized in T.D. 10001 [REG-R57][REG-R58] and is not printed in the retrieved research material.

Reported only. A single deterministic cell cannot activate a fraction of itself, so the base run exercises at the model point’s income_start_age instead; this cells exists so the incidence assumption is visible and testable rather than implied.

is_exercise(t)[source]#

True through the contract year whose closing anniversary carries the first LW.

Requires an in-force rider, the ACCUM phase opening the year and an exercise_age() at or above both the contractual minimum of 50 [S2][S3][S9] and the model point’s income_start_age. It depends only on the state opening the contract year, which is what lets the phase, the benefit base and the withdrawal be evaluated without a circular reference — and what makes it constant across the year, so every month of the exercise year reads the same phase and the same locked payout percentage.

phase_open(t)[source]#

The phase the annual steps of month t’s contract year are processed under.

The phase opening the contract yearphase_init() in the first projected year and the closing phase of the previous anniversary after that — promoted to INCOME when the first lifetime withdrawal falls in this year. Constant across the twelve months, because every one of the eight steps belongs to the year rather than to the month. Steps 1-3 are skipped when this is DEPLETED and steps 1-7 when it is TERMINATED.

depletion_cause(t)[source]#

True when a charge, an excess withdrawal or a negative MVA touched the AV in month t.

The attribution test of step 5, evaluated before the depletion test: an account value run to zero by an excess withdrawal, a surrender charge or a market value adjustment loses the guarantee entirely, while one run to zero by guaranteed withdrawals and rider charges keeps it [S1][S5][S9]. One carrier’s confinement and terminal illness waivers are themselves excess withdrawals that terminate the income rider [S1] — a trap if waivers are added.

phase(t)[source]#

The phase closing month t, after step 7’s transition at the anniversary.

Step 7 is annual and the account value can only reach zero at an anniversary — nothing between them takes a withdrawal or a charge — so away from one this simply carries phase_open(), and the transition is tested at the anniversary itself.

ACCUM -> INCOME at the first lifetime withdrawal; INCOME -> DEPLETED when the account value reaches zero with no depletion_cause(), which is where the economic value of the guarantee sits; INCOME -> TERMINATED when it reaches zero with one; and any -> TERMINATED when the benefit base reaches zero [S9]. An account value exhausted in ACCUM terminates rather than depletes [std]: the notes define the depleted state only out of INCOME, and before exercise there is no LW to pay.

rider_in_force_open(t)[source]#

Whether the GLWB rider is alive opening the contract year of month t.

glwb_elected() in the first projected contract year — an in-force model point enters on the rider state its row states — and rider_in_force() at the previous anniversary after that. Kept as a cells of its own, like phase_open(), because four formulas need the opening rider state and none of them may reach for a closing one.

rider_in_force(t)[source]#

Whether the GLWB rider is still alive at the end of month t [S9].

It terminates on the earliest of death of the covered person, the benefit base reduced to zero, termination of the base contract, assignment, owner cancellation on or after the earliest cancellation date, or a change in a covered person — with no refund of past charges [S9]. Death is a decrement here, and cancellation and assignment are not modelled, so what survives is the base-reaching-zero and contract-termination legs.

in_growth_period(t)[source]#

T_g: whether the benefit base still rolls up and stacks in month t’s contract year.

To the earlier of the first lifetime withdrawal and contract year 20 [S1][S2]. The year of exercise itself is still inside the window — the worked example credits both the rollup and the stack in contract year 8, the year income starts — which is why the test is on the phase opening the year.

step_up_applies(t)[source]#

Whether the annual step-up BB <- max(BB, AV) is tested at month t.

Annual by construction, so it is tested only at the anniversary.

A [std] generalisation. No retrieved document describes an automatic annual ratchet during deferral; documented instead are an at-exercise step-up to the contract value [S5], an annual benefit amount computed on the greater of base and account value at exercise [S9], and a never-decreasing income amount once withdrawals begin [S3]. step_up_mode = "at_exercise" reduces the model to those designs; "annual" is the superset and the baseline. Under the blended baseline it rarely binds — a dollar of credit adds $1 to the account value and $1.50 to the base — but rarely is not never: on a new issue the bonus opens contract year 1 with an account value of 107,000 against a benefit base of 100,000, so a first contract year with a zero index credit gives an account value of 106,050 against a base of 105,000 and the step-up binds.

rollup_pp(t)[source]#

rollup(t) = g(t) x RB(0): the guaranteed rollup increment, once a year.

A flat dollar increment, not simple interest on the grown base. One carrier computes it on premium less withdrawals [S2] and another on the adjusted initial base [S9]; the latter’s fifteen-year table confirms a constant $3,000 a year on a $100,000 adjusted initial base [S9]. Compounding it inflates the base and every downstream charge and payment.

stack_pp(t)[source]#

stack(t) = m x max(0, IC(t) + FI(t)): the stacking credit at the anniversary.

On realised dollar credits, net of any strategy fee (zero here) and floored at zero [S8][S9] — one specimen’s separately named stacking credit. The gross index credit is used, so the pure stacking design can credit 250% to the base while kappa sends only 50% to the account value [S3][S4]. FI here is the annual fixed interest of the contract year, fixed_interest_ann_pp(), not the anniversary month’s own accrual.

benefit_base_pp_at(t, timing)[source]#

BB per contract in month t, read at the point given by timing.

"BEF_ROLLUP" is the base opening the contract yearbenefit_base_pp_init() in the first projected year and the base at the previous anniversary after it; "BEF_STEP_UP" is after the rollup and the stack; "BEF_WD" is after the annual step-up and is the base the lifetime withdrawal is computed on; "EOY" is after step 5’s proportional reduction. All four steps are annual, so away from an anniversary every timing returns the opening base and the benefit base is flat.

The base is notional: it has no cash value, cannot be withdrawn and cannot be taken as a lump sum [S1][S9].

benefit_base_pp(t)[source]#

BB(t): the GLWB benefit base per contract closing month t.

rollup_base_pp(t)[source]#

RB(t): the rollup base closing month t.

Reduced in the same proportion as the benefit base under the baseline [std] convention [S9], and only at an anniversary, where the reduction happens. Set rb_wd_convention = "dollar" for the alternative documented at another carrier, RB(t) = max(0, RB(0) - G(t)) — “Premium minus Withdrawals” [S2].

lw_pp_at(t, timing)[source]#

LW per contract in month t, read at the point given by timing.

"BEF_WD" is the amount payable at this contract year’s anniversary, after step 4’s ratchet; "EOY" is the closing value, after step 5’s proportional reduction. The ratchet is annual, so away from an anniversary this carries the amount set at the last one — the income in force, which is what a mid-year reading of the rider state means.

At exercise LW = pi x BB(4); afterwards the ratchet LW = max(LW(0), pi x BB(4)) applies so income never decreases [S3]. Unused LW does not carry forward [S9]; one carrier is the exception, accumulating the shortfall without interest [S3]. In DEPLETED the amount is simply carried: there is no base to recompute it from.

lw_pp(t)[source]#

LW(t): the lifetime withdrawal amount per contract closing month t.

wd_scheduled_pp(t)[source]#

The ad hoc gross withdrawal scheduled at the anniversary of month t’s year, else zero.

Read from withdrawal_table.csv, which is not a step function: a contract year with no row takes nothing. Its key column is an elapsed contract year count, not the frame’s month index — a row keyed y is a withdrawal at the anniversary closing contract year y + 1 — so it is read at duration(t) and the file is unchanged by the move to a monthly grid. Lifetime withdrawals are generated by the rider, not scheduled here.

wd_pp(t)[source]#

G(t): the gross withdrawal taken at the anniversary in month t.

Zero in every other month: both the lifetime withdrawal and the ad hoc schedule are contractually annual, so the finer grid moves no cash between anniversaries. In INCOME, utilization_intensity() x LW plus any scheduled ad hoc amount; in DEPLETED, exactly LW, paid by the insurer from its own funds; in ACCUM, only the scheduled amount. Gross by construction: a contract promising a stated net check would need a gross-up solve, which is not implemented.

wd_guar_pp(t)[source]#

min(G(t), LW(t)): the guaranteed portion of month t’s withdrawal.

A withdrawal is applied first against the guaranteed annual amount; that portion reduces the account value dollar for dollar and leaves the benefit base and the income amount unchanged [S9].

wd_excess_pp(t)[source]#

E(t) = max(0, G(t) - LW(t)): the excess withdrawal in month t.

The quantity that permanently reduces the guarantee, and that destroys it entirely if it exhausts the account value [S1][S5][S9]. Note this is not the chassis’s wd_excess_pp, which is the amount exposed to the surrender charge; that is wd_charge_base_pp() here. Before exercise LW is zero, so the whole withdrawal is an excess.

wd_unfunded_pp(t)[source]#

The part of G(t) that neither the account value nor the guarantee pays.

G(t) - min(G(t), max(0, AV(2)(t))) in a month whose closing phase is TERMINATED; zero everywhere else. The two exhaustion branches are not symmetric, and this is where they part. In DEPLETED the account value is gone but the guarantee is alive, so the insurer pays LW from its own funds for the rest of the covered life [S1][S3][S9][R1] and nothing is unfunded. On the TERMINATED branch the account value is driven to zero by an excess withdrawal, a surrender charge or a negative MVA, and “an account value run to zero by an excess withdrawal loses the guarantee entirely” [S1][S5][S9] — [S5] is explicit that the contract “as well as the rider will be considered Surrendered”. Beyond the account value there is then nothing to pay from: no balance and no promise. Paying the requested amount in full would honour the guarantee in the very year the withdrawal destroys it, which is the trap the notes’ attribution test exists to avoid.

The notes write step 4 as AV(5)(t) = AV(2)(t) - G(t) with no cap because they write it for the funded case. The cap here is on the payment, not on the withdrawal: wd_pp() still carries the amount requested, so the excess still sets depletion_cause() and still drives rho to 1 and the benefit base to zero. Only wd_payment_pp() and the three ledger lines are reduced.

wd_guar_paid_pp(t)[source]#

The guaranteed portion of month t’s withdrawal that is actually paid.

min(G, LW) less whatever of wd_unfunded_pp() the excess could not absorb. The account value funds the withdrawal in the order the notes compose it — the first LW dollars are the guaranteed portion and the remainder is the excess [S9] — so an under-funded withdrawal leaves the excess unpaid first and eats into the guaranteed portion only after that [std], the notes being silent on a withdrawal the account value cannot fund. Equal to wd_guar_pp() in every month except a terminating anniversary.

wd_excess_paid_pp(t)[source]#

The excess portion of month t’s withdrawal that is actually paid.

max(0, E(t) - wd_unfunded_pp(t)): the excess is the first thing an under-funded withdrawal loses, being the part with no promise behind it. Equal to wd_excess_pp() in every month except a terminating anniversary.

wd_cum_pp(t)[source]#

Wcum(t): cumulative gross withdrawals from entry to the end of month t.

A running total over months, though only the anniversary months add to it.

free_wd_base(t)[source]#

The base of the free withdrawal amount: the AV opening the contract year of month t.

10% of the account value at the anniversary that opens the contract year, available from contract year 1 and with no carry-forward [S1][S3][S5][S6][S9][S10]; the combination is [std]. Fixing the base at a known prior value is what lets the chargeable amount be evaluated without a fixed point, and it is what makes the allowance a property of the contract year rather than of the month.

free_wd_allow(t)[source]#

FW(t) = 0.10 x AV(0): the free withdrawal amount for month t’s contract year.

wd_free_pp(t)[source]#

The free-allowance portion of the anniversary’s withdrawal: the part of FW(t) it uses.

The chassis name for this quantity (MYGA_US_S, UL_US_S, RILA_US_S), so the library spells the free-allowance portion of a withdrawal one way everywhere. Zero away from the anniversary, where no withdrawal is taken and the whole allowance therefore remains against a mid-year surrender.

A [std] choice the notes flag as open. [S9] says only that withdrawals up to the annual benefit amount carry no charge “even if greater than the Free Withdrawal Amount”; it does not say whether they exhaust it. The convention here — the guaranteed amount consumes the free amount — is the insurer-favourable reading and is what the worked example uses, leaving 12,800.00 - 10,144.16 = 2,655.84 against the surrender. The alternative leaves the whole FW(t) available against the excess.

free_wd_remain(t)[source]#

The free withdrawal amount left after the contract year’s guaranteed withdrawal.

The whole allowance away from an anniversary, where no withdrawal has been taken yet — which is what a mid-year surrender is charged against.

wd_charge_base_pp(t)[source]#

X(t): the part of month t’s withdrawal exposed to charge, clawback and MVA.

max(0, G(t) - FW(t)) before exercise and max(0, E(t) - remaining free) after it [std] — withdrawals up to LW carry no surrender charge, no MVA and no bonus clawback even when ``LW`` exceeds the free withdrawal amount [S9]. Zero once the account value is gone: post-depletion income is not a chargeable withdrawal.

surr_charge_base_pp(t)[source]#

X(t) at a full surrender in month t: AV(t) less the free amount left.

The chassis calls this surr_excess_pp; renamed here only for symmetry with wd_charge_base_pp(), because E(t) already names a different quantity in these notes. The construction is the chassis’s and the source is the same [S10].

bonus_factor()[source]#

b/(1+b): the factor that strips the bonus out of a bonus-inclusive account value.

The clawback factor is b/(1+b), not b [S10] — the account value already contains the bonus, so using b over-recovers by (1+b).

bonus_clawback_on(base, vested, bonus)[source]#

CB = (1 - A) x [B/(1+B)] x C: the non-vested bonus recovery [S10].

A is the vested percentage for the contract year, B the bonus percentage and C the gross withdrawal less the free withdrawal amount. Worked verbatim at [S10]: contract year 5, bonus 16%, gross $100,000, free $7,000 gives 0.70 x 0.1379 x 93,000 = 8,979. Written as a function of its three arguments so that arithmetic is testable directly.

mva_ref_yield(y)[source]#

i_y: the MVA reference index level at anniversary y, from the scenario table.

Keyed by anniversary number, like index_level(), so the level in force at a transaction closing the contract year of month t is mva_ref_yield(duration(t) + 1). A declared investment-grade corporate bond yield index; generic here [std], and named as Barclay’s US Credit Index in the linear-form products [S6][S7].

mva_term(t)[source]#

n/12: the years remaining in the MVA period at the end of month t.

The MVA period is the ten-year surrender charge period [S7][S10], and the notes state the term in months — which is what the monthly grid can now say exactly: surr_charge_period - (t + 1)/12, a transaction in month t being settled at its end. At the worked example’s anniversary 8 — month 95, the close of contract year 8 — that is 10 - 96/12 = 2 years, the n = 24 months the notes quote, so every anniversary value is the annual grid’s; a surrender six months earlier now carries the 30 months it actually has instead of being rounded to the anniversary. The remaining term reaches zero at anniversary 10, so a transaction carries a live adjustment through contract year 9 and none in contract year 10.

mva_in_force(t)[source]#

Whether an MVA applies to a transaction in month t.

MVA = 0 outside the MVA period and on the death benefit [S5][S6][S7][S10]; the second is handled where the death benefit is valued.

mva_rate(t)[source]#

The market value adjustment rate, [(1+i0)/(1+it)]^(n/12) - 1 [S10].

Signed, and negative when the reference yield has risen [S10]. This is the ratio-of-yield-factors family, which is naturally bounded; the linear family (i0 - it) x T adopted by the fixed-deferred chassis [S6][S7] is unbounded and must be collared separately. The FIA composite restates the MVA rather than inheriting it, which is why the chassis’s mva_family switch is absent here.

The reference yield is read at the anniversary closing the contract year of month t, duration(t) + 1, which is the transaction date the adjustment applies at.

mva_pp_on(t, base, gross)[source]#

The collared MVA on a currency base withdrawn as gross in month t.

|MVA| <= max(0, G - SC - CB - MGV) [S10], so a negative MVA combined with the surrender charge and the clawback can never push the proceeds below the guaranteed minimum value, and the maximum positive adjustment cannot exceed the maximum negative one. In the worked example’s surrender trace the limit is max(0, 122,865.84 - 5,965.56 - 84,605.80) = 32,294.48 and the raw -1,158.64 sits well inside it.

The notes are silent on how a surrender-value collar applies to a partial withdrawal, and both readings are shipped. The limit is stated on the gross withdrawal, and its stated purpose is that the adjustment “never reduces the surrender value below the guaranteed minimum value” — a full-surrender concept, and the only case the worked example demonstrates. Read literally, a partial withdrawal smaller than the floor gives a non-positive limit and therefore no adjustment at all. mva_collar_basis selects:

"gross" [std] default

the notes’ literal text, reference = G(t).

"surrender_value"

the test applied to the contract rather than to the payment, reference = AV(t), so a partial withdrawal carries the adjustment its rate produces up to what the remaining floor allows.

The two are identical on the surrender path, where G(t) = AV(t), so the worked example reproduces under either; they differ only where the notes say nothing.

wd_charge_pp(t)[source]#

SC(t) on month t’s withdrawal: sc(t) x X(t) [S5][S10].

wd_clawback_pp(t)[source]#

CB(t) on month t’s withdrawal [S10].

wd_mva_pp(t)[source]#

MVA(t) on month t’s withdrawal, collared [S10].

wd_payment_pp(t)[source]#

The cash paid on month t’s withdrawal, G - unfunded - SC - CB + MVA.

The surrender charge and the clawback are internal transfers within the account value, not fee income: the account value falls by the gross G(t) while the holder receives this. Reporting them as income while also projecting the account value net of them double-counts.

wd_unfunded_pp() is zero in every month except a terminating anniversary, where it strips out the part of the request the account value could not fund and the destroyed guarantee does not cover. On the DEPLETED branch it stays zero and the whole of LW is paid, which is the guarantee.

wd_reduction_rate_on(av, lw, gross)[source]#

rho for a gross withdrawal of gross against av with guaranteed amount lw.

E / (AV - LW), the post-exercise construction stated verbatim at [S9]: account value $100,000, base $200,000, annual benefit amount $10,000, withdrawal $28,000 gives denominator $90,000, excess $18,000, reduction 20%, base to $160,000 and benefit amount to $8,000. Passing lw = 0 gives the pre-exercise form G / AV [S1][S3][S5][S9] — the two denominators differ by exactly LW, which is the pitfall the notes name. rho = 1 when the denominator is non-positive: the base goes to zero and the rider terminates [S9].

wd_reduction_rate(t)[source]#

rho(t): the proportional reduction applied to BB, LW and RB in month t.

Pre-exercise the denominator is the gross account value after the rider charge; post-exercise it is that value net of the guaranteed amount [S9]. Zero when there is no excess: a withdrawal inside LW leaves the guarantee untouched.

surr_charge_pp(t)[source]#

SC(t) on a full surrender in month t.

surr_clawback_pp(t)[source]#

CB(t) on a full surrender closing month t [S10].

mva_pp(t)[source]#

MVA(t) on a full surrender closing month t, collared [S10].

surr_value_pp(t)[source]#

The surrender value before the nonforfeiture floor, AV - SC - CB + MVA.

The composition order is account value, then charge and clawback and MVA computed on the pre-deduction excess, then the floor — the chassis’s order, and the one contractual element the FIA notes inherit rather than restate.

surr_benefit_pp(t)[source]#

CSV(t) = max(AV - SC - CB + MVA, MGV): the surrender benefit paid [S1][S6][S10].

Zero in DEPLETED: there is no account value and no surrender value [S1][S9]. A binding floor is not a separate cash flow — it raises the benefit, and MGV - SV is a reconciliation quantity only.

mgsv_charge_pp(t)[source]#

The annual contract charge deducted from the Model #805 floor, at the anniversary.

Model #805 4.A permits $50 a year, accumulated at the nonforfeiture rate, together with premium tax actually paid and indebtedness [R2]. All are zero here [std] because no retrieved product declares an actual annual policy fee, which makes the modeled floor slightly conservative.

mgsv_pp(t)[source]#

MGV(t): the Model #805 guaranteed minimum value closing month t.

The floor accretes every month at (1 + i_nf)^(1/12) - 1, so twelve months compound to exactly the annual nonforfeiture rate and the anniversary values are the annual grid’s; the withdrawal is deducted at the anniversary, where it is taken. Accrete, then deduct — the worked example pins the ordering at 93,811.84 x 1.01 - 10,144.16 = 84,605.80. The fixed-deferred chassis deducts and then accretes; the two are different arithmetic on the same concept, which is exactly what that file warns against carrying across.

Accruing monthly rather than once at the anniversary is what makes the floor mean something for a mid-year death or surrender, which is where it most often binds. The opening value is 0.875 x P excluding the bonus at issue [S10][R2]. rider_charge_from_mgsv switches on the treatment documented at two carriers, one deducting the rider charge from the floor and the other its allocation charge [S1][S2][S3][S4]; the composite does not [std].

mgsv_rate_statutory(cmt5, option_cost)[source]#

The Model #805 4.B/4.C indexed nonforfeiture rate [R2][R3].

max(0.0015, min(0.03, round(CMT5 to 1/20 of 1%) - 0.0125 - delta)) where delta is the equity-index reduction of 4.C: available only when the annualized option cost of the guaranteed index features is at least 25 basis points, and then equal to min(100 bp, option cost), certified annually [R3].

Two traps the notes name. The 4.B floor is 15 basis points, not 1% — the composite’s 1.00% is a [std] pick inside the 0.15%-3% corridor, not the statutory floor. And whether the 15 bp floor survives the 4.C reduction is not stated in the retrieved text [unverified]; the specimen contract language, “the interest rates will range between 0.15% and 3%”, suggests it does [S10], which is the reading implemented. The statute defines the minimum: the contract rate must satisfy mgsv_rate >= mgsv_rate_statutory(...), which is what mgsv_rate_is_compliant() tests, not the reverse inequality.

mgsv_rate_is_compliant(cmt5, option_cost)[source]#

True when the contract nonforfeiture rate meets the statutory minimum [R2][R3].

mort_rate(t)[source]#

q(t): the annual mortality rate of the contract year containing month t.

Read at age(t), the attained age opening that contract year — the library convention of Term_US_S and MYGA_US_S. The rate actually applied to a month is mort_rate_mth(); this cells stays annual so the table is read the way it is published and so the assumption is visible at the frequency it is set at.

The shipped table is the same illustrative Makeham annuitant curve as products/fixed_deferred_annuity [std], not a published basis. The prescribed basis is the 2012 IAM Basic / 2012 IAR generational family with Projection Scale G2, q_x^(2012+n) = q_x^(2012) x (1 - G2_x)^n, rounding applied from the 2012 period rate each time and never by compounding an already rounded rate [REG-R59][REG-R60]; it may not be redistributed here, so swap it in by repointing Data.mort_table_file. Generational projection is not implemented for the same reason. mort_ae_factor carries the A/E deviation, 100% [std] against the 2020-2024 payout experience study [REG-R61]; the spelling is the library’s shared one, so the A/E factor reads the same in every model that has one.

mort_rate_mth(t)[source]#

The monthly mortality rate, 1 - (1 - q)^(1/12).

The uniform-force conversion the library uses everywhere: twelve of these compound to exactly the annual rate of the contract year, so the anniversary in-force numbers are the annual grid’s and only their distribution through the year changes. Dividing by twelve instead would overstate the decrement, and the error grows with the rate.

shock_lapse_rate(t)[source]#

w_shock: the surrender rate in the year the surrender charge expires [R8].

The single most important behavioral fact in the product. 33% without a GLWB rider against 10% with one in force but not activated, and 5% [std] once it is activated, extrapolated from the finding that contracts with GLWBs lapse less than those without and that activated GLWBs lapse least [R1][R8]. Applying a plain fixed-deferred shock lapse — roughly 52%-56% for fixed-rate deferred annuities [REG-R63, unverified] — to an FIA with an in-force rider materially understates the tail the product is sold for.

lapse_rate_base(t)[source]#

w_base(t): the base annual surrender rate of month t’s contract year [std].

2% in years 1-3, 3% in 4-6, 4% in 7-9, 5% in year 10, the shock in the year the surrender charge expires, and 6% thereafter — the shape [R1] describes: low early, rising through the charge period, spiking at expiry, then falling back but staying above pre-shock levels. The notes’ separate M_shock multiplier is absorbed here, because the shock is stated as an absolute rate rather than as a factor on the ultimate; see the Space docstring.

lapse_moneyness_factor(t)[source]#

M_money(t) = clamp(1 - 0.6 max(0, BB/AV - 1), 0.2, 1.0) [std].

Surrender is suppressed when the guarantee is in the money, because a rational surrender destroys a guarantee worth BB - AV in benefit-base terms. The observed direction is documented [R1][R8]; the functional form is not.

Evaluated on the state closing the contract year, at anniv_mth(), so the factor is a property of the year like the base rate it multiplies. Reading it month by month would let the fixed-account accrual drift the ratio within the year and make the annual surrender assumption depend on where in the year it is sampled.

lapse_rate(t)[source]#

w(t): the annual surrender rate of the contract year containing month t.

min(0.35, w_base(t) x M_money(t)), and zero in DEPLETED — with no account value there is nothing to surrender, so lapse is impossible [S1][S9] and leaving the decrement on would silently truncate the most expensive part of the liability. In the contract year the contract terminates, the survivors leave as a deemed full surrender [S5][S9], so this returns 1.0 there and the in-force roll-forward closes.

Evaluated on the phase closing the contract year, at anniv_mth(), so it is one rate for the whole year; lapse_rate_mth() is what a month actually applies.

lapse_rate_mth(t)[source]#

The monthly surrender rate, 1 - (1 - w)^(1/12).

The same uniform-force conversion as mort_rate_mth(), so twelve months compound to exactly the contract year’s annual rate — including the shock in the year the surrender charge expires, which is spread. That is deliberate and is the opposite of Term_US_S, where the notes state the shock as a rate “applied in full at the end of the final level-period month”: here the notes state it as the surrender rate of contract year 11, a year that carries no surrender charge in any month, so there is no contractual date inside it for the decision to cluster on.

The one rate that is not spread is the deemed full surrender at termination. That is a contractual event, not a behavioral assumption: the contract “as well as the rider will be considered Surrendered” [S5] at the anniversary the account value is destroyed, so the survivors leave in that month and not before.

pols_if(t)[source]#

The in-force probability at the start of month t.

The library convention, set by Term_US_S and savings.CashValue_SE: this is the count entering the month, before its decrements, and it is the weight carried by every cash flow reported on the same row of result_cf(). At an anniversary month 12k + 11 it is the count that reaches the anniversary and therefore the weight the year’s withdrawal and charges carry — on the annual grid that weight was the count entering the year, which is the one place the finer grid changes the answer rather than only the resolution.

pols_if(entry_mth()) = pols_if_init(), the recursion runs pols_if(t + 1) = pols_if(t)(1 - q_m(t))(1 - w_m(t)) with death before surrender [std], and it is zero from proj_len() on, where the survivors of the last projected month have left through pols_maturity(). Because the monthly rates compound to the annual ones, pols_if(12k) is exactly the annual-grid model’s pols_if(k): the notes’ l(k).

pols_if_at(t, timing)[source]#

In-force in month t read at the point given by timing.

"BEF_DECR" and "BEF_MORT" are both the count entering the month, which is pols_if() itself — this product has no annuitization decrement, so the chassis’s two timings coincide. "BEF_LAPSE" is after deaths, and "AFT_DECR" is the count at the end of month t and therefore pols_if(t + 1). Both decrements are the monthly rates.

pols_death(t)[source]#

Deaths in month t, on the monthly mortality rate.

pols_lapse(t)[source]#

Full surrenders in month t, on the survivors of that month’s mortality.

pols_maturity(t)[source]#

Survivors leaving at the projection horizon, non-zero only at proj_len() - 1.

Not a decrement — the horizon runs out — but needed for the in-force roll-forward to close. Numerically zero under the shipped mortality table, whose annual rate reaches 1.000000 at age 120, hence a monthly rate of 1.0, so the projection closes itself; see the Space docstring.

pols_decr(t, kind)[source]#

The number of contracts leaving in month t by benefit kind.

claim_pp(t, kind)[source]#

The benefit paid per contract at the end of month t by kind.

"DEATH" is max(AV(t), MGV(t)) with 100% bonus vesting, no surrender charge and no MVA [S1][S2][S5][S10], and is never below the cash surrender benefit [R2 6]. Note the FIA composite restates this rather than inheriting the chassis’s full account value floored at the surrender benefit. "LAPSE" is the surrender benefit. "MATURITY" is the account value floored at the guaranteed minimum at the horizon. All three are zero in DEPLETED: there is no account value, no surrender value and no death benefit, and the only exit is death [S1][S9].

claim_from_av_pp(t, kind)[source]#

The account value released per contract by a claim of kind.

AV(t) for all three kinds; the difference from claim_pp() is claims_over_av(), positive when the Model #805 floor binds and negative when the surrender charge and a negative MVA bite.

premiums(t)[source]#

Premium income: the single premium, at the start of month 0 on a new issue.

There is no issue-instant row, so the premium is a beginning-of-month flow of the first projected month rather than a row of its own. An in-force model point paid its premium before the projection starts, so this is zero throughout for it — as are the acquisition expense and the premium tax that key off it.

prem_to_av_pp(t)[source]#

Premium and bonus credited to the account value per contract, at issue only.

prem_to_av(t)[source]#

Premium and bonus credited to the block’s account value, at issue only.

wd_guar(t)[source]#

Guaranteed withdrawal outgo in month t, weighted by pols_if(t).

The count entering the month, which is the in-force number reported on the same row of result_cf(); at an anniversary that is the count which reaches it. On wd_guar_paid_pp(), not on the amount requested, so a terminating anniversary cannot pay a guaranteed withdrawal the account value could not fund and the destroyed rider no longer covers. Zero in DEPLETED, where the same payment is reported as income_payments() so the two lines partition the total rather than overlap.

wd_excess(t)[source]#

Excess withdrawal outgo in month t: E - unfunded - SC - CB + MVA.

Weighted by pols_if(t), the count entering the month, and on wd_excess_paid_pp(): the excess is the first part of an under-funded withdrawal to go unpaid.

income_payments(t)[source]#

Post-depletion income outgo: LW at the anniversary while phase = DEPLETED.

This is the guarantee. There is no account value behind it, no rider charge is deducted, and it runs for the rest of the covered life [S1][S3][S9][R1].

Annual like every other withdrawal, so it falls in the anniversary month and is weighted by the contracts that reach it. Paying it in each of the twelve months would multiply the guarantee twelvefold and break the partition withdrawals = wd_guar + wd_excess + income_payments that result_cf() documents.

withdrawals(t)[source]#

Total withdrawal outgo in month t, wd_payment_pp(t) x pols_if(t).

Equal to wd_guar() + wd_excess() + income_payments() in every month, the three lines partitioning it by the notes’ ledger categories. All four are published by result_cf(), so summing every column of that frame double-counts the withdrawal; the ledger identity is on the total, and the split is there because the notes report the three categories separately.

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

Benefit outgo in month t, for one kind or, with kind=None, all three.

claims_from_av(t, kind)[source]#

The account value released by a claim of kind, in-force weighted.

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

Benefit paid less the account value released, claims - claims_from_av.

Signed and a reconciliation quantity only: positive exactly when the Model #805 floor binds, negative when the surrender charge, the clawback and a negative MVA bite. A binding floor does not add a top-up cash flow — it raises the benefit.

commissions(t)[source]#

Acquisition commission, zero [std].

The technical notes fold the whole acquisition cost into a single 6.0%-of-premium acquisition expense and give no separate commission, so comm_rate_acq is 0 and this line exists for shape only. It is kept rather than dropped because every model in the library carries the same cash flow vocabulary.

premium_taxes(t)[source]#

Premium tax on the single premium, 0% on the composite state basis [std].

inflation_factor(t)[source]#

The expense inflation factor in month t, 1.025^(t/12).

A fractional power of the annual rate, so twelve months of it compound to exactly one year of inflation and the factor at each anniversary is the annual grid’s.

expenses(t)[source]#

Insurer expenses: acquisition at issue and inflating maintenance monthly [std].

6.0% of the single premium in month 0 on a new issue, and $80 per contract per year inflating at 2.5% — one twelfth of it a month, weighted by the in-force at the start of the month. Spreading the maintenance charge is the point of the finer grid for this line: it is an insurer cost accruing continuously, not a contractual event tied to the anniversary, so it now falls where it is incurred and is weighted by survivors who have not yet left. premiums(t) is zero everywhere but the first month, so the acquisition term simply drops out elsewhere. Both are standardizations: no retrieved document discloses FIA acquisition cost or per-contract maintenance.

net_cf(t)[source]#

Net cash flow in month t.

The index credit, the rider charge, the surrender charge, the bonus clawback and the movement of the Model #805 floor are internal accounting entries: they drive the account value, the benefit base and the benefit amount but are never ledger lines of their own. Only amounts paid to or received from the contract holder, and the insurer’s own expenses, appear here.

av_at(t, timing)[source]#

The in-force weighted account value in month t; see av_pp_at().

Every timing inside the month carries pols_if(t) — the count entering it — because all processing happens before the decrements. "EOY" carries the count leaving the month, pols_if(t + 1): zero at the horizon, where pols_maturity() has taken the survivors out.

inv_income(t)[source]#

Interest credited to the whole block in month t.

rider_charges(t)[source]#

Rider charges deducted from the whole block’s account value in month t.

wd_from_av(t)[source]#

The account value released by month t’s withdrawals, gross of charge and MVA.

av_depletion(t)[source]#

The block’s withdrawal outgo the account value could not fund in month t.

av_change(t)[source]#

The change in the block’s account value over month t.

Closing less opening, the opening being read at "BEF_PREM" so that the single premium of a new issue’s month 0 is a movement rather than part of the balance brought in. Away from the first month av_at(t, "BEF_PREM") is exactly the previous month’s closing value.

check_av_roll_fwd_resid(t)[source]#

Account value roll-forward residual for month t; the signed float.

AV(t) - AV(0)(t) = premium in + interest credited - rider charges - withdrawals + the part of a withdrawal the account value could not fund - the account value released by each claim kind. The cash paid on a surrender may differ from the account value released — by the charge, the clawback, the MVA and any binding Model #805 floor — and that difference is claims_over_av(), deliberately not part of this identity. It closes month by month on the finer grid, where the interest term is the month’s accrual and the claim terms are that month’s exits.

The per-t residual is kept because it is what a debugging session wants when check_av_roll_fwd() returns False; the boolean is defined in terms of it.

check_av_roll_fwd()[source]#

Check the account value roll-forward: True when it closes at every projected t.

No argument and a bool, following savings.CashValue_SE and the rest of this library, so one test can call the same check across every model. The signed residual for a single month is check_av_roll_fwd_resid().

The tolerance is absolute and loose enough for an account value of order $100,000 accumulated over a century of months; it is not a modelling assumption.

check_pols_roll_fwd_resid(t)[source]#

In-force roll-forward residual for month t; the signed float.

pols_if(t) - pols_if(t+1) = deaths + surrenders + horizon exits. Zero to floating point for every t, including the last, t = proj_len() - 1, where pols_if(proj_len()) is zero and pols_maturity() carries the survivors out.

check_pols_roll_fwd()[source]#

Check the in-force roll-forward: True when it closes at every projected t.

No argument and a bool, as for check_av_roll_fwd(); the signed residual for a single month is check_pols_roll_fwd_resid().

result_cf()[source]#

Result table of cashflows, indexed by the month t from entry_mth().

The frame runs t = entry_mth() proj_len() - 1, one row per projected month and no issue-instant row: the first row of a new-issue model point carries the premium and the acquisition expense as beginning-of-month flows alongside that month’s own activity. result_cf_annual() sums it into contract years.

pols_if is the in-force count at the start of month t, which is the weight every cash flow on the same row carries — dividing a row’s cash flow by its per-contract amount returns this column exactly. Every withdrawal and benefit driven by the contract falls in an anniversary month, so those columns are zero in eleven rows out of twelve; deaths, surrenders and maintenance expense fall in every row.

withdrawals is the library-wide column for partial withdrawal payments and is the total; the three columns after it — wd_guar, wd_excess and income_payments — partition that total into the notes’ own ledger categories. They are published alongside it, not instead of it, so summing every column of the frame double-counts the withdrawal. net_cf is income-positive, as everywhere in this library.

result_pols()[source]#

Result table of in-force movements, indexed by the month t.

pols_if opens the row and pols_if_aft_decr closes it, and the two decrement columns are the monthly rates actually applied; the annual rates they are converted from are mort_rate() and lapse_rate(), constant across each contract year. The closing column is the opening figure of the next row in every month but the last, where pols_maturity() takes the survivors out and the projection stops. The identity check_pols_roll_fwd_resid asserts is therefore pols_if(t) - pols_if(t+1) = pols_death + pols_lapse + pols_maturity.

result_av()[source]#

Result table of the per-contract account value and surrender trace, indexed by month.

The first eight columns walk the worked example’s account value rows, now month by month: fixed_interest_pp accrues in every row, index_credit_pp, rider_charge_pp and wd_pp only in anniversary rows. The rest are the surrender trace, available in every month rather than only at the anniversaries the notes tabulate — which is the reading a mid-year surrender needs.

result_glwb()[source]#

Result table of the GLWB rider state per contract, indexed by the month t.

The benefit base is notional — no cash value, not withdrawable, not payable as a lump sum [S1][S9] — so none of these columns is a cash flow. They are the drivers of the rider charge, the lifetime withdrawal and, in the end, the post-depletion income. Every one of them is annual: the rider state is flat between anniversaries and steps at each one, so the rows of interest are the anniversary rows t = 12k + 11.

result_cf_annual()[source]#

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

Every cash flow column is the total of its twelve months; pols_if is the count at the start of the contract year, pols_if(12 * duration), which is the number the annual-step model carried on the same row.

Against the annual grid this agrees exactly on pols_if, premiums, commissions and premium_taxes, the last three being the single premium and two rates on it. It does not agree on the withdrawal columns, the claims or the expenses, and so not on net_cf: a withdrawal taken at the anniversary is now weighted by the contracts that reach it rather than by those that entered the year, claims fall in the month of exit, and maintenance expense accrues monthly. That gap is the point of the finer grid — see the Space docstring.