The FIA_US_S Model#
Reference liability cash flow model for U.S. fixed indexed annuities with a GLWB.
FIA_US_S is the executable counterpart of
products/fixed_indexed_annuity/technical-notes.md in the lifelib-products
library. It projects gross liability cash flows for a single-contract model point of a
single-premium fixed indexed annuity carrying a guaranteed lifetime withdrawal benefit:
a 7% premium bonus vesting over ten years, one annual point-to-point indexed account
credited at max(0%, min(cap, index return)), a notional benefit base growing by a
guaranteed simple rollup plus 150% of realised dollar credits, a 0.95% rider charge
on that base, a lifetime withdrawal locked at the attained age of first exercise, and —
the economic centre of the product — a guaranteed income stream that survives
account-value exhaustion and pays for life.
The base contract is the deferred annuity chassis of
MYGA_US_S: the surrender-benefit composition order and the NAIC
Model #805 floor construction are the chassis’s. Everything else here is restated by
the FIA notes with its own parameters and must not be carried across from the chassis —
the account-value roll-forward is index-credit driven rather than interest-accretion
driven, the MVA is the ratio form [(1+i0)/(1+it)]^(n/12) - 1 rather than the linear
(i0 - it) x T, the death benefit is max(AV, MGV), and the lapse architecture is
rider-suppressed rather than a plain shock at surrender-charge expiry. The chassis’s
MGSV and these notes’ MGV are one quantity under two source labels; this
model uses the chassis name mgsv_pp()
throughout.
Spaces. The model contains two:
DataReads the seven input CSVs and holds their filename References. It takes no parameters, so each file is read once per model.
ProjectionThe by-contract projection, parameterized by
point_id:Projection[1]is an ItemSpace projecting model point 1. It reaches the input tables through itsdataReference, which resolves to the singleDataSpace.
The split matters for more than tidiness. Because Projection is parameterized, every
Projection[N] is a separate ItemSpace with its own cells cache; readers placed there
would re-read every file for every model point. In Data they are evaluated once,
however many contracts are projected.
Input data is external: CSVs in the model folder’s parent directory, read at run time rather than stored inside the model. The model folder itself holds no data, so the model and its inputs must travel together.
Projection basis. Annual steps. t counts contract years, and each t is
also the anniversary that ends contract year t, because the notes make the
anniversary the single event date: every mechanic in the composite is annual — annual
point-to-point crediting [S2][S4][S10], the rider charge at the end of each contract
year [S9], the annual benefit base update [S9] and the annual lifetime withdrawal. Note
the contrast with MYGA_US_S, whose t counts months: that
chassis credits daily and needs a grid fine enough to resolve a 30-day window, whereas
here a monthly grid would buy nothing but the excluded variants (monthly-sum crediting,
Athene’s monthly charge deduction, daily interim values, mid-year withdrawal crediting).
The product assignment table records this product as monthly; its own technical notes
state annual, and the notes govern.
age(t) is the attained age at anniversary t, age_at_entry() + t, exactly
as the notes define it — the age that reads the lifetime-withdrawal percentage table.
Mortality over contract year t is therefore read one year lower, at age(t - 1).
The anniversary’s processing order is the notes’ own, and every quantity that changes
inside it is exposed through a timing argument rather than being buried:
index credit and fixed interest —
av_pp_at(t, "BEF_FEE")rider charge on the opening benefit base —
av_pp_at(t, "BEF_WD")benefit base: rollup, stack, step-up —
benefit_base_pp_at(t, "BEF_WD")lifetime and excess withdrawal —
lw_pp_at(t, "BEF_WD"),wd_pp(t)charges on the excess and the proportional reduction of the guarantee —
wd_reduction_rate(t),benefit_base_pp(t)guaranteed minimum value roll —
mgsv_pp(t)phase transition including the depletion test —
phase(t)decrements —
pols_if_at(t, "AFT_DECR")
pols_if(t) is the in-force count at the start of contract year t, the
library-wide convention set by Term_US_A and savings.CashValue_SE, and it is
the weight carried by every cash flow reported on the same row of result_cf(). The
technical notes define l(t) the other way round, as the probability at the end of
the year; that quantity is kept as pols_if_at(t, "AFT_DECR"), which is also
pols_if(t + 1). Neither reading is discarded and the two never share a name.
Steps 1–3 are skipped in DEPLETED and steps 1–7 in TERMINATED. t = 0 is
the issue instant for a new-issue model point and carries the premium, the acquisition
expense and the initial branch of every recursion. A model point may instead be entered
in force at anniversary entry_year() on stated balances, which is what the
worked example does and why result_cf() is indexed from entry_year() rather than
always from zero.
Undiscounted. Like every model in this library, this one projects gross liability cash flows only; reserves and discounting are a separate layer, and the notes’ own Valuation and reserve pointers section cites AG 33, AG 35 and VM-22 rather than reproducing them. The contractual discounting that lives inside a benefit formula — the ratio-form MVA — is part of the product and stays.
What is sourced and what is not. The contractual elements come from the composite
specimen: the 0% index credit floor [S1][S4][S10][R1]; the 5.25% declared cap and 2.30%
fixed rate [S2] against the 0.25% guaranteed minimum cap [S4] and 1.00% guaranteed
minimum fixed rate [S10]; the 9.1%-to-0% surrender charge schedule and the 0-to-100%
bonus vesting vector [S5]; the 7% premium bonus [S5] and the b/(1+b) clawback [S10];
the 10% free withdrawal [S1][S3][S5][S6][S9][S10]; the ratio-form MVA and its
nonforfeiture collar [S10]; the 5.00%/2.00% guaranteed simple rollup [S2]; the 150%
stacking factor [S8][S9]; the 0.95% rider charge on the benefit base, deducted after
index credits [S9]; the lifetime withdrawal percentage bands [S3]; the cause-dependent
treatment of account-value exhaustion [S1][S5][S9]; and the Model #805 construction —
87.5% of premium excluding the bonus accumulated at a nonforfeiture rate inside the
0.15%-3% corridor, whose statutory floor is 15 basis points, not 1% [R2][R3].
Everything behavioural and expense-related is a standardization: the annual grid and the
anniversary-only event date; the annual step-up (no retrieved document describes an
automatic ratchet during deferral); the 1.00% flat nonforfeiture rate inside the
corridor; the insurer-favourable reading under which the guaranteed withdrawal consumes
the free withdrawal amount; the base surrender vector 2/3/4/5/6% and the three-way shock
lapse 33%/10%/5%; the rider moneyness multiplier; the locking of the payout percentage
at first exercise; the 6.0%-of-premium acquisition expense; the $80 per contract per
year maintenance expense inflating at 2.5%; the 0% premium tax; the illustrative
mortality table; and the attained age 120 projection horizon. Two crediting parameters
belong on that list rather than on the sourced one: the notes give the index-margin and
performance-trigger forms but declare no level for either, so spread_rate = 2.00%
and trigger_rate = 4.50% are illustrative [std] levels that exercise those
branches and nothing more. The cap (5.25% [S2]) and the participation rate (80%, from
[R1]’s worked min(80% x 10%, 6%) = 6%) are the sourced ones.
Exhaustion pays differently depending on its cause. On the anniversary the account
value runs out, the withdrawal requested exceeds the balance available to meet it. In
DEPLETED the insurer funds the whole shortfall, because the guarantee survives and
that stream is the product. On the TERMINATED branch it funds none of it: the
balance is gone and the rider that would have covered the rest was destroyed by the very
withdrawal being paid, [S5] treating the contract “as well as the rider” as surrendered
at that point. Paying the request in full on both branches would honour the guarantee in
the year the excess withdrawal kills it. The cap sits on the payment only — wd_pp
still carries the amount requested, so the excess still sets depletion_cause, still
drives the proportional reduction to 1 and still takes the benefit base to zero — and
wd_unfunded_pp is the part kept out of the ledger.
This model is a mechanics demonstration, not a pricing or reserving result. Replace
the assumption tables with company data before drawing any conclusion from the output.
Where the notes are silent, both readings are shipped. The MVA collar is stated on
the gross withdrawal but justified as a surrender-value test, which the worked
example only ever exercises at a full surrender. Read literally it gives a partial
withdrawal below the nonforfeiture floor no adjustment at all; read as a test on the
contract it gives the adjustment the rate produces. mva_collar_basis selects, the two
agree on the surrender path so the worked example reproduces under either, and a test
pins the gap open rather than closing it in either direction.
Not implemented. Named here so the gaps cannot be mistaken for oversights. The
monthly-sum crediting method max(f, sum_k min(R_k, c_m)), which needs a monthly grid
the notes deliberately exclude [S4][R1]; interim values, whether Nassau’s Daily/Protected
Account Value or Nationwide’s Balanced Allocation Value, both daily marks of the embedded
option rather than interpolations [S10][S11]; the cap re-declaration rule, because the
notes state the target (set the cap so the one-year call-spread cost equals the option
budget) but give no option-pricing function, so the base projection holds the snapshot
scale level [R1][R6]; stochastic GLWB activation on the h(a) incidence table, which
cannot be applied to a single deterministic cell — activation_rate() reports the
table and the base run activates at the model point’s income_start_age instead;
generational mortality projection with Scale G2, because the 2012 IAM/IAR family and the
G2 scale may not be redistributed here [REG-R59][REG-R60]; joint-life survivorship, the
notes specifying the joint payout percentage but no second-life mortality; the income
doubler, confinement and terminal illness waivers, and annuitization, all described and
put out of scope by the notes themselves; additional premium; and check_margin(),
because the notes define no margin decomposition and the model projects no asset side.
Model points. model_point_table.csv carries nine contracts on the anchor
configuration — male 62 ANB, non-qualified, $100,000 single premium, GLWB elected at
issue, income from attained age 70. Point 1 is the worked example, entered in force
at anniversary 7 on the balances the notes state there; point 2 is the same cell issued
at t = 0; point 3 is the notes’ “Where the step-up binds” block, growth mechanism (a);
point 4 is growth mechanism (b), pure stacking with only half of index credits reaching
the account value; point 5 is joint life; point 6 carries no rider, so its shock lapse is
33%; point 7 overdraws at 105% of the maximum and so loses the guarantee at exhaustion;
point 8 takes a pre-exercise withdrawal that attracts the charge, the clawback and the
MVA; and point 9 defers income to attained age 85, past both the year the surrender charge
expires and the end of the twenty-year growth window, so its shock lapse is the 10%
rider-in-force-but-not-activated rate, its benefit base stops growing on the
contract-year-20 leg of T_g rather than on the first withdrawal, and its payout
percentage locks in the 80+ band. Between them they exercise both benefit-base growth
mechanisms, all three shock-lapse rates, both legs of the growth window, both step-up
binding cases, all four phases, both proportional-reduction denominators and both
depletion attributions, so no branch of the notes’ parameter set is dead code. A test
asserts every point projects.
Verification. tests/test_fixed_indexed_annuity_us.py asserts all sixteen rows of
the notes’ worked example table and every line of its surrender trace, to the cent and to
the eight decimals the MVA factor is displayed at; the “Where the step-up binds” variant
block on point 3; the notes’ statement that the step-up binds in contract year 1 on a
new issue with a zero index credit; the depletion arithmetic — $11,997.42 a year,
exhausted during contract year 19 at attained age 81 — and the survival of the income
stream after it; the verbatim [S9] excess-withdrawal reduction and the verbatim [S10]
clawback; the in-force and account-value roll-forwards, both through the no-argument
check_pols_roll_fwd() and check_av_roll_fwd() and through the per-anniversary
check_*_resid(t) residuals they are built on; that the pols_if column of
result_cf() is the weight carried by the cash flows on its own row; the payment cap
on the terminating exhaustion branch; and one test per pitfall the notes state as a model
mechanic. Three of the thirteen entries in the notes’ Known modeling pitfalls list are
not model mechanics and carry no test: that the behavioural assumptions must not be
reused in a CARVM valuation, that “efficient policyholder selection” is not AG 33’s
language, and that the declared parameters are stale and state-varying are all statements
about how the projection may be used rather than about what it computes, and no
assertion can reach them. A fourth, “Interim values and index costs”, is half covered —
the interim-value structures it names are not implemented at all, while the index-cost
haircut it also names is, and is asserted to come off R(t) ahead of both the cap and
the participation rate. One line of the notes does not reproduce and is
pinned both ways instead: the surrender trace’s net proceeds of 115,741.64 is the sum of
the displayed cent-rounded components, and carried at full precision — which the notes’
own rounding convention asks for — the net is 0.55 cents lower. Every component
reproduces exactly.
Example
>>> import modelx as mx
>>> model = mx.read_model("products/fixed_indexed_annuity/FIA_US_S")
>>> model.Projection[1].result_cf()