The RILA_US_S Model#
Reference liability cash flow model for U.S. registered index-linked annuities (RILA).
RILA_US_S is the executable counterpart of
products/registered_index_linked_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 buffered index-linked deferred annuity: one purchase
payment allocated to one index-linked option, a 10% buffer, Cap / Step / Edge crediting
over a 1, 3 or 6 year point-to-point term, a 7-7-6-5-4-3-0 withdrawal charge above a 10%
free amount, a return-of-premium GMDB, no explicit asset-based charge, and — the
mechanic that defines the product — an Actuarial Guideline LIV Interim Value at which
every mid-term transaction settles.
That interim value is why this model carries an option pricer. AG 54 makes the value of a
hypothetical replicating portfolio of European options plus a fixed income proxy the
contractual value for every mid-term withdrawal, surrender, death benefit, annuitization
and transfer [R2], and the source prospectuses implement exactly that with Black-Scholes
[S2][S6]. No other product in this library has a contractual value that cannot be computed
without a derivatives pricer. The three components the notes separate — the crediting
engine, the interim-value engine and the market-data provider — are separate here too:
credit_rate_term() credits,
iv_factor() prices, and
Data supplies the market state.
This model is built on the deferred annuity base chassis,
MYGA_US_S, and shares its cells names for every concept the two have
in common — av_pp, av_pp_at, free_wd_allow, surr_charge_rate,
surr_value_pp, pols_if_at, claim_pp, check_av_roll_fwd. Where the RILA
notes restate a recursion with product-specific parameters, this model follows the RILA
notes, not the chassis: there is no market value adjustment on the contract, no Model
#805 minimum guaranteed surrender value (AG 54 displaces Model #805 outright [R2]
[REG-R42][REG-R44]), and the account value is a derivative price rather than a book value.
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. Monthly steps. t counts policy months, and following the
notes t denotes month ends, with t = 0 the Issue Date. Complete contract years
are duration(t) = t // 12 — the notes’ cy(t) = floor(t/12) — so an anniversary
month t = 12, 24, ... already counts as a completed year, and policy_year(t) =
duration(t) + 1. Note the contrast with Term_US_A, where t counts years,
and the smaller contrast with MYGA_US_S, whose beginning-of-month
transaction convention makes its duration(t) ceil(t/12) - 1: on a month-end
convention the anniversary belongs to the year that just closed. That month-end reading is
right for what is read at the instant t — the withdrawal charge a transaction
settling there bears, the free-withdrawal base snapshotted there, and the surrender
behaviour keyed to that charge — but wrong for a rate applied across month t, so the
attained age behind q_m(t) and the expense inflation step are keyed on the interval
reading duration_bom(t) = ceil(t/12) - 1 instead: all twelve months of contract year 1,
month 12 included, are charged at q_x and at the issue expense level. The two readings
differ only in anniversary months. The contractual interim
value is a daily quantity [S2][S4][S6]; the model evaluates it at each month end
[std], which resolves every contractual boundary because terms are whole years, the
withdrawal-charge schedule runs by complete contract years and the free-withdrawal limit
resets annually [S1][S2].
proj_len() is 12 * policy_term() months, and policy_term() is the Maturity
Date rule: the later of the anniversary after the oldest owner’s age 90 and ten years
[S2] — 360 months on the anchor cell. The survivors there are force-annuitized through
pols_maturity().
The month’s processing order follows the notes’ step list exactly. Market state (the
index level, the Market Value Rate, the implied volatility and the dividend yield are
refreshed from the scenario); term-end crediting (at a Term End Date the completed
term’s index performance R is turned into a crediting rate g and the Investment
Amount rolls to IA(1 + g)); renewal and transfer (the 80 / 15 / 5 roll split, and
the option is re-struck at the prevailing index level, the new declared cap and a fresh
option budget beta); interim values (fixed income proxy, derivative asset proxy,
trading costs); accounts (the Fixed and Holding Accounts accrue, the Account Value is
set, and on a Contract Anniversary the free-withdrawal base is snapshotted); contract
holder transactions (the withdrawal is allocated, charged, and reduces the Investment
Amount proportionally and the return-of-premium base proportionally); and finally
decrements at the end of the month — death at q_m(t), then surrender at w_m(t)
on survivors [std order], plus the discrete term-end surrender fraction phi if
t is a Term End Date.
t = 0 is the Issue Date. The single premium and the acquisition expense fall there,
as they do in the notes’ cash flow ledger, and inv_amt_pp(0), rop_pp(0) and
pols_if(0) are the initial branches of the recursions, so result_cf() starts at
t = 0 rather than t = 1.
In-force counts follow the library-wide convention: pols_if(t) is the number in force
at the start of month t and is the weight applied to that same month’s cash flows,
so pols_if(1) = pols_if_init() as in Term_US_A and the pols_if column of
result_cf() reconciles against the row it sits on rather than the next one. The notes’
own end-of-month l(t) is not lost: it is pols_if_at(t, "AFT_DECR"), the last
point of the decrement chain. Surrender rates follow the mort_rate / mort_rate_mth
pair - lapse_rate(t) is the notes’ annual w_annual(y,t) and lapse_rate_mth(t)
its monthly w_m(t), which is what the decrement chain reads.
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’ Valuation
and reserve pointers section cites AG 54, VM-21 and C-3 Phase II rather than reproducing
them. The discounting that lives inside the benefit formula — the fixed income asset
proxy’s ((1 + r_0) / (1 + r(t)))^tau market value adjustment factor, and the
Black-Scholes discount factors — is contractual and stays.
What is sourced and what is not. Contractual: the 10% buffer, the 1/3/6 year term menu, the guaranteed minimum Cap / Step / Edge rates of 2% / 6% / 8% by term, the 1% minimum on the Fixed and Holding Accounts, the 7-7-6-5-4-3-0 withdrawal charge and its non-gross-up, the zero free amount in contract year 1 and 10% of the prior anniversary’s Account Value thereafter, the return-of-premium death benefit for issue ages 80 and under, the Maturity Date rule, the Transfer Period during which the interim value equals the Investment Amount, and the interim value algebra itself with its straight-line option-budget amortization and CMT discount [S1][S2][S4][S5][S6][R2].
Everything else is a standardization. The declared Cap / Step / Edge rates of 100% / 8% /
6% and the 3.00% Fixed Account rate are a snapshot — no current rate sheet was
retrievable, both insurer rate pages returning HTTP 403. The market data that the
contractual formula consumes is a flat scalar surface: a 4.00% risk-free rate, a 2.00%
dividend yield and a 20.00% implied volatility. The 0.10% trading cost factor matches only
the order of magnitude implied by [R6], and the two free parameters of the alternative
interim-value family (b) are [std] on the same footing: the investment-grade discount
spread iv_credit_spread = 1.00%, which no source quantifies ([S4] says only that the
rate is above swap rates), and the Cap Calculation Factor’s expense rate
iv_expense_rate = 0.10%, which [S4] gives only as a dollar illustration. All
behaviour is [std] — the base surrender
shape, the 3.0 charge-expiry shock multiplier, the interim-value moneyness suppression
M_iv, the term-end concentration phi, the 80 / 15 / 5 roll split, the 2% partial
withdrawal rule, the 100% mortality A/E, the $60 per contract per year maintenance
expense and the 6% + $200 acquisition expense — and the shipped mortality table is an
illustrative curve, not the prescribed 2012 IAM Basic table with Scale G2.
This model is a mechanics demonstration, not a pricing or reserving result. Replace
the assumption tables and the market data with company data before drawing any conclusion
from the output.
Not implemented. Named here so the gaps cannot be mistaken for oversights.
Performance Lock: the notes give the election rule and the dollar-for-dollar withdrawal
consequence but not the rate at which a locked bucket accrues to term end, so the module
is incomplete and is left out rather than guessed. Tiered participation rate: the notes
give its replicating portfolio but no crediting formula g. Dual-direction and
absolute-return designs, Annual Lock segments, rainbow segments and Secure Lock+: listed
as out of scope by the product spec, with no formula. RMD-driven withdrawals: named as a
behavioural input, with no amount formula. Multi-option contracts: the notes define a
model point as one contract holding one option and describe the multi-option case as a
vector sharing one contract-level decrement and guarantee base; this model carries the
single-option case, and the pro-rata-to-interim-value allocation rule is implemented over
the option, the Fixed Account and the Holding Account rather than over several options.
The payout phase: the Maturity Date benefit is emitted as an outgo at the Account Value;
converting it to a payment stream is the immediate-annuity chassis. Stochastic
scenarios: the index path, the CMT curve, the volatility and the dividend yield are read
from a deterministic scenario table, and the Academy’s regression grid [R6] would be run
by supplying more scenarios, not by changing formulas. A volatility surface: the notes
call the flat-surface approximation the single largest simplification in the model, and it
is one this implementation makes. check_margin(): on this chassis the cap is the fee —
no charge is deducted from index-linked value — so there is no charge-versus-cost
decomposition to check; check_av_roll_fwd()
and check_pols_roll_fwd() are implemented
and both close to floating point. Each check_* takes no argument and returns a
bool over every projected month, as CashValue_SE does, with the signed per-month
residual available as check_*_resid(t) for a debugging session.
Model points. model_point_table.csv carries fifteen contracts. Points 1 and 2 are
the worked example’s anchor cell — male 60, $100,000 single premium, one 6-year option,
10% buffer, Cap crediting at a declared 100% cap — run on the notes’ Scenario A and
Scenario B respectively, point 2 carrying the illustrative $8,000 withdrawal at the term
midpoint. Point 3 is the same contract with the notes’ base behavioural withdrawal rule
switched on, which is the second reading of a place where the notes contradict themselves
(see the README). Points 4 to 14 exercise every branch the notes specify: Step, Edge and
Floor crediting; interim value families (b) and (c) and the pre-AG 54 time-prorated
engine; the updated-time-to-expiry amortization convention; a participation rate above
100% on a 20% buffer; an uncapped Cap option; the NGE cap-solve; a withdrawal above the
free amount, so the withdrawal charge and its non-gross-up are live; and, at issue age 81,
the death benefit band in which the return-of-premium guarantee does not apply. Point 15
is the notes’ second labelled verification: it runs the pre-AG 54 engine on a scenario
whose index level makes the accrued crediting rate exactly -20% at month 60, which puts an
Account Value of exactly $80,000 at the start of contract year 6 and so reproduces the
[S2] withdrawal-charge example — $8,000 free, $72,000 chargeable, a 3% charge of $2,160
and a $77,840 cash surrender value — end to end rather than by re-deriving it. A test
asserts every point projects to completion.
Verification. tests/test_registered_index_linked_annuity_us.py asserts every row
and every column of the notes’ worked example table — all six rows and all thirteen
columns, money to the cent — together with the trace beneath it: the option budget
beta = 10.0632%, the fixed leg opening at $89,936.81, its 1.7834% equivalent accretion
yield and the 2.22% implied spread, the $2,687.62 cost of the 100 bp rate rise, the
counterfactual interim values without the rate move, the $1,433.62 excess of notional lost
over cash withdrawn, and the term-end identity V = IA(1 + g) for every crediting type.
The [S2] withdrawal-charge example is asserted on model point 15 — $80,000 of Account
Value at the start of contract year 6 giving a $2,160 charge and a $77,840 cash surrender
value — and the pre-AG 54 engine reproduces the $52,500 of the [S1] worked example. There
is one test per entry in the notes’ “Known modeling pitfalls” list, and three more that
hold the documentation to the model: the two readings of the contract year, the timing
literals the Projection docstring prints, and the [std] marks on the invented
interim-value parameters.
Example
>>> import modelx as mx
>>> model = mx.read_model(
... "products/registered_index_linked_annuity/RILA_US_S")
>>> model.Projection[1].result_cf()