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:

Data

Reads the seven input CSVs and holds their filename References. It takes no parameters, so each file is read once per model.

Projection

The by-contract projection, parameterized by point_id: Projection[1] is an ItemSpace projecting model point 1. It reaches the input tables through its data Reference, which resolves to the single Data Space.

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. Monthly steps, as the _S suffix and the product assignment table both say. t is the 0-based month index: duration(t) = t // 12 is the completed contract years, policy_year(t) = duration(t) + 1 is the contractual label, and the frame is t = entry_mth() proj_len() - 1 with proj_len() = 12 * policy_term().

Every mechanic of the contract is still annual. Annual point-to-point crediting [S2][S4][S10], the rider charge at the end of each contract year [S9], the benefit base update [S9] and the lifetime withdrawal are all annual, and the notes make the anniversary the single event date — so each of them happens once a year, in the anniversary month is_anniv(t), and the contract moves no cash between anniversaries. What the finer grid resolves is everything that is not a contractual event: mortality and surrender fall in the month they happen, 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. The excluded variants stay excluded — monthly-sum crediting, one carrier’s monthly charge deduction, daily interim values, mid-year withdrawal crediting are all still absent, and the indexed account is deliberately flat between anniversaries.

Because the monthly decrement rates compound to the annual ones and every contractual step is unmoved, every state value at an anniversary equals the annual-step model’s: account value, benefit base, rollup base, lifetime withdrawal, Model #805 floor, phase and pols_if at t = 12k. The cash differs, and that is the point — result_cf_annual() sums the monthly frame into contract years and agrees exactly on pols_if, premiums, commissions and premium_taxes, but not on the withdrawals, the claims or the expenses.

age(t) = age_at_entry() + duration(t) is the attained age opening the contract year of month t, so that year’s mortality reads age(t) itself; mort_rate_mth(t) converts it for the month. The transactions at the closing anniversary are one year older: exercise_age(t) is the age that reads the lifetime-withdrawal percentage table and clears the minimum exercise age.

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:

  1. index credit and fixed interest — av_pp_at(t, "BEF_FEE")

  2. rider charge on the opening benefit base — av_pp_at(t, "BEF_WD")

  3. benefit base: rollup, stack, step-up — benefit_base_pp_at(t, "BEF_WD")

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

  5. charges on the excess and the proportional reduction of the guarantee — wd_reduction_rate(t), benefit_base_pp(t)

  6. guaranteed minimum value roll — mgsv_pp(t)

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

  8. decrements — pols_if_at(t, "AFT_DECR")

pols_if(t) is the in-force count at the start of month t, the library-wide convention set by Term_US_S and savings.CashValue_SE, and it is the weight carried by every cash flow reported on the same row of result_cf(). At t = 12k it is the technical notes’ own l(k), the probability in force at the end of contract year k, and the equality is exact. At an anniversary month it is the count that reaches the anniversary, which is the weight that month’s withdrawal and charges carry — where the annual grid could only weight them by the count that entered the year.

Steps 1–3 are skipped in DEPLETED and steps 1–7 in TERMINATED; step 8 is the one that runs every month. There is no issue-instant row: the premium, the premium bonus and the acquisition expense are beginning-of-month flows of month 0 on a new-issue model point, alongside that month’s own activity. A model point may instead be entered in force after entry_year() completed contract years on stated balances — which is what the worked example does, and why result_cf() is indexed from entry_mth() = 12 * 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 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 index path — a grid of months is not the same thing, and rate_scenario.csv states index levels at anniversaries only [S4][R1]; interim values in either documented form, 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 and the only non-zero fixed allocation, so it is the point that exercises the monthly fixed-account accrual; 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-month check_*_resid(t) residuals they are built on; that every anniversary value of the account value, the benefit base, the Model #805 floor, the lifetime withdrawal, the phase and pols_if reproduces the annual-step model exactly, and that no contractual cash moves between anniversaries; 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()            # by month
>>> model.Projection[1].result_cf_annual()     # summed into contract years