Pension Risk Transfer (PRT) Pricing Price pension-risk-transfer transactions for direct-pay annuity quotes.
Bid pricing for PRT deals. Different intended use from M-070/M-071 (transaction-pricing vs new-business retail pricing) and different regulatory profile (ERISA-related considerations).
Inputs, processing, outputs
Methodology
M-072 prices a pension-risk-transfer (PRT) group annuity buyout: the insurer receives a lump-sum premium and assumes a defined-benefit plan's obligation to pay scheduled pension benefits to retirees for life. The engine is insmodel.L4.prt_model (PRTModel, internal model_id P-005, ProductArchetypeType.PRT). It is a liability-driven, deterministic monthly cash-flow projection — no lapse, no surrender, no policyholder optionality (the contract is irrevocable). Cash flows are driven entirely by annuitant mortality, longevity improvement, and investment income on the backing general-account reserve.
The pricing mechanics, as implemented in firmmodel/products/prt_model.py:
-
Liability cash-flow projection (
calculate). For each monthly step the engine computes, on the beginning-of-period reserve: - Pension payment =annuitants_remaining × current_monthly_benefit. With a COLA rate,current_benefitsteps up at each year boundary; the defaultcola_rateis 0. - Investment income =reserve × monthly_yield, wheremonthly_yield = (1 + portfolio_yield)^(1/12) − 1. - Mortality decrement =annuitants × monthly_qx, withmonthly_qx = 1 − (1 − annual_qx)^(1/12). - Mortality release (profit from deaths) =deaths × (reserve / annuitants)— the per-annuitant reserve freed when an annuitant dies. - Expense =reserve × (expense_bps/10000/12) + annuitants × per_annuitant_expense/12. - Net income =investment_income − pension_payment + mortality_release − expense. - Reserve roll-forward =reserve + investment_income − pension_payment − expense, floored at 0;annuitantsdecremented bydeaths. The projection stops early ifannuitants < 1orreserve <= 0. -
Mortality basis (
_get_mortality_rate). Base annual qx at age 70 is set by gender (male 0.025, female 0.018), graded linearly above age 70 bymortality_age_slope(0.0008/yr), gender-blended byfemale_mix, then improved by a constant annual factor(1 − mortality_improvement_rate)^year, clipped to[0.0001, 1.0]. For the canonical 65% female / 35% male blend at age 70, year 0, this yields annual qx = 0.02045. -
Pricing / profitability (
compute_profitability). The engine runscalculateon a pricing cohort seeded frominitial_reserve(the premium) andinitial_annuitant_count, then discounts the net-income stream at a monthly-equivalent of the annualdiscount_rate: - PVFP =Σ net_income_m × (1 + monthly_discount)^(−m); profit_margin =pvfp / initial_reserve. - irr ≈total_net_income / initial_reserve / years(a simple annualized approximation, not a root-found IRR). - return_on_capital =pvfp / (initial_reserve × 0.04)(a flat 4% capital charge). - breakeven_year = first year cumulative net income turns positive (Noneif never). - longevity_sensitivity =pvfp_stressed − pvfp, where the stressed run bumpsmortality_improvement_rateby +10% (more improvement → annuitants live longer → lower profit).
There is no explicit "buyout premium" solve: the premium is supplied as initial_reserve, and the model evaluates whether it is adequate by projecting whether it produces positive PVFP. Pricing adequacy is read off the sign of PVFP / profit_margin rather than computed as a closed-form annuity present value. The canonical default block supplies initial_reserve = $3.3B (the PV of the default 10,000 × $2,500/mo = $300M/yr benefit stream, ≈11x annual benefit at age 70 / 5% yield — prt_model.py line 109, set by the INV-025 fix on 2026-06-04), so the worked default funds the cohort over the full projection horizon rather than depleting; the premium remains an input, not a fair-price output.
Key Assumptions and Their Justification
| Parameter | Canonical value | Derivation | Justification |
|---|---|---|---|
| Annuitant mortality basis (A-001) | base qx male 0.025 / female 0.018 at age 70 | published_source | Stands in for the SOA 2012 IAM annuitant table; annuitant (not insured-life) mortality is the correct basis for group annuities. |
| Mortality age slope | 0.0008 per year above age 70 | calibrated | Linear approximation of the qx age-gradient for the retiree band. |
| Mortality improvement (A-002 analogue) | 1.0% p.a. constant reduction | published_source | Stand-in for SOA Scale MP-2021. Applied as a scalar (1−r)^year — the dominant longevity-risk lever, stress-tested at +10%. |
Gender mix (female_mix) |
0.65 (65% female) | data_calibrated | Typical DB-pension retiree demographic; higher female share lengthens duration. |
| Portfolio yield | 5.0% | market_data | General-account backing-asset yield; single blended yield, not a duration-matched asset curve. |
| Expense loading | 15 bps of reserve + $40/annuitant/yr (A-070) | data_calibrated | Reserve-proportional admin cost plus per-life servicing cost. |
| COLA rate | 0.0% (configurable) | contractual | Base PRT block assumes level (non-escalating) benefits. |
| Pricing discount rate | 6.0% | parameter | Risk-adjusted rate for PVFP; distinct from the 5% asset yield, creating the priced spread. |
| Capital charge | 4% of premium | regulatory proxy | Flat PRT capital basis for return-on-capital; a simplification of a full economic-capital model. |
The profit/risk margin is implicit, not an explicit additive load: it emerges from the spread between the 5% asset yield earned and the 6% discount rate at which liabilities are valued, net of mortality release and expense. The model does not add a separate explicit profit loading on top of the liability present value.
Output Snapshot
Deterministic single-block run of PRTModel — reproducible, requires no live firm data (python scripts/model_snapshots.py M-072 in InsModel; mechanics asserted by tests/test_prt_model.py). The provider is a MagicMock.
Canonical block (mirrors the snapshot-script header): 10,000 annuitants · avg age 70 · 65% female · $2,500/mo benefit · $3.3B premium/reserve (≈11x annual benefit PV) · 5% yield · 1% improvement · discount 6% · 240mo (full horizon; premium-solve still open).
Re-captured 2026-06-06 from
python3 scripts/model_snapshots.py M-072against the post-INV-025 funded default (initial_reserve$3.3B,prt_model.pyline 109, InsModel #52 / commitbda809a). Deterministic, no live firm data (MagicMock provider). With the block now funded the projection reaches the full 240-month horizon rather than exhausting at month 13.
| output | value | meaning |
|---|---|---|
| initial_premium_reserve | 3,300,000,000.00 | premium received / opening reserve (PV of default benefit stream, ≈11x annual benefit) |
| pvfp_at6pct | -1,176,158,338.41 | present value of net income at 6% discount |
| profit_margin | -0.3564 | pvfp / initial_reserve |
| longevity_sensitivity_plus10pct | -7,656,904.12 | PVFP impact of +10% improvement rate (negative: more improvement → lower profit) |
| month1_pension_payment | 25,000,000.00 | 10,000 × $2,500 (independent of reserve) |
| month1_investment_income | 13,444,608.49 | $3.3B × monthly-equiv of 5% |
| month1_mortality_release | 5,677,160.06 | reserve freed by month-1 deaths |
| month1_net_income | -6,324,064.79 | NII + release − payment − expense |
| month1_mortality_rate_annual_qx | 0.0204 | blended qx ≈ 0.02045 |
| final_month_projected | 240 | reaches the full projection horizon |
| final_reserve | 257,290,679.09 | end-of-horizon reserve |
Reading the snapshot (honest interpretation, not a defect to hide): PVFP is negative (−$1.18B, profit_margin −0.36) on this canonical block. This is the correct deterministic output of the engine as currently parameterised, not a bug: the liabilities are discounted at 6% while the backing assets earn only 5%, so the priced spread is adverse and the supplied $3.3B premium is not an adequate (fair) buyout price under those assumptions. The model is an adequacy scorer of a supplied premium, not a fair-price solver (see Limitation 2 — premium-solve still absent), so a negative PVFP is the engine correctly flagging that the default premium/spread is uneconomic. The first-month flow mechanics (pension payment 10,000 × $2,500 = $25M, blended annual qx ≈ 0.02045) are reserve-independent and individually checked by tests/test_prt_model.py. The longevity-sensitivity sign is negative as expected (more improvement → annuitants live longer → lower profit). See Limitations.
Re-captured 2026-06-06 from the snapshot script (deterministic, no live data); supersedes the stale $300M/month-13-depletion table cleared 2026-06-04 (INV-025).
Limitations and Known Gaps
- Default block now funds the benefit stream (degenerate-default fixed); premium-solve still absent. The earlier degenerate default —
initial_reserve$300Magainst a$25M/mobenefit stream, which exhausted the reserve at month 13 — was fixed in code on 2026-06-04 (INV-025 partial fix,prt_model.pyline 109): the defaultinitial_reserveis now$3.3B, the PV of the default benefit stream (≈11x annual benefit at age 70, 5% yield), so the canonical cohort funds over the full projection horizon. What remains true is that the engine still has no premium-solve: it scores the adequacy of a supplied premium (via the sign of PVFP / profit_margin) rather than computing the actuarially fair buyout price. A production run still supplies its own reserve; the default is now a self-consistent funded block but is not derived from a fair-price solve (see Limitation 2). Building the premium-solve is tracked for ratification (INV-025 still-open leg) and is not applied in this documentation pass. - Premium is an input, not an output. Unlike a true annuity-pricing routine, M-072 does not return a buyout premium from liability cash flows plus a loading. Premium adequacy is inferred from the sign of PVFP/profit_margin. There is no closed-form
PV(benefits) + loading → pricesolve. - Scalar mortality improvement, not age-banded. Improvement is a single annual percentage rather than the age-banded SOA Scale MP-2021 structure; biases qx at age extremes.
- Single blended portfolio yield — no asset-liability duration matching. Investment income uses one flat
portfolio_yield; no duration-matched asset portfolio or reinvestment/disintermediation risk, material for long-dated annuitant liabilities. - Single-life mortality only — no joint-and-survivor benefit form. Real PRT books carry J&S annuitants; the engine models single-life decrement only, understating tail duration for J&S-heavy blocks.
- No stochastic longevity. Longevity risk is captured solely by a deterministic ±10% improvement-rate shift; no stochastic mortality distribution or trend-uncertainty band.
- Simplified expense and capital model. Expense is reserve-bps plus per-life; no explicit acquisition cost. Return-on-capital uses a flat 4% charge, not an economic-capital computation.
- No reinsurance / cession modeled. Any reinsurance is an entity-level adjustment outside this product model.
- Firm-data path divergent (BV-032). The snapshot is deterministic with synthetic inputs only; no 10-K-grounded PRT-book figures are asserted.
Validation Packet
| evidence | status | reference |
|---|---|---|
| Unit/behavioural test suite | present | tests/test_prt_model.py — 24 tests (calculation, mortality decrement, reserve runoff, COLA, gender-mix, profitability, longevity sensitivity, governance). All green 2026-06-06. |
| Deterministic snapshot | present | scripts/model_snapshots.py M-072 — re-captured into §Output Snapshot 2026-06-06. |
| Validation evidence pack (MRM §10.5 item 5) | present | insightalm/modelling/validation_evidence/M-072/v1.0.0/README.md — maps each §10.5 sub-item to named tests + the snapshot (closes RAT-072 COND-001). |
| Per-version change log (MRM §10.5 item 7) | present | insightalm/modelling/model_cards/M-072-changes.md (closes RAT-072 COND-002). |
| Calibrated-block frozen gold case | pending | A frozen weight/output regression test asserting the $3.3B canonical run cell-for-cell is not yet added in InsModel (the snapshot is reproducible but not pinned by an assert). Tracked — not asserted here. |
| Premium-solve / fair-price validation | missing | No premium-solve exists in the engine (Limitation 2); engine-build, honestly conditional. |
| Independent challenge (2L) | per registry | see lifecycle. |
References
Engine and tests (InsModel):
- ecosystem/InsModel/Models/firmmodel/products/prt_model.py — PRTModel (insmodel.L4.prt_model, model_id P-005).
- tests/test_prt_model.py — 24-test suite. Snapshot: scripts/model_snapshots.py M-072.
- firmmodel/governance/legacy_metadata/PRTModel.yaml — extracted SR 11-7 narrative.
Actuarial / methodology: - SOA Research Report — Pension Risk Transfer: Actuarial Considerations (2019). - American Academy of Actuaries Practice Note — Assumptions for Group Annuity Contracts Used to Settle DB Pension Plan Obligations (2023). - ASOP No. 27 (rev. 2024) — economic assumptions for measuring pension obligations. - ASOP No. 35 — demographic assumptions (mortality, longevity improvement). - ASOP No. 56 — Modeling. - SOA 2012 Individual Annuity Mortality (IAM) Basic Table; SOA Mortality Improvement Scale MP-2021.
Internal: Decision 020 (Assumption Policy); Decision 023 (Policy-Based Binding); BV-032 (firm-data divergence).
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-05-09 — added DS-018 + DS-062 references and Standards Coverage section per Stage B (DG-005 closure). (Corrected 2026-06-04: an earlier wording of this entry stated that Methodology / Key Assumptions / Validation "remain stubbed for actuarial author follow-on" — those sections are in fact authored; the stale note has been removed.)
- 2026-06-06 — 1L remediation of RAT-072-v1.0.0 (Decision 053 §2.2). Re-captured the Output Snapshot from
scripts/model_snapshots.py M-072(deterministic; pvfp −$1.18B / profit_margin −0.36 / longevity_sens −$7.66M / final_reserve $257.3M at month 240) — supersedes the pending-re-capture placeholder; added the honest interpretation that negative PVFP is the engine correctly flagging the default premium/spread as uneconomic (not a defect). Assembled the validation-evidence pack atvalidation_evidence/M-072/v1.0.0/README.mdmapping MRM §10.5 item 5 sub-items to the 24 realtests/test_prt_model.pycases + the snapshot (closes COND-001). Created the per-version change logmodel_cards/M-072-changes.md(closes COND-002). No model outputs, back-test numbers, or validation results were fabricated; the premium-solve, frozen-gold regression, and realized-outcome back-test remain honestly conditional. - 2026-06-04 — recorded the INV-025 default-funding code change: engine default
initial_reserveraised$300M → $3.3B(PV of the default benefit stream, ≈11x annual benefit;prt_model.pyline 109, InsModel #52 / commitbda809a), and the snapshot script's M-072 config + caption updated to the$3.3B/ 240mo canonical block ("full horizon; premium-solve still open"). Card refreshed to match: cleared the stale$300M/ month-13-depletion Output Snapshot (numeric re-capture left for the gated snapshot run), rewrote Limitation 1 (degenerate-default fixed; premium-solve still absent), reframed the Validation-Packet priced-block row to a pending calibrated-block frozen-gold case, and anchored the Methodology worked default on the funded$3.3Bblock. No model outputs, back-test numbers, or validation results were fabricated or changed; the premium-solve, snapshot re-capture, frozen-gold case, and Tier-1 validation pack remain tracked for ratification.
Open findings (2)
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.
PRT default params underfund the benefit stream; no premium-solve
Shipped initial_reserve ($300M) is far too small for the shipped benefit stream (10,000 x $2,500/mo = $25M/mo), so the canonical projection exhausts the reserve at month 13 and returns degenerate profitability (PVFP ~ -premium, no breakeven). The engine has no premium-solve: it scores adequacy of a *supplied* premium rather than computing the actuarially fair buyout price.
Recommendation: Ship internally-consistent defaults (reserve = PV of the benefit stream) AND add a premium-solve (PV(benefits)+loading -> price). Add a calibrated-block frozen case.
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-072-v1.0.2
2L issued RAT-072-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-001 | 2026-08-23 | NARROWED, STILL OPEN (the remaining Tier-1 blocker). The validation-evidence pack provides adequate conceptual soundness, multi-config/OOS, sensitivity on the material assumptions, and internal-consistency cross-checks within tolerance — all independently re-run by the reviewer on this pass (24 tests pass; snapshot reproduces to the cent; test bodies inspected and real). What is still missing for an unconditional Tier-1 approval is the §10.5 item-5 sub- requirement COND-001 names: (a) a model-vs-realized-outcome back-test against disclosed-firm PRT results (pending real disclosed data — BV-032; the PRU Institutional-Retirement block is a conceptual anchor only, no residual computed), and (b) an independent challenger re-implementation (gold-copy / QuantLib) for a credentialed-grade effective challenge. Build these (engine/data work, not a documentation fix per Decision 053 §4) and file the residual + tolerance, then this clears. |
| COND-002 | 2026-08-23 | CLEARED. Per-version change log filed at insightalm/modelling/model_cards/M-072-changes.md, satisfying MRM §10.5 item 7: keyed on M-072 v1.0.0, begins at the v1.0.0 release point, records the material INV-025 default-funding change with revalidation evidence, and carries the open items forward honestly. Verified by direct read on this pass; no fabricated figures (the log's snapshot table matches what the reviewer re-ran). |