The Projection Space#
The by-contract projection of MYGA_US_S.
The Space is parameterized by point_id, so Projection[1] is an ItemSpace
projecting model point 1:
>>> Projection[1].result_cf() # the anchor cell
>>> 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_deferred_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
MYGA_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 |
surr_charge_age_cap_file |
data.surr_charge_age_cap_table() |
surr_charge_age_cap.csv |
rate_scenario_file |
data.rate_scenario() |
rate_scenario.csv |
withdrawal_file |
data.withdrawal_table() |
withdrawal_table.csv |
mva_factor_file |
data.mva_factor_table() |
mva_factor_table.csv |
Projection basis
t counts policy months, 1-based, with t = 0 the issue instant carrying the
premium, the acquisition commission, the premium tax and the initial branch of every
recursion. policy_year(t) = ceil(t / 12) is the contract year, so anniversaries fall
at t = 12, 24, ... and the five-year guarantee period ends at t = 60.
Within a month: the free-withdrawal counters roll, the guarantee-period boundary is applied, the elective withdrawal is taken and the tax basis updated — all at the beginning of the month (BOM) — then interest is credited, the Model #805 floor rolls forward and decrements are applied in the order annuitization, mortality, surrender at the end of the month (EOM) [std], with every decrement benefit valued on the post-crediting account value.
Twelve monthly factors (1 + i_cr)^(1/12) reproduce the declared effective annual
rate exactly, so av_pp(12) = premium_pp() * (1 + i_cr). Do not additionally compound
daily; the discretization affects only the placement of interest within a month.
Naming
Cells names follow lifelib’s basiclife.BasicTerm_S and savings.CashValue_SE
wherever those models have an analogue — 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
month. The technical notes use compact actuarial symbols instead. The mapping is:
Notes symbol |
Cells |
Meaning |
|---|---|---|
t |
duration_mth(t) |
Elapsed policy months |
y = ceil(t/12) |
policy_year(t) |
Contract year containing month t |
y - 1 |
duration(t) |
Completed contract years |
x |
age_at_entry |
Issue age (ANB) |
x + y - 1 |
age(t) |
Attained age in month t |
n |
guar_period |
Guarantee period in years |
(none) |
policy_term |
Years from issue to maturity_age |
(none) |
proj_len |
Last projection month |
P |
premium_pp |
Single purchase payment |
i_cr(t) |
credit_rate(t) |
Declared credited rate, eff. annual |
f(t) - 1 |
credit_rate_mth(t) |
Monthly credited rate |
s_ren |
renewal_spread |
Renewal declaration spread (Reference) |
i_nf |
mgsv_rate |
GMSV accumulation rate (2.80%) |
g - 1 |
mgsv_rate_mth |
Monthly GMSV rate |
i_stat |
mgsv_rate_statutory(cmt5) |
Model #805 indexed rate |
GMIR |
gmir |
Guaranteed minimum interest rate |
MR(t) |
market_rate(t) |
Market competitor rate |
AV(t) |
av_pp(t) |
Account value at end of month t |
AV(t-1), AV’(t), AV(t) |
av_pp_at(t, timing) |
BEF_WD / BEF_INV / EOM |
l(t) x AV(t) |
av_at(t, timing) |
In-force weighted account value |
(interest credited) |
inv_income_pp(t) |
Interest credited in month t |
(cumulative interest) |
interest_credited_pp(t) |
Interest credited to date |
MGSV(t) |
mgsv_pp(t) |
Model #805 floor at end of month t |
c(t) |
mgsv_charge_pp(t) |
Monthly slice of the contract charge |
d(t) |
mgsv_wd_deduct_pp(t) |
Withdrawal deducted from the floor |
FWB(y) |
free_wd_base(y) |
Free-withdrawal base at the anniversary |
0.10 x FWB(y) |
free_wd_allow(y) |
Allowance for contract year y |
FW(t) |
free_wd_avail(t) |
Unused allowance at BOM of month t |
(FW after W(t)) |
free_wd_remain(t) |
Unused allowance after the withdrawal |
W(t) |
wd_pp(t) |
Gross amount removed from the AV |
(free part of W) |
wd_free_pp(t) |
Free portion of W(t) |
E(t) on a withdrawal |
wd_excess_pp(t) |
Withdrawal exposed to charge and MVA |
E(t) on a surrender |
surr_excess_pp(t) |
Surrender exposed to charge and MVA |
sc(y) |
surr_charge_rate(t) |
Surrender charge rate in force |
sc_clock(t) |
surr_charge_year(t) |
Year within the current schedule |
(schedule key) |
surr_charge_id(t) |
initial / renewal / none |
C(t) |
surr_charge_pp(t) |
Charge on a full surrender |
C(t) on a withdrawal |
wd_charge_pp(t) |
Charge on a partial withdrawal |
mu(t) |
mva_rate(t) |
MVA rate, signed |
M(t) |
mva_pp(t) |
Capped MVA on a full surrender |
M(t) on a withdrawal |
wd_mva_pp(t) |
Capped MVA on a partial withdrawal |
cap(.) |
mva_pp_on(t, base) |
The capped MVA on any currency base |
T(t) |
mva_term(t) |
MVA duration in years |
i0 |
mva_ref_yield_locked(t) |
Reference yield locked at period start |
it |
mva_ref_yield(t) |
Reference yield at month t |
Phi(t) |
mva_factor_geometric(a, b, tau) Geometric MVA factor |
|
F_s |
mva_duration_factor(t) |
Declared-differential duration factor |
prem_accum_gmir_pp(t) |
Premium less prior withdrawals at the GMIR |
|
s_adm |
mva_admin_spread |
Geometric MVA expense adder (Reference) |
SV(t) |
surr_value_pp(t) |
Gross surrender value before the floor |
SB(t) |
surr_benefit_pp(t) |
Surrender benefit paid |
gp_end(t) |
gp_end(t) |
Months left in the guarantee period |
(the 30-day window) |
in_gp_window(t) |
True in the guarantee-period-end window |
basis(t) |
tax_basis_pp(t) |
IRC 72 investment in the contract |
(taxable part of W) |
taxable_wd_pp(t) |
Income-first taxable amount |
l(t-1) |
pols_if(t) |
In-force at the start of month t |
l(0) |
pols_if_init |
In-force at issue |
l(t) |
pols_if_at(t, “AFT_DECR”) |
In-force at the end of month t |
(intra-month l) |
pols_if_at(t, timing) |
BEF_DECR / BEF_MORT / BEF_LAPSE / AFT_DECR |
q(t) |
mort_rate_mth(t) |
Monthly mortality rate |
(annual q_x) |
mort_rate(t) |
Annual mortality at the attained age |
w(t) |
lapse_rate_mth(t) |
Monthly total surrender rate |
w_annual(t) |
lapse_rate(t) |
Annual total surrender rate |
Base(y) |
lapse_rate_base(t) |
Prescribed base lapse by contract year |
G |
gmir_factor |
VM-22 GMIR factor |
Market(t) |
lapse_dyn_market(t) |
Dynamic-lapse market term |
Rate(t) |
lapse_dyn_rate(t) |
Dynamic-lapse rate term |
Phi_MVA(t) |
mva_lapse_factor_at(t) |
MVA factor gating the dynamic term |
BF |
lapse_buffer |
Dynamic-lapse buffer factor (Reference) |
X |
lapse_dyn_exponent(t) |
Dynamic-lapse exponent |
a(t) |
annuitization_rate(t) |
Monthly annuitization election rate |
l a |
pols_annuitization(t) |
Annuitization elections |
l (1-a) q |
pols_death(t) |
Deaths |
l (1-a)(1-q) w |
pols_lapse(t) |
Full surrenders |
(none) |
pols_maturity(t) |
Deemed-maturity annuitizations |
P at t = 0 |
premiums(t) |
Premium income |
(premium credited) |
prem_to_av_pp(t) |
Premium credited to the account value |
W + M - C |
withdrawals(t) |
Withdrawal payments |
(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 |
0.02 x P |
commissions(t) |
Acquisition commission |
(50/12) x 1.025^(y-1) |
expenses(t) |
Maintenance expense |
premium tax |
premium_taxes(t) |
Premium tax |
NetCF(t) |
net_cf(t) |
Net cash flow |
Six names needed care, and all six are collisions the notes themselves carry.
d(t) in these notes is the withdrawal deducted from the Model #805 floor in
processing step 7, not deaths as in Term_US_A; it becomes
mgsv_wd_deduct_pp(), leaving pols_death() unambiguous. c(t) is the monthly
slice of the annual contract charge, not conversions, and becomes
mgsv_charge_pp(). E(t) names two different currency bases — the excess of a
withdrawal over the free allowance and the excess of the whole account value over it
at a full surrender — which wd_excess_pp() and surr_excess_pp() separate; the
same symbol is expenses in Term_US_A, which here is expenses(). X is
the dynamic-lapse exponent here and the premium tax in Term_US_A, split into
lapse_dyn_exponent() and premium_taxes(). T(t) is the MVA duration in
years while t is the policy month, so it becomes mva_term(). And the floor
itself carries three labels in the sources — MGSV in this library, “GMSV” in the specimen
[S11], “MGV” in the fixed-indexed annuity notes — one concept, named mgsv_pp()
throughout.
Two further names carry a library-wide convention that differs from the notes’ own indexing, and in both cases the notes’ quantity survives under a second name rather than being dropped.
pols_if() is the count in force at the start of month t, matching
Term_US_A (pols_if(1) == pols_if_init()) and savings.CashValue_SE, and it
is the weight applied to that same month’s cash flows — withdrawals(),
expenses() and the pols_if column of result_cf() now reconcile row by row,
which they did not while pols_if was the closing count. The notes’ end-of-month
l(t) is unchanged and unmoved; it is read as pols_if_at(t, "AFT_DECR"), and
pols_if(t + 1) == pols_if_at(t, "AFT_DECR") in every month but the last, where
pols_maturity() empties the block.
lapse_rate() is the annual total surrender rate and lapse_rate_mth() the
monthly one, matching the mort_rate() / mort_rate_mth() pair and the eight
other models that spell it that way. The notes write the monthly rate w(t) and the
annual rate w_annual(t), so the pairing here is the notes’ own — only the suffixes
move.
Three rows of the table above are References, not Cells, because the notes give each
as a single assumption number rather than a function of t: s_ren is
renewal_spread, the spread in i_cr^ren(t) = max(GMIR, MR(t) - s_ren) (0.00% on the
base run, 1.00% in the notes’ scenario [std]); BF is lapse_buffer, the 50 bp
band inside which the dynamic-lapse response is exactly zero; and s_adm is
mva_admin_spread, the 25 bp administrative-expense adder in the geometric MVA’s
denominator, which is 0 in Voya’s variant [S3][S4]. Rebasing any of the three is an
assignment to the Reference, with no formula change. P_accum@GMIR, by contrast, is a
recursion and so a Cells: prem_accum_gmir_pp() is the level the gmir_floor cap
holds AV + M above [S13].
Two quantities the notes list as model point attributes are computed here instead,
because each is fixed by a rule the notes also state: av_initial is
premium_pp() * (1 - load_prem_rate) with no front-end load [S5][S10][S16], and
mgsv_initial is net_consideration_ratio * premium_pp() — 87.5% of gross
consideration [R1 4.A(2)]. Only tax_basis_initial stays a model point column, because
it genuinely varies with tax status.
Timing arguments
Account value, following CashValue_SE’s av_pp_at:
"BEF_WD"AV(t-1), at the start of monthtbefore the elective withdrawal."BEF_INV"AV'(t), after the withdrawal and after annuitization elections, before crediting."EOM"AV(t), after crediting. Equal toav_pp().
Policy counts, following CashValue_SE’s pols_if_at:
"BEF_DECR"l(t-1), in force at the start of montht. Equal topols_if()."BEF_MORT"after annuitization elections,
l(t-1)(1 - a(t))."BEF_LAPSE"after deaths,
l(t-1)(1 - a(t))(1 - q(t))."AFT_DECR"after surrenders,
l(t)— the notes’ end-of-month in-force.
Benefit kind arguments are "DEATH", "LAPSE", "ANNUITIZATION" and
"MATURITY". Any other value of any of these raises ValueError.
pols_maturity and the projection horizon
The technical notes state no projection horizon: a MYGA under the rollover
architecture renews indefinitely, and the notes’ own base-lapse pattern is written as a
repeating five-year cycle with no terminal date. The model runs to the contract
anniversary at attained age maturity_age = 100 [std] — the last attained age in
the sourced cap band on the renewal surrender charge, which the notes tabulate as 4% at
94, 3% at 95, 2% at 96, 1% at 97 and 0% at 98-100 [S1][S2]. The cap reaches zero at 98;
100 is where the sourced band stops, and it is past the statutory deemed maturity date of
Model #805 section 8 [R1]. The survivors at that
anniversary are annuitized out through pols_maturity(), zero in every month but the
last, so that
- pols_if(t) - pols_if(t+1) = pols_annuitization(t) + pols_death(t)
pols_lapse(t) + pols_maturity(t)
holds for every t — the start-of-month count opens the row, the four exits are taken
during it and the next month opens on what is left. Including the last month, where the
block would otherwise appear to
lose lives with no cause. The name follows BasicTerm_S.pols_maturity and the
construction follows Term_US_A.
Cells Descriptions#
- tax_status()[source]#
Tax status of the contract: NQ, IRA, Roth or inherited.
Reported only. The base run is non-qualified [std]; no RMD module is implemented.
P: the single purchase payment per contract.
- guar_period()[source]#
n: the guarantee period in years; the surrender charge and MVA periods equal it.
- declared_rate_initial()[source]#
The declared credited rate guaranteed for the initial term, 4.45% p.a. [S11].
- gmir()[source]#
The guaranteed minimum interest rate, 0.25% p.a. [S11]; a floor on any declared rate.
- mgsv_rate()[source]#
i_nf: the contract GMSV accumulation rate, 2.80% p.a. [S11].
The Model #805 floor accretes at this rate. It must be at least the statutory indexed rate
mgsv_rate_statutory(); crediting the floor at more is permitted and simply produces a higher floor [R1 4.B].
- mgsv_annual_charge()[source]#
The annual contract charge deducted from the Model #805 floor.
$0 representative [S11]; the statutory maximum is $50 p.a. [R1 4.A].
- mgsv_wd_convention()[source]#
How a withdrawal reduces the Model #805 floor:
grossornet_of_charges.gross[S11] deducts W(t) itself;net_of_charges[S9] deducts the cash actually paid, W(t) + M(t) - C(t), keeping the floor higher. Both are live in the market and the difference compounds at i_nf for the rest of the contract.
- renewal_architecture()[source]#
rollover(Camp A) orannual_redeclare(Camp B).rolloverstarts a fresh guarantee period, surrender charge schedule and MVA period at each boundary [S1][S2][S5][S11], so the shock lapse repeats every n years.annual_redeclareredeclares the rate each anniversary with no further surrender charge and no MVA [S13], so the shock happens once.
- free_wd_rule()[source]#
pct_avorinterest_only.pct_av[S10] is the representative 10%-of-base allowance;interest_only[S5][S6] is the prior contract year’s credited interest. The notes’ third value,greatest_of[S13], is not implemented: no formula is given for it.
- free_wd_mva_exempt()[source]#
Whether the free allowance is exempt from the surrender charge and the MVA.
True is the retail MYGA convention [std] [S8][S11] and gives
E(t) = AV(t) - FW(t). False is the registered-contract convention [S3][S4] and givesE(t) = AV(t), collapsing the surrender benefit to the multiplicative formmax(AV(t) x (1 + mu(t) - sc(y)), MGSV(t)). The notes apply the flag to both the charge and the adjustment; in the market it strictly concerns the adjustment.
- mva_cap_rule()[source]#
sym_sc,min_sc_interest,asym_sc_snfl,gmir_floorornone.The cap, not the formula family, is the largest cross-carrier divergence in the source set, so it is a first-class model parameter. See
mva_pp_on().
- mva_ref_yield_at_issue()[source]#
i0 at issue: the MVA reference yield locked for the initial period, 5.00% [std].
Premium tax deducted from the contract, 0% [std] [S3][S16].
- gp_start_month(t)[source]#
The month boundary at which the current guarantee period began; 0 for the first.
- in_gp_window(t)[source]#
True in the 30-day guarantee-period-end window [S1][S2][S5][S6].
The notes place the window in the month after a guarantee period closes, i.e. at
t - 1 == 0 (mod 12n)witht > 1, so for n = 5 it is months 61, 121, 181, … In the window the full account value is available with no surrender charge and no MVA, and it is the only month in which annuitization is elected. Underannual_redeclarethere is exactly one window, at the end of the initial term.
- scenario_rate(t, name)[source]#
Step-function lookup of column
namein the model point’s rate scenario.Each row of rate_scenario.csv states the level that holds from its own month until the next row of the same scenario, so a flat path is one row.
- market_rate(t)[source]#
MR(t): the market competitor rate driving renewal declarations and dynamic lapse.
VM-22 defines it for a 5 to 7 year guarantee period as the 7-year Treasury rate plus a 50% A / 50% AA spread less the pricing spread [R2 6.B.5]; the model takes it as an exogenous input because the index is a state-filed variable [std].
- mva_ref_yield(t)[source]#
it: the MVA reference yield at month t, from the scenario table [std].
The source design is Barclay’s US Credit Index with a state-varying formula [S8]; the model takes a scalar series rather than hard-coding an index.
- credit_rate(t)[source]#
i_cr(t): the declared credited rate in force, effective annual.
The initial declared rate for the whole initial term [S11], then the renewal rule
max(GMIR, MR(m) - s_ren)[std] withmthe redeclaration month. The base run setsrenewal_spread = 0, so the credited rate equals the competitor rate and the dynamic lapse term is exactly zero.
- credit_rate_mth(t)[source]#
The monthly credited rate,
(1 + i_cr(t))^(1/12) - 1.Twelve of these factors reproduce the declared effective annual rate exactly, so the discretization moves interest only within a month. Do not compound daily as well.
- mgsv_rate_statutory(cmt5)[source]#
The Model #805 indexed nonforfeiture rate at a 5-year CMT level [R1 4.B].
max(0.0015, min(0.03, round(CMT5 to 1/20 of 1%) - 0.0125)). Two traps the notes call out: the floor is 15 basis points, not the widely repeated 1%; and the statute defines the minimum rate, it does not cap the contract rate — the contract must satisfymgsv_rate() >= mgsv_rate_statutory(cmt5), which is whatmgsv_rate_is_compliant()tests, not the reverse inequality.
- mgsv_rate_is_compliant(cmt5)[source]#
True when the contract GMSV rate meets the statutory minimum at that CMT5 [R1 4.B].
- prem_to_av_pp(t)[source]#
Premium credited to the account value per contract; the whole premium at t = 0.
- wd_scheduled_pp(t)[source]#
The gross withdrawal scheduled for month t in withdrawal_table.csv, else zero.
- wd_pp(t)[source]#
W(t): the gross amount removed from the account value at BOM of month t.
Gross by construction: a contract promising a stated net check needs a gross-up solve, which is not implemented [S3]. Two sources add up here — the amount scheduled in withdrawal_table.csv, and the free-withdrawal utilization variant
free_wd_util x FW(y)taken in the first month of each contract year. Base run utilization is 0 [std], so on model points other than the worked-example anchor the account value runs unbroken.
- av_pp(t)[source]#
AV(t): account value per contract at the end of policy month t.
AV(t) = (AV(t-1) - W(t)) x (1 + i_cr(t))^(1/12)withAV(0) = P[S4][S5][S16]. No charges are deducted from the account value: this chassis carries no front-end load, no annual fee and no rider charges [S5][S10][S13][S16].
- av_pp_at(t, timing)[source]#
Account value per contract at month t, read at the point given by
timing."BEF_WD"isAV(t-1),"BEF_INV"isAV'(t) = AV(t-1) - W(t)and"EOM"isAV(t). At t = 0 all three are the premium.
- interest_credited_pp(t)[source]#
Cumulative interest credited to one contract from issue to the end of month t.
Used by the
min_sc_interestMVA cap, which limits the adjustment to the lesser of the surrender charge and the interest credited to date [S8][S9].
- prem_accum_gmir_pp(t)[source]#
Premium less prior withdrawals accumulated at the GMIR.
The reference level for the
gmir_floorMVA cap, under whichAV + Mmay not fall below it [S13]; the surrender charge may still breach that level.
- free_wd_base(y)[source]#
FWB(y): the free-withdrawal base fixed at the start of contract year y.
Purchase payments in year 1, the account value at the most recent anniversary thereafter [S10]. Making the base a fixed known amount at each anniversary is what lets
E(t) = AV(t) - FW(t)be evaluated without a fixed point.
- free_wd_allow(y)[source]#
The free-withdrawal allowance for contract year y, non-cumulative [S4][S16].
pct_av:free_wd_rate x FWB(y)[S10].interest_only: the interest credited over the prior contract year [S5][S6], hence zero in year 1.greatest_of[S13] is not implemented and raises.
- free_wd_avail(t)[source]#
FW(t): the unused free-withdrawal allowance at BOM of month t, before W(t).
Reset to the whole allowance in the first month of each contract year; carried forward within the year; never carried across an anniversary [S4][S16].
- wd_free_pp(t)[source]#
The portion of W(t) covered by the free allowance, hence free of charge and MVA.
- free_wd_remain(t)[source]#
The unused free-withdrawal allowance after month t’s withdrawal.
This, not
free_wd_avail(), is the allowance a full surrender at the end of month t may still shelter.
- wd_excess_pp(t)[source]#
E(t) for a partial withdrawal:
max(0, W(t) - FW(t))[S8][S9][S10].The whole of W(t) when
free_wd_mva_exemptis False [S3][S4].
- surr_excess_pp(t)[source]#
E(t) for a full surrender at the end of month t:
AV(t) - FW(t)[S8][S11].The whole of AV(t) when
free_wd_mva_exemptis False [S3][S4].
- surr_charge_id(t)[source]#
The surrender charge schedule in force:
initial,renewalornone.Under
rollovera fresh renewal schedule attaches at each guarantee-period boundary [S1][S2][S11]; underannual_redeclareno charge applies after the initial term [S13]. Getting this clock wrong relocates the shock lapse by years.
- surr_charge_year(t)[source]#
sc_clock: the contract year within the schedule in force, 1..n.
Resets to 1 at each renewal under
rollover[S1][S2][S11]. Voya and Nationwide instead run the clock from the original purchase payment date so it never restarts [S3][S4]; that variant is not carried on this chassis.
- surr_charge_age_cap(t)[source]#
The attained-age cap on the surrender charge: 4% at 94 down to 0% at 98+ [S1][S2].
Returns 1.0 below the sourced age band, i.e. no cap.
- surr_charge_rate(t)[source]#
sc(y): the surrender charge rate in force at month t.
9/8/7/6/5 over the initial term [S10] and 5/4/3/2/1 over a renewal term [S2], capped by the attained-age scale [S1][S2], zero once a schedule runs out, and zero in the 30-day guarantee-period-end window and before issue [S1][S2].
- mva_term(t)[source]#
T(t): the MVA duration in years at the end of month t [S8].
The notes write it as (days to the end of the current contract year / 365) + whole years remaining in the MVA period. On a monthly grid those two terms collapse algebraically to
(12 n k - t) / 12— the time to the end of the current MVA period — and the same quantity serves as the geometric branch’stau, which the notes define as days to maturity / 365.25. The two divisors differ in the sources; on exact twelfths they cannot.
- mva_in_force(t)[source]#
True when an MVA applies at month t.
mu(t) = 0unconditionally once the MVA period has expired [S8][S13][S16] and in the 30-day guarantee-period-end window [S2]. It is also zero on a death benefit [S2][S4][S8][S13][S16], on annuitization [S16], and on RMD or waiver withdrawals [S2][S5][S13]; the first two are handled where those benefits are valued and the third is not implemented.
- mva_ref_yield_locked(t)[source]#
i0: the reference yield locked at the start of the current guarantee period.
The model point’s issue value for the initial term; re-locked at the prevailing reference yield at each renewal under
rollover[S2][S11].
- mva_ref_term_years(tau)[source]#
The whole-year maturity at which the distribution yield is read [S4].
Partial years round up, capped at the guarantee period: the Nationwide contract reads a 3-year yield for 985/365.25 = 2.69 years remaining while the exponent keeps the exact day count. With a single scalar reference yield per scenario this selection determines nothing; it is carried so a curve-valued extension has the rule in place.
- mva_factor_geometric(i_deposit, i_dist, tau)[source]#
Phi: the geometric MVA factor
((1+a)/(1+b+s_adm))^tau[S3][S4].s_admismva_admin_spread, 25 bp in the Nationwide contract, covering the cost of liquidating fixed-income investments and structurally biasing the adjustment against the owner; Voya’s variant sets it to zero [S3].
- mva_duration_factor(t)[source]#
F_s: the declared-differential adjustment factor by years remaining [S14].
A contractual table of modified durations, read on the
Ic < 6%orIc >= 6%column and linearly interpolated between whole years, flat beyond the last row.
- mva_rate(t)[source]#
mu(t): the market value adjustment rate at month t, signed and dimensionless.
linear_duration[std]:(i0 - it) x T(t)[S8][S9] — the first-order approximation of the geometric form, always in the contract holder’s disfavour when rates rise.geometric:Phi(t) - 1[S3][S4].declared_differential:(Ic - In) x F_son the insurer’s own new-money rate [S14], withIntaken as the market rate [std].
- mva_pp_on(t, base)[source]#
M: the capped MVA amount on a currency base at month t.
Five cap rules, all first-class parameters because the cap is the largest single cross-carrier divergence in the source set:
sym_sc[std]clamp(M_raw, -C, +C)[S2].min_sc_interestclamp(M_raw, -K, +K)withK = min(C, interest credited to date)[S8][S9].asym_sc_snflM <= +Cwith no downside cap; onlySB >= MGSVbinds below [S12].gmir_floorAV + M >=premium less prior withdrawals accumulated at the GMIR [S13]. The floor is stated for a full surrender; applying it to a partial withdrawal base is an extension of the source.noneUncapped and fully two-sided [S3][S4].
- surr_value_pp(t)[source]#
SV(t): the gross surrender value,
AV(t) + M(t) - C(t), before the floor.The composition order is account value, then MVA, then surrender charge, then the nonforfeiture floor, and it is not interchangeable: both M and C are computed on E(t) before either is deducted [S8]. Charging first and adjusting the net figure understates the adjustment by
sc x |M|; flooring before the MVA silently removes the downside protection.
- surr_benefit_pp(t)[source]#
SB(t): the surrender benefit actually paid,
max(SV(t), MGSV(t))[S8][S9][S12].A binding floor is not a separate cash flow: it raises SB(t), and
MGSV(t) - SV(t)is a reconciliation quantity only.
- mgsv_charge_pp(t)[source]#
c(t): the monthly slice of the annual contract charge deducted from the floor.
- mgsv_wd_deduct_pp(t)[source]#
d(t): the withdrawal deducted from the Model #805 floor in month t.
W(t) under the
grossconvention [S11]; the cash actually paid,W(t) + M(t) - C(t), undernet_of_charges[S9].
- mgsv_pp(t)[source]#
MGSV(t): the Model #805 minimum guaranteed surrender value at end of month t.
MGSV(t) = [MGSV(t-1) - d(t) - c(t)] x (1 + i_nf)^(1/12)withMGSV(0) = 0.875 x P[R1 4.A][S11]. Premium tax actually paid and indebtedness are further permitted deductions, accumulated at the same rate, and are zero here.
- taxable_wd_pp(t)[source]#
The income-first taxable portion of month t’s withdrawal [R6 72(e)(3)(A)].
Taxable to the extent the account value gross of surrender charge exceeds the basis. A reported quantity: it generates no insurer cash flow.
- tax_basis_pp(t)[source]#
basis(t): the IRC 72 investment in the contract at the end of month t [R6].
Reduced only by the non-taxable remainder of a withdrawal. Reported, not a cash flow.
- mort_rate(t)[source]#
The annual mortality rate at the attained age and sex, from mort_table.csv.
The shipped table is an illustrative Makeham curve [std], not a published basis. The prescribed basis is the 2012 IAM Basic table with Projection Scale G2 and the VM-22 Table 6.7 factors [R2 6.B.8][R9], which may not be redistributed here; swap it in by repointing
Data.mort_table_file. Mortality is second-order on this product: death pays the full account value with no charge and no MVA.
- gmir_factor()[source]#
G: the VM-22 GMIR factor stepping the base lapse rate [R2 6.B.5].
1.25 at a GMIR of 1.0% or less, 1.00 up to 2.5%, 0.70 above. The representative 0.25% GMIR [S11] therefore takes the highest multiplier, which is the conservative end of the prescribed scale.
- lapse_rate_base(t)[source]#
Base(y): the prescribed base lapse rate for the contract year containing month t.
VM-22 Table 6.5 mapped onto the five-year architecture as a [std] extension [R2][REG-R36].
rollover: 1% inside each guarantee period and 75% in the contract year following each expiry, repeating with period n.annual_redeclare: 1% to expiry, 75%, then 10%, 7.5% and 3% thereafter. The architecture switch, not the level, is the first-order modeling decision — Camp A creates a repeating shock, Camp B a single one.
- lapse_dyn_exponent(t)[source]#
X: 2.0 during the initial surrender charge period, 2.5 at the shock and after [R2].
- lapse_dyn_market(t)[source]#
Market(t): the credited-minus-competitor response of the VM-22 dynamic lapse form.
-1.25 (CR - MR)^Xwhen the contract is competitive, zero inside the 50 bp buffer,+1.25 (MR - BF - CR)^Xwhen it is not [R2 6.B.5]. The buffer and the power make the sensitivity strongly convex aroundCR = MR.
- lapse_dyn_rate(t)[source]#
Rate(t): the dynamic term gated by the surrender-charge and negative-MVA haircut.
Market(t) x max(0, 1 - 5 (1 - CSV/AV))withCSV/AV = SB(t)/AV(t)[R2 6.B.5], so the dynamic response switches off entirely once the combined haircut reaches 20%. The ITM factor is 1 for an Accumulation contract with no guaranteed living or death benefit, which the guideline states explicitly.
- mva_lapse_factor_at(t)[source]#
Phi_MVA(t): the factor multiplying the dynamic term while an MVA is in force.
The VM-22 prescribed values are 0 in force and 1 expired [R2], i.e. the regulator’s view is that an in-force MVA completely neutralizes disintermediation. The model exposes the in-force value as
mva_lapse_factor, defaulting to the best-estimate 0.35 [std] rather than the prescribed 0; set it to 0 for the statutory run. It is a judgement, not a calibration — sensitivity-test it.
- lapse_rate(t)[source]#
w_annual(t):
clamp(Base(y) G + Rate(t) Phi_MVA(t), 0.005, 0.90)[R2 6.B.5].The annual total surrender rate, paired with
lapse_rate_mth()exactly asmort_rate()is paired withmort_rate_mth().On the base deterministic run
MR = CRandrenewal_spread = 0, soMarket(t) = Rate(t) = 0and this isBase(y) x 1.25: contract years 1 to 5 at 1.25% and the shock year at 90%, the 93.75% product capped.
- lapse_rate_mth(t)[source]#
w(t): the monthly total surrender rate,
1 - (1 - w_annual)^(1/12).This is the rate the monthly decrement actually applies;
lapse_rate()is the annual rate it is derived from.
- annuitization_rate(t)[source]#
a(t): the monthly annuitization election rate.
1.0% of in-force in each 30-day guarantee-period-end window after contract year 1 and zero elsewhere [std]. No public annuitization take-up study for deferred annuities was located; VM-22 prescribes 0% at all projection intervals for the standard projection [R2 6.B.6], which is a statutory simplification rather than an experience estimate. Set
annuitization_rate_window = 0for the statutory alternative.
- pols_if(t)[source]#
l(t-1): the in-force probability at the start of policy month t.
The library-wide convention, following
Term_US_AandCashValue_SE: this is the count that opens month t and the weight applied to that same month’s cash flows, so thepols_ifcolumn ofresult_cf()reconciles with the row it sits on.pols_if(0) = pols_if(1) = pols_if_init(), the issue instant carrying no decrement.The notes’ end-of-month
l(t) = l(t-1)(1 - a(t))(1 - q(t))(1 - w(t))ispols_if_at(t, "AFT_DECR"); the two agree aspols_if(t + 1)in every month butproj_len(), wherepols_maturity()annuitizes the survivors out and the block opens the following month empty.
- pols_if_at(t, timing)[source]#
In-force at month t read at the point given by
timing."BEF_DECR"isl(t-1), equal topols_if();"BEF_MORT"is after annuitization elections;"BEF_LAPSE"is after deaths;"AFT_DECR"is after surrenders and is the notes’ end-of-monthl(t). The order is the notes’l(t) = l(t-1)(1 - a)(1 - q)(1 - w)[std order].
- pols_maturity(t)[source]#
Deemed-maturity annuitizations, non-zero only at
proj_len().Not a decrement — the projection horizon runs out — but needed for the in-force roll-forward to close; see the Space docstring.
- annuitization_pp(t)[source]#
The amount transferred to the payout model on an annuitization election.
The full account value
AV'(t)in the 30-day window; outside it,AV'(t)less the surrender charge computed onAV'(t), with no MVA [S1][S2][S5][S16][std].The notes name
SV(t)for the non-window case, andSV(t) = AV(t) + M(t) - C(t)is composed on the post-crediting account value — but the same notes make annuitization an elective BOM transaction and setmu(t) = 0on it. The two cannot both hold on a monthly grid, and the model resolves it at BOM: annuitizers leave before the month’s interest is credited, which is exactly whatclaim_from_av_pp()releases for them (AV'(t), notAV(t)). This cells is therefore the BOM analogue ofSV(t)— the same composition on the account value the annuitant actually takes with them. ReadingSV(t)literally would pay out one month of interest the block never credited to those contracts, and the difference(AV(t) - AV'(t)) * (1 - sc(y))would surface as a spuriousclaims_over_av(). The choice is inert on every shipped model point, wherea(t)is zero outside the window; a test pins it open.No nonforfeiture floor is applied in either branch:
SV(t)is the pre-floor value. Payout factors are out of scope: no retrieved product document contains an annuity rate table, so the accumulation model emits the transfer and hands the amount on.
- claim_pp(t, kind)[source]#
The benefit paid per contract in month t by
kind."DEATH"is the full account value with no charge and no MVA [S1][S2][S13], floored at the cash surrender benefit and hence at MGSV(t) [R1 6]; on the base run the floor is inert because 4.45% exceeds 2.80%, but at a renewal rate down to the GMIR it overtakes the account value, so it is tested at every duration [std]."LAPSE"is SB(t)."ANNUITIZATION"isannuitization_pp()."MATURITY"is the account value floored at MGSV at the projection horizon.
- claim_from_av_pp(t, kind)[source]#
The account value released per contract by a claim of
kind.Equal to
claim_pp()except where the Model #805 floor binds; the difference isclaims_over_av(). Annuitizers leave at BOM and so releaseAV'(t), before the month’s interest.
Premium income: the single purchase payment at t = 0.
- withdrawals(t)[source]#
Withdrawal payments in month t:
(W(t) + M(t) - C(t))weighted byl(t-1).The ledger separates the free part (
wd_free_pp, never charged or adjusted) from the excess (wd_excess_pp); this line is the total cash paid. The weight ispols_if(), the start-of-month count printed on this same row ofresult_cf(), because the withdrawal is taken at BOM before any decrement.
- claims_over_av(t, kind=None)[source]#
Benefit paid less the account value released,
claims - claims_from_av.Signed, and normally negative on this chassis: a surrender pays out less than the account value it releases, by the surrender charge and any negative MVA. It turns positive exactly when the Model #805 floor binds and SB(t) exceeds AV(t). Either way it is a reconciliation quantity, not a cash flow of its own — a binding floor raises SB(t), it does not add a separate top-up line, and the charge and the MVA are internal accounting entries rather than ledger lines.
- commissions(t)[source]#
Acquisition commission: 2.00% of premium paid at issue [std].
No retrieved document discloses MYGA commission; this is a pure modeling assumption.
Premium tax on the purchase payment, 0% [std] [S3][S16].
- expenses(t)[source]#
Insurer expenses: acquisition at issue and inflating maintenance monthly [std].
(50 / 12) x 1.025^(y-1)per contract per month, in-force weighted. VM-22 prescribes $35 x 1.025^(offset) per contract for business the company does not administer [R2 6.B.3]; the $50 figure is a [std] uplift of that anchor to a self-administered block.expense_acqis 0 because this chassis’s only acquisition cost is the commission.
- net_cf(t)[source]#
Net cash flow in policy month t.
Interest credited, the surrender charge, the MVA and the movement of the Model #805 floor are internal accounting entries, not ledger lines: they drive AV, MGSV and the benefit amount but are never cash flows 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 at month t; see
av_pp_at()fortiming.Each timing carries the in-force appropriate to that point in the month:
l(t-1)before the withdrawal,l(t-1)(1 - a(t))once annuitizers have left, and at the end of the month the count that survives every exit —pols_if(t + 1), which isl(t)except atproj_len(), where the deemed maturity empties the block. Usingpols_if_at(t, "AFT_DECR")there would leave the matured account value standing in the block and released as a claim, andcheck_av_roll_fwd()would not close.
- wd_from_av(t)[source]#
The account value released by month t’s withdrawals, gross of charge and MVA.
- check_av_roll_fwd_resid(t)[source]#
Account value roll-forward residual in month t; zero to floating point.
AV(t) - AV(t-1) = premium in - withdrawals out + interest credited - the account value released by each of the four claim kinds. Note that the cash paid on a surrender may exceed the account value released when the Model #805 floor binds; that excess isclaims_over_av()and is not part of this identity. t = 0 is the premium deposit itself and is excluded.The signed residual, for a debugging session that needs to know where and by how much
check_av_roll_fwd()failed.
- check_av_roll_fwd()[source]#
True when the account value roll-forward closes in every projected month.
Takes no argument and returns a bool, following
CashValue_SE.check_av_roll_fwdand the library convention, so one test can call the same check across every model.check_av_roll_fwd_resid()carries the per-month signed residual, and this isabs(resid) <= check_tol_avovert = 1 .. proj_len().
- check_pols_roll_fwd_resid(t)[source]#
In-force roll-forward residual in month t; zero to floating point.
pols_if(t) - pols_if(t+1) = annuitizations + deaths + surrenders + deemed maturities: the start-of-month count opens the row, the four exits are taken during it and the next month opens on what is left. In the notes’ own indexing that isl(t-1) - l(t), sincepols_if()is the start-of-month count.The signed residual;
check_pols_roll_fwd()is the bool over allt.
- check_pols_roll_fwd()[source]#
True when the in-force roll-forward closes in every projected month.
No argument, returns a bool over
t = 1 .. proj_len(), matchingcheck_av_roll_fwd()and the library convention.
- result_cf()[source]#
Result table of cashflows, indexed by policy month t from 0 to
proj_len().t = 0carries the purchase payment, the acquisition commission and the premium tax, exactly as the notes’ cash flow ledger indexes them.The
pols_ifcolumn is the start-of-month count, which is the weight applied to every cash flow on that same row, so the printed in-force reconciles with the printed flows:withdrawals(t) / wd_payment_pp(t)andexpenses(t) / ((expense_maint / 12) * inflation_factor(t))both return it.
- result_pols()[source]#
Result table of in-force movements, indexed by policy month t.
pols_ifopens the row (the start-of-month count, the same columnresult_cf()prints), the four exit columns are taken during the month, andpols_if_aft_decris the notes’ end-of-monthl(t). The closing balance is next month’spols_if, which equalspols_if_aft_decrin every month butproj_len(), where the deemed maturity empties the block.