Annuity Product Pricing Suite Price annuity products (RILA, indexed annuity, VA guarantees, spread products) for new-business profitability and reserve seeding.
Common pricing harness across annuity product family. Per §12.3 "intended use" test, these share intended use; differences in product mechanics handled as variants. See Q-03 for collapse vs split debate.
Inputs, processing, outputs
- data sources
- DS-001 · DS-008
- assumptions
- A-001, A-002, A-010, A-060, A-061, A-070
- engines
-
insmodel.L4.spread_liability
insmodel.L4.rila_model
insmodel.L4.indexed_annuity
insmodel.L4.va_guarantee
insmodel.L4.benefit_engine
insmodel.L6.projection_orchestrator
- contracts
- pricing_results_v1
- upstream
- M-120
- dimensions
- D6 · D7
Methodology
M-070 is a pricing suite, not a single engine: a common harness across the annuity product family that composes four product models living under firmmodel/products/. Each model shares the same BaseProductModel contract (parameter schema, calculate() cash-flow projection, compute_profitability(), get_termsheet()) and the same composed calculation engines (Investment, Lapse, Expense, Benefits, Reserve, plus Stochastic/Mortality where the product needs them). The four members differ in their crediting and guarantee mechanics, which is exactly where annuity pricing risk lives.
Representative engine — IndexedAnnuityModel (P-003), RILA configuration. The snapshot below exercises this engine because it runs cleanly and deterministically and exhibits the suite's most distinctive mechanic: the embedded-derivative option budget. The engine projects monthly cohort cash flows (calculate() in firmmodel/products/indexed_annuity.py). Each month it accrues investment income on the general-account backing assets, accrues a monthly share of the embedded-option cost, deducts expenses and rider charges, applies lapse with a surrender-charge deterrent, and rolls the account value forward as AV(t+1) = av·(1 + credited_return) − fees − withdrawals (floored at zero). The credited return is the heart of pricing:
- Upside is capped:
credited = min(index_return · participation, cap). - Downside under RILA is buffered: a loss within the buffer credits 0%; a loss beyond the buffer passes through as
−(loss − buffer).
The option budget is the spread the general account can afford to spend on hedging the crediting promise: option_budget = portfolio_yield − expense_rate − profit_margin_target − option_budget_spread (_calculate_option_budget()), floored at zero — in the canonical run, 5% − 0.30% − 0.75% − 0.25% = 3.70% annual. The cost of the promise is computed by _price_index_option() via Black-Scholes option replication: the RILA crediting feature is decomposed into a long call spread (ATM call minus a call struck at the cap, scaled by participation — the capped upside) plus, for the buffer, a short put spread (ATM put minus a put struck at 1 − buffer — the cost of absorbing the first N% of loss). The FIA variant prices the call spread only (0% floor). The embedded-derivative fair value carried on the balance sheet is av · option_cost_pct · term_remaining_fraction, so it amortises to zero across the crediting term and resets each cycle. compute_profitability() discounts monthly net profit to a PVFP, profit margin, simple IRR, and return-on-capital.
The other three suite members (summarised):
SpreadLiabilityModel(MYGA / fixed annuity / funding agreement / GIC,insmodel.L4.spread_liability) — pure ALM. The insurer earnsportfolio_yield, credits a contractualcredited_rate, and books the difference as net spread. No embedded derivative, no separate account.RILAModel(P-002,insmodel.L4.rila_model) — the earlier indexed-annuity model. Same call-spread/put-spread core as P-003 but without the unified RILA/FIA handling and option-budget abstraction; P-003 is the comprehensive successor.VAGuaranteeModel(variable annuity,insmodel.L4.va_guarantee) — separate-account product with embedded guarantee riders (GMDB/GMWB/GMIB/GMAB). Each guarantee is priced as a put option on the separate-account value relative to a guarantee/benefit base, funded by M&E and rider fees. Dynamic lapse is the key sensitivity: in-the-money guarantees suppress lapse, raising guarantee cost.
Key Assumptions and Their Justification
The pricing suite is parameterised per product; the table lists the load-bearing assumptions for the representative RILA run.
| ID / param | Name | Canonical value | Derivation | Justification |
|---|---|---|---|---|
| A-010 | Base lapse curve (indexed) | 2% Y1 → 20% Y7+, surrender-charge-adjusted | data_calibrated | Duration-graded lapse consistent with VM-20 §6 guardrails; surrender charge suppresses early lapse. |
| A-060 / A-061 | Crediting strategy (cap / buffer / participation) | cap 15% · buffer 10% · participation 100% · ANNUAL_POINT_TO_POINT | product_design | Contractual crediting terms set at issue; they define the embedded option the insurer must hedge. |
| A-070 | Per-policy / spread expense | 30 bps of AV (indexed) | data_calibrated | Industry FY2024 expense-study range; deducted monthly from AV. |
| (param) | Option-pricing market inputs | implied vol 18% · r 4% · q 1.5% | published_source / market | Black-Scholes inputs for call/put-spread replication; a flat-vol, single-rate simplification (see Limitations). |
| (param) | Option budget components | portfolio yield 5% · profit-margin target 0.75% · hedge-friction spread 0.25% | management_target | Defines affordable hedging spread; profit-margin target is a pricing lever. |
| (param) | Policyholder behaviour (deterministic index path) | expected index return 8% | assumption | Standalone calculate() uses a deterministic index return; full distributional pricing needs the stochastic adapter. |
| A-001 / A-002 | Mortality (VA guarantees only) | age-dependent qx via MortalityEngine | published_source | GMDB/GMWB costing needs mortality; spread/indexed products use de-minimis mortality only. |
Operational notes. The crediting cap, buffer, floor and participation are product-design assumptions (contractual, set at issue) rather than calibrated experience assumptions — pricing risk here is the risk that the option budget is mis-set relative to the true hedge cost, not assumption-estimation error. The option budget is deliberately conservative. Lapse is the only behavioural assumption that materially feeds back into cost for indexed products; for VA guarantees, dynamic (moneyness-dependent) lapse is first-order.
Output Snapshot
Deterministic single-cohort run of the suite's representative engine, IndexedAnnuityModel (P-003) in its RILA configuration — reproducible, requires no live firm data (python scripts/model_snapshots.py M-070 in InsModel; mechanics asserted by tests/test_indexed_annuity_e2e.py, 13 tests passing). The provider is a MagicMock.
Input: account value $1,000,000 · 15% cap · 10% buffer · 100% participation · 6-year term · ANNUAL_POINT_TO_POINT crediting · implied vol 18% · r 4% · q 1.5% · index return +8%. Row shown is month 12 (the first annual crediting event).
| output | value | meaning |
|---|---|---|
| credited_return | 0.08 | +8% index return, below the 15% cap → credited in full |
| index_credit | 78,902.03 | dollar credit to the account at the annual reset |
| account_value_post_credit | 1,063,939.13 | AV after credit, fees, and lapse decrements |
| option_budget_annual | 0.04 | 3.70% affordable hedge spread (yield − expense − margin − friction) |
| embedded_option_cost_pct | 0.09 | option cost as 8.90% of notional (call spread + buffer put spread) |
| call_spread_value_per100 | 5.70 | capped-upside leg of the replicating portfolio |
| put_spread_value_per100 | 3.20 | buffer (downside) leg of the replicating portfolio |
| embedded_derivative_fv | 73,135.90 | balance-sheet fair value of the crediting feature, mid-term |
| reserve_host_plus_embedded | 1,137,075.03 | host contract value + embedded-derivative FV |
| pvfp_120mo_at8pct | 239,949.94 | present value of future profit over a 10-yr pricing horizon |
| profit_margin | 0.24 | PVFP as a fraction of initial account value |
The embedded option cost (8.90% of notional over the 6-year term) decomposes into a 5.70 call-spread leg (the capped upside) and a 3.20 put-spread leg (the 10% buffer) — directly visible in the replicating portfolio. Note the option budget shown is a rate (3.70% p.a.), while option_cost_pct is a 6-year term cost; the engine amortises the latter monthly when comparing against budget. The PVFP and profit-margin figures are pricing diagnostics from compute_profitability(), not statutory reserves.
Captured 2026-06-04 · deterministic, no live data.
Limitations and Known Gaps
- The suite is a harness, not a single validated engine. M-070 documents four distinct product models under one card per the §12.3 "intended use" collapse (Q-03 remains an open collapse-vs-split debate). The snapshot exercises only
IndexedAnnuityModel(RILA); the spread and VA-guarantee engines are summarised but not independently snapshotted here. - Deterministic index path in the standalone pricing run.
calculate()projects on a single expected index return (8%); the capped/buffered payoff is path-dependent and asymmetric, so a single point estimate understates the true distributional cost. Full stochastic pricing requiresStochasticScenarioAdapter. - Black-Scholes option pricing uses flat vol and a single rate.
_price_index_option()assumes constant implied volatility and a single risk-free rate with no skew/term-structure. Real RILA/FIA hedging trades against a vol surface; the replication is first-order. - Embedded-derivative FV is a simplified amortisation, not a re-priced mark. The carried FV is a linear amortisation of the issue-date option cost across the term, not a fresh Black-Scholes re-mark each period to current moneyness.
- Profitability metrics are simplified. IRR is a
total_profit / AV / yearsapproximation rather than a true cash-flow IRR solve; return-on-capital uses a flat 8% capital charge for all indexed products. RILAModel(P-002) is partly superseded. It overlapsIndexedAnnuityModel(P-003); itsIndexedProductEnginedependency is noted as stubbed in source. Carrying both is a known redundancy.- No live firm-data path (BV-032). All figures are from documented canonical inputs and MagicMock providers; reserve/MRB figures shown are pricing-harness outputs, not statutory or LDTI-reported values.
Tracked for ratification (not applied in this documentation pass). The following are output-changing / modeling-code items left for ratification, noted here for transparency: produce the Tier-1 validation-evidence pack (backtesting per MRM §10.2 + a sensitivity suite across the material assumptions); independently snapshot/validate the two non-snapshotted members (
SpreadLiabilityModel,VAGuaranteeModel), not just the representative RILA run; complete 2L independent effective challenge and close RAT-070-v1.0.0 per MRM §10.6 (peer_review.statuspending → signed); and resolve the embedded-derivative FV methodology (linear amortisation of issue-date option cost / flat-vol single-rate Black-Scholes vs a fresh re-mark to current moneyness). No model outputs, back-test numbers, or validation results were fabricated or changed in this pass.
Validation Packet
| evidence | status | reference |
|---|---|---|
| End-to-end test suite | present | tests/test_indexed_annuity_e2e.py — 13 tests (crediting cap/buffer, option budget, embedded-derivative FV, profitability, termsheet, stochastic adapter). |
| VA guarantee tests | present | tests/test_va_guarantee_model.py. |
| Deterministic snapshot | present | scripts/model_snapshots.py M-070 (representative RILA engine). |
| Per-archetype snapshots | partial | Only the indexed-annuity engine is snapshotted; spread and VA-guarantee archetypes are not. |
| Validation evidence pack (MRM §10.5 item 5) | present | insightalm/modelling/validation_evidence/M-070/v1.0.0/README.md (2026-06-06) — maps each §10.5 sub-item to the 98 passing InsModel tests + the deterministic snapshot + the PRU disclosed-block bounds cross-check. Closes COND-001 on RAT-070-v1.0.0; honest gaps carried below. |
| Change log (MRM §10.5 item 7) | present | insightalm/modelling/model_cards/M-070-changes.md (2026-06-06). Closes COND-002 on RAT-070-v1.0.0. |
| Independent challenge (2L) | pending | 2L re-review of COND-001/COND-002 + supersession to RAT-070-v1.0.1 is 2L's job; current peer_review.status = pending (registry); next Tier-1 revalidation due 2026-08-23. Q-03 collapse-vs-split still open. |
| Backtest (model-vs-reality) + quantitative sensitivity-elasticity suite | pending | The e2e suite asserts engine mechanics + mechanical crediting-term/lapse sensitivities; a Tier-1 model-vs-reality backtest (MRM §10.2, BV-032) and a calibrated elasticity table over the continuous market inputs (implied vol, portfolio yield, profit-margin target) are not yet produced — carried as honest gaps in the validation pack and Limitations, not closed. |
References
Engine source:
- ecosystem/InsModel/Models/firmmodel/products/indexed_annuity.py — IndexedAnnuityModel (P-003), representative engine.
- firmmodel/products/spread_model.py — SpreadLiabilityModel; firmmodel/products/rila_model.py — RILAModel (P-002); firmmodel/products/va_guarantee_model.py — VAGuaranteeModel; firmmodel/products/base_product.py — shared contract; firmmodel/products/stochastic_adapter.py.
Tests: tests/test_indexed_annuity_e2e.py (13), tests/test_va_guarantee_model.py. Snapshot: scripts/model_snapshots.py M-070.
Standards: - ASOP No. 56 — Modeling. - FASB ASU 2018-12 (LDTI) — Market Risk Benefit (MRB) measurement for VA guarantee embedded derivatives. - ASOP No. 22 — cash-flow-testing context for annuity reserve seeding.
Internal: M-001 for engine-card depth exemplar; BV-032 (firm-data divergence); Q-03 (collapse-vs-split for the annuity family).
Change Log
Card change history. Code-side change history lives in git log of the component files.
- 2026-05-08 — stub created from registry data per Decision 023 Phase 5 / B-07.
- 2026-06-04 — Tier-1 hand-authoring of Methodology, Key Assumptions and Their Justification, Output Snapshot (deterministic RILA run via
scripts/model_snapshots.py M-070), Limitations, Validation Packet, and References fromindexed_annuity.py+IndexedAnnuityModellegacy_metadata; stub marker advanced to ``. - 2026-06-06 (RAT-070-v1.0.0 remediation, 1L) — assembled the Tier-1 validation-evidence pack at
validation_evidence/M-070/v1.0.0/README.md(98 passing InsModel tests + deterministic snapshot reproducing this card's Output Snapshot + PRU disclosed-block bounds cross-check; honest gaps carried) closing COND-001; authored the per-version change log atM-070-changes.mdclosing COND-002; reconciled registrydocumentation_packflags (validation_evidence/change_log→ present); refreshed the Validation Packet rows to point at the pack/log. No model outputs, back-test numbers, or validation results were fabricated (Decision 053 §4). - 2026-06-06 — documentation-pack completion pass: added a dedicated Standards Coverage section (ASOP 56 / ASOP 22 / ASU 2018-12 LDTI, each tied to applicability) with a frontmatter-vs-References reconciliation note and an
asc_944-tag flag raised to engine-governance (no reg tag silently added); expanded Dependencies with the MRM §10.5(8) shared-component aggregate-risk view (benefit_engineshared with M-001/M-002/M-071;projection_orchestratorshared with M-001/M-002/M-003/M-051/M-100, sourced fromengine_registry.yamlmodel_membership); corrected the Validation Packet "Independent challenge (2L)" row from "per registry" to an honest pending state (RAT-070-v1.0.0 not closed,peer_review.status = pending, revalidation due 2026-08-23, validation_evidence not produced) and added a backtest/sensitivity-evidence "pending" row; noted gated/ratification items under Limitations. No model outputs, back-test numbers, or validation results were fabricated or changed.
Open findings (1)
Independent 2nd-line review (INV-2026-06) — implemented capability vs registered scope. Each carries a recommended fix and is tracked in insightalm-mrm until closed.
Validation evidence + change logs missing across most of the inventory
Only M-001/M-020/M-050 carried full documentation packs before this pass. Most models record validation_evidence: missing and change_log: missing with peer_review: pending. Gold tests freeze behaviour but many assert only structural invariants (e.g. reserve>0), not correctness against external truth. The flagship T0-vs-10-K match is circular (BV-032).
Recommendation: For each Tier-1 model: produce a validation-evidence pack (back-test vs disclosed results once BV-032 re-calibration lands, sensitivity suite, challenger comparison), a change log, and a 2L ratification. Sequence behind BV-032 (firm-data) for anything needing 10-K reconciliation.
Per-tier expectations
Per MRM Framework §10.2 + §10.3, this model's regulatory_frameworks tag list activates the following overlays:
| component | tier-1 expectation | status |
|---|---|---|
| Registry entry | required | present |
| Model card (§10.5 doc pack) | required | present |
| Validation evidence | required | present |
| Change log | required | present |
| Independent effective challenge (2L) | required | pending |
Conditionally approved — RAT-070-v1.0.2
2L issued RAT-070-v1.0.2 with the following conditions outstanding. Bridge audit Rule 2 treats this as a WARN; the model can remain in monitoring with the conditions on record.
| id | deadline | condition |
|---|---|---|
| COND-003 | 2026-08-23 | Tier-1 validation-evidence ADEQUACY gap — UNCHANGED from RAT-070-v1.0.1, independently re-verified as STILL OPEN at this re-review (2026-06-08). The assembled pack and the model card honestly carry two open Tier-1 elements that MRM §10.5 item 5 calls for and charter §4.2 requires: (1) NO model-vs- reality back-test against a realized outcome (BV-032) — the PRU TestPRUCalibration cross-check is a bounds/sanity reconciliation (test_fee_level_in_range asserts rider 100-150 bps; test_equity_allocation _typical asserts 0.50-0.90; test_pru_mrb_scaling asserts embedded- derivative FV > $100M and < 10% of a $193.4B SA block), NOT a reconciliation against PRU's disclosed ~$4.3B MRB within a documented tolerance; and (2) NO quantitative sensitivity-elasticity suite tabulating dPVFP / d(option-cost) per unit shock to the three continuous market inputs (implied vol, portfolio yield, profit-margin target) — only the MECHANICAL crediting-term responses (cap clamp, buffer pass-through, ITM lapse suppression) are tested. 1L should build the Tier-1 back-test (a realized-outcome reconciliation within a documented tolerance) and the calibrated elasticity table (in the style of M-001/M-002 sensitivity atlases) before this RAT can be superseded by RAT-070-v1.0.3 with decision: approved. |