Tier 1 · Capital Solvency Assessment

GOES Economic Scenario Generator

M-120 · lifecycle: monitoring · ratification broken

Intended Use

GOES Economic Scenario Generator Produce stochastic economic scenarios (yield, equity, credit, inflation) for downstream reserve, capital, and embedded-value modeling.

Per §12.1 worked example. The canonical model-of-models upstream of every Tier-1 reserve and capital model on the platform.


Components

Inputs, processing, outputs

data sources
DS-001 · DS-007 · DS-012 · DS-015
assumptions
A-050, A-051, A-052, A-053, A-060, A-061, A-062, A-063, A-090, A-091
engines
goes.L5.cir_treasury
goes.L5.svj_equity
goes.L5.credit_model
goes.L5.cpi_inflation
goes.L5.orchestrator
contracts
goes_output_v1
dimensions
D1

Methodology & Mechanics

Methodology

M-120 is the GOES Economic Scenario Generator (Generator Of Economic Scenarios) — the prescribed multi-factor real-world ESG that the NAIC adopts in the 2026 Valuation Manual to replace the Academy Interest Rate Generator (AIRG) for principles-based reserve and capital work. GOES is the NAIC's custom calibration of Conning's GEMS engine; M-120 is the platform's independent, open re-implementation of that calibration (it does not embed Conning's proprietary GEMS code). It produces coherent stochastic paths for Treasury yields, equity, corporate credit, and inflation that feed nearly every Tier-1 reserve/capital model: VM-20 stochastic reserves (M-001), VM-21 CTE (M-002), and C-3 Phase I/II capital (M-050).

GOES lives in its own repository (ecosystem/goes, package goes) and is wired to InsModel through the goes_output_v1 contract: GOES writes six CSV files (treasury_yields, equity_gwf, bond_gwf, equity_returns, bond_returns, inflation), and InsModel's firmmodel/data_providers/goes_data_provider.py (GOESDataProvider) reads them and serves them to the calculation engines through the standard EngineDataProvider interface. The same contract is also consumed by Gold Copy (gold_copy/scenarios/goes_reader.py).

The generator (goes.L5.orchestrator, GOESGenerator in src/goes/generator.py) composes five sub-models on a single monthly clock (dt = 1/12), default horizon 30 years, default 10,000 scenarios, seeded for reproducibility:

  1. Treasury — 3-factor CIR with deterministic shift (goes.L5.cir_treasury, CIRThreeFactorModel). Three independent Cox-Ingersoll-Ross factors dX_i = (κ_i − λ1_i)((θ_i + λ0_i) − X_i) dt + σ_i √X_i dW_i combine additively into the overnight rate, plus a deterministic shift function l(s) calibrated so the month-0 curve reproduces the initial market curve exactly (verified by test_initial_curve_match). Yields come from the closed-form CIR zero-coupon bond coefficients P_i(τ) = A_i(τ)·exp(−B_i(τ)·X_i). The factors carry NAIC-Q&A risk premia (λ0, λ1) splitting risk-neutral from real-world dynamics, an exponential shift decay (Decay = 3 per NAIC Q&A Q8), a permanent maturity-graded term premium, a fractional rate floor, weekly sub-stepping (4 sub-steps/month) for integration accuracy, and optional 2-regime volatility switching plus a B-coefficient yield_amplifier for tail widening. This is the sub-generator snapshotted below.
  2. Equity — Stochastic-Volatility-with-Jumps (SVJ) (goes.L5.svj_equity, SVJEquityModel). A Bates-1996 model (Heston stochastic variance + Merton jumps): dS/S = (r(t) + ERP − λμ_J − ½V) dt + √V dW_S + J dN, dV = κ_v(θ_v − V) dt + σ_v √V dW_V, with leverage correlation ρ = −0.70. Drift is linked to the CIR overnight rate; dividends are linked to the CIR 10-year yield. Five secondary equity indices (Mid/Small Cap, NASDAQ, EAFE, EM) are derived from S&P 500 by a Sharpe-ratio / vol-ratio scaling.
  3. Corporate credit (CreditSpreadModel). Mean-reverting (CIR-like, non-negative) credit spreads with an 8×8 Markov rating-transition matrix (AAA…CCC + Default), producing total/income/price returns for 8 bond funds layered on the Treasury curve.
  4. Inflation / CPI (goes.L5.cpi_inflation, InflationModel). Mean-reverting inflation (θ ≈ 2.5%) linked to the short rate, producing a CPI index path and a real-short-rate path.
  5. Global jump process (GlobalJumpProcess). A cross-asset Poisson tail event that simultaneously shocks equity down, widens spreads, and spikes volatility — reproducing the empirical rise in cross-asset correlation during crises.

The orchestrator runs them in dependency order on one RNG stream (global jump → Treasury → equity → credit → secondary equity → wealth factors → inflation), so the resulting scenario set is internally coherent across asset classes. Output is emitted in NAIC GOES CSV format and validated against the downloaded official NAIC/Conning scenario files via src/goes/naic_comparison.py (the --compare CLI flag).


Key Assumptions

Key Assumptions and Their Justification

GOES is parameter-driven via GOESConfig. The load-bearing assumptions, with the values shipped in the default production config:

Domain Parameter Value Justification
Treasury (CIR) Factor mean-reversion speed κ (3 factors) 0.30 / 0.08 / 0.50 Fast short-end factor, slow level factor, medium slope factor — the standard 3-factor decomposition reproducing curve level/slope/curvature dynamics.
Treasury (CIR) Factor reversion level θ 0.018 / 0.0135 / 0.005 Recalibrated downward (F1 0.020→0.018, F2 0.015→0.0135) to pull the long-run yield median ~35bps closer to the official NAIC GOES median.
Treasury (CIR) Factor volatility σ 0.050 / 0.030 / 0.020 Per-factor diffusion; each satisfies the real-world Feller condition 2(κ−λ1)(θ+λ0) > σ² (enforced in CIRFactorParams.validate, tested by test_feller_violation).
Treasury (CIR) Risk premia λ0 / λ1 small ±values Split risk-neutral from real-world dynamics per NAIC ESG Q&A Q11; κ−λ1 > 0 and θ+λ0 > 0 are mechanically enforced.
Treasury (CIR) Shift decay / sub-stepping Decay=3 · 4 weekly sub-steps Per NAIC Q&A Q8 (Decay=3 in exposed scenarios); weekly sub-stepping matches Conning's integration accuracy.
Equity (SVJ) Vol mean-reversion / vol-of-vol κ_v=3.0 · σ_v=0.20 Calibrated so implied annual equity vol ≈ 14–15% (historical ≈14.5%) and 10Y GWF median ≈ 1.88–2.09 (Conning H2 target).
Equity (SVJ) Leverage correlation ρ −0.70 Negative price-vol correlation (leverage effect) — standard Heston calibration; vol spikes as prices fall.
Equity (SVJ) Jump intensity / mean / vol 0.20 / −0.06 / 0.08 ~0.2 jumps/yr, downward-biased — reproduces fat left tail in equity returns; jump compensator offsets expected jump drift.
Equity (SVJ) Equity risk premium 0.025 Additional drift above the risk-free rate; gives geometric mean ≈ 7.2–7.4%.
Credit Rating transition matrix 8×8 annual Simplified-from-historical Markov matrix driving spread/default dynamics across AAA→Default.
Inflation Long-run target θ_π 0.025 ~2.5% long-run inflation per GOES; mean-reverting and rate-linked.
Global jump Intensity / equity impact 0.08 / −0.12 Cross-asset tail event (~0.08/yr) that raises correlations in crises.

Operational notes: The basic CIR model produces narrower yield distributions than full GEMS, so the shipped default applies a calibrated yield_amplifier = 4.3 (centered B-coefficient tail widening) together with a public 20% rate ceiling (rate_ceiling = 0.20, per GOES Acceptance Criteria 1A/1I; confirmed by the genuine NAIC 2024-12-31 file where the 3M P99 maxes ~15.4%). This lands the 10Y P5–P95 at year 10 at ~875bps vs the official NAIC target of ~891bps (within reconciliation tolerance), keeps all 26 primary acceptance criteria met (the rate_ceiling is a safety guardrail whose threshold traces to the public 20% criterion, not load-bearing — with the cap off, amp=4.3's 3M tail tops out ~14%, so pct_3m_above_20pct passes either way; 2L NB-120-01), and preserves the median (the amplifier is centered, so it does not shift P50). Optional 2-regime volatility switching (regime_params) remains off by default. The 10,000-scenario / 30-year defaults are the production run size; the snapshot below uses a reduced set for fast, deterministic documentation.


Output Snapshot

Output Snapshot

Deterministic run of the GOES CIR 3-factor Treasury sub-generator (goes.L5.cir_treasury, CIRThreeFactorModel) using the production default CIR factors and initial curve — reproducible, requires no live firm data (python scripts/model_snapshots.py M-120 in InsModel; the underlying engine is asserted by goes/tests/test_treasury.py, 32 passing tests). Because goes is a separate package, the snapshot inserts the GOES source tree on sys.path before importing.

Input: seed 42 · 200 scenarios · 120 monthly steps (10-year horizon) · default GOES initial curve (10Y = 4.400%)

output value meaning
seed 42 fixed RNG seed (reproducible)
n_scenarios 200 scenario paths simulated
n_steps_monthly 120 monthly steps = 10-year horizon
initial_10y_yield 4.400% month-0 calibrated curve point
10y_yield_mean 4.903% mean terminal 10Y Treasury yield
10y_yield_p5 0.850% 5th-percentile terminal 10Y yield
10y_yield_p50 4.556% median terminal 10Y yield
10y_yield_p95 9.717% 95th-percentile terminal 10Y yield
10y_yield_min / max 0.010% / 13.139% terminal 10Y yield range
short_rate_mean 5.094% mean terminal overnight short rate
short_rate_p5 / p50 / p95 2.989% / 5.050% / 7.388% terminal short-rate distribution

The terminal 10Y distribution mean-reverts toward the CIR long-run level and stays positive (Feller + fractional floor). The P5–P95 band is now ~887bps at the 10-year horizon — landing on the official NAIC GOES 2024-12-31 target of 891bps — because the shipped default now applies the calibrated yield_amplifier = 4.3 plus the public 20% rate ceiling (COND-002 / INV-009 resolved; see Limitations item 3). The median (~4.6%) is preserved — the amplifier is centered on E[X_i(t)], so it widens the tail without shifting the median. These are scenario inputs only — they become reserves/capital only when consumed downstream by M-001/M-002/M-050.

Diff vs prior snapshot (2026-06-04, pre-calibration default yield_amplifier=1.0, no ceiling): 10Y P5 3.866% → 0.850%, P50 4.728% → 4.556% (preserved), P95 5.928% → 9.717%, P5–P95 ~206bps → ~887bps, min/max 3.584%/6.724% → 0.010%/13.139%. The widening to the NAIC band is the intended, output-changing calibration. Re-froze via python3 scripts/model_snapshots.py M-120 (InsModel) after setting the calibrated default.

Captured 2026-06-07 · deterministic, no live data · calibrated default (COND-002).


Limitations

Limitations and Known Gaps

  1. Status: under_development — not production-ratified. M-120 is an active re-implementation of the NAIC GOES calibration, not a frozen, 2L-ratified model. Its parameters, default toggles, and tail calibration are still moving. No production reserve or capital number should be certified off M-120 output until it is promoted to active and ratified.
  2. Upstream-dependency cascade. M-120 is the prescribed scenario source for the full stochastic legs of M-001 (VM-20 CTE70), M-002 (VM-21 CTE), and M-050 (C-3 Phase I/II). Because M-120 is under_development, those consumers currently run their deterministic / 16-scenario-exclusion paths only and cannot certify a held full stochastic CTE reserve. M-120's status therefore cascades: until it is ratified, the downstream stochastic reserves of M-001/M-002/M-050 are blocked at the deterministic floor (see M-001 Limitation 6 / Output Snapshot). This is the single highest-leverage gap in the Tier-1 chain.
  3. Tail width calibrated to the NAIC band (COND-002 resolved); residual extreme-tail shape gaps remain. The basic 3-factor CIR produces narrower yield distributions than full GEMS, so the shipped default now applies a calibrated yield_amplifier = 4.3 plus a public 20% rate ceiling (rate_ceiling = 0.20). At this default the 10Y P5–P95 is ~875bps vs the official NAIC ~891bps target (was ~206bps at the prior yield_amplifier=1.0 default), the official-file reconciliation is 444/481 (Treasury 60/60), and the acceptance scorecard is 35/40, 26/26 primary (the rate_ceiling is a public-criterion safety guardrail, not load-bearing for this calibration — the centered amp=4.3 3M tail tops out ~14%; 2L NB-120-01). The 5 remaining acceptance misses are extreme (1st/99th-percentile 20Y) tail-shape features that need time-varying κ/θ or regime switching, which the centered amplifier does not fully reproduce. A documented residual: bond-fund excess returns now reconcile 0/4 (the widened rate fan inflates the rate-driven return component, ~38–43bps over NAIC for Short/Long IG) — a known CIR-vs-GEMS limitation (see item 5 and docs/naic_validation_report.md §5), bounded overshoot, not a defect.
  4. Risk premia λ0/λ1 are approximate, not Conning's. The exact Conning Lambda0/Lambda1 calibration is proprietary and unavailable; M-120 uses fitted approximations. This is a structural source of divergence from the official NAIC files at the distribution edges.
  5. Bond-fund GWF residuals at long horizons. Short-duration bond funds (Money Market, Short Govt, Short IG Corp) overshoot NAIC GWF at the 20–30-year horizon because the CIR yield median runs ~10–14bps above NAIC and compounds; this is a calibration residual, not fixable by duration tuning alone.
  6. No live-data calibration; immune to but separate from BV-032. The snapshot is purely synthetic (seeded RNG, no firm filings), so it is not exposed to the BV-032 firm-data divergence — but it also makes no 10-K / firm-specific claim. M-120 generates market scenarios only; it provides no mortality, lapse, expense, or firm-financial assumptions (GOESDataProvider.get_assumptions returns empty by design).
  7. Cross-venv coupling. GOES is a separate repo/venv and is not installed in the InsModel venv; the InsModel-side snapshot only runs by path-inserting the GOES source tree. Production consumption is decoupled correctly via the goes_output_v1 CSV contract, but any in-process snapshot has this packaging caveat.

Ratification status. Resolved in the Decision 053 re-review loop: INV-009 / COND-002 (default-config calibration to the NAIC band) is applied — the shipped default is now yield_amplifier=4.3 + rate_ceiling=0.20, giving 10Y P5–P95 ~875bps vs the ~891bps target, with the gold snapshot re-frozen and the diff recorded above. COND-001 (official-file backtest) is cleared — the public NAIC/Conning 2024-12-31 statistics XLSX was acquired into ecosystem/goes/data/naic_official/20241231/, the 11 previously-skipped test_naic_comparison.py tests are live and passing, and --compare reconciles 444/481 (Treasury 60/60). COND-003 (doc-currency surface mapping) was cleared in the prior tranche (GOES engine files now resolve to the real ecosystem/goes repo shas). Still pending 2L: unlocking the held full stochastic reserves of M-001/M-002/M-050 (INV-010 cascade — gated on M-120 promotion + re-validation of the held reserves) and the lifecycle under_development → active promotion itself (2L re-review of this remediation; last_validated_on to be set on promotion).


Validation Evidence

Validation Packet

Check Where What it asserts
Treasury engine suite (32 tests) goes/tests/test_treasury.py Simulation shapes, short-rate non-negativity, month-0 curve reproduction (test_initial_curve_match, atol 1e-10), reproducibility under a fixed seed, mean-reversion to Σθ, Feller-condition enforcement, regime-switching paths.
Equity / inflation / integration goes/tests/test_equity.py, test_inflation.py, test_integration.py SVJ return/vol behavior, inflation mean-reversion + rate linkage, end-to-end orchestrator coherence.
Acceptance criteria goes/tests/test_acceptance.py, src/goes/acceptance_criteria.py Scorecard against GOES acceptance-criteria spec (34/40 met; 6 documented CIR-limitation misses).
NAIC official comparison goes/tests/test_naic_comparison.py, src/goes/naic_comparison.py (--compare) Distributional comparison of yields/GWF against the downloaded official NAIC/Conning 2024-12-31 scenario files.
Feller / parameter guards CIRFactorParams.validate, SVJParams.validate Real-world Feller 2(κ−λ1)(θ+λ0) > σ², positive reversion, ρ∈[−1,1] — raise at construction (test_feller_violation, test_negative_kappa_rw).
Contract conformance ecosystem/contracts/goes_output_v1.json (v1.0.0) CSV column names/types/units consumed by InsModel GOESDataProvider and Gold Copy goes_reader.py; any change requires a version bump + migration in all consumers.
Consumer-side integration InsModel/Models/tests/test_goes_data_provider.py, test_projection_goes_integration.py, test_vm20_stochastic_goes.py InsModel correctly ingests GOES CSVs and feeds VM-20 stochastic projection.
Output snapshot InsModel/Models/scripts/model_snapshots.py M-120 Deterministic CIR Treasury summary stats reproduced above (seed 42).

Governance binding: engine IDs are stamped via the Decision 023 @engine("goes.L5.*") decorator (src/goes/governance.py); SR 11-7 narrative lives in src/goes/legacy_metadata/<ClassName>.yaml. No 2L production ratification exists yet (consistent with under_development).


References

References

Regulatory / actuarial: - NAIC GOES (Generator of Economic Scenarios) — prescribed ESG replacing the Academy Interest Rate Generator (AIRG) in the 2026 Valuation Manual for PBR reserve and capital work. - NAIC ESG Q&A (April 2021) — CIR factor dynamics, real-world constraints (Q11), shift decay (Q8, Decay=3). - American Academy of Actuaries ESG / AIRG — predecessor scenario generator being superseded by GOES. - Conning GEMS — the commercial ESG that the official NAIC GOES calibration is built on; M-120 is an independent re-implementation (GEMS code not embedded). - C-3 Phase I/II capital framework — downstream consumer of GOES scenarios (M-050).

Academic: - Cox, Ingersoll & Ross (1985) — the square-root short-rate model underlying the 3-factor Treasury generator. - Bates (1996) / Heston (1993) + Merton (1976) — stochastic-volatility-with-jumps equity dynamics.

Engine source (ecosystem/goes/src/goes/): - generator.pyGOESGenerator / GOESConfig orchestrator (goes.L5.orchestrator). - treasury/cir_three_factor.pyCIRThreeFactorModel (goes.L5.cir_treasury). - equity/svj_model.pySVJEquityModel (goes.L5.svj_equity). - corporate/credit_model.pyCreditSpreadModel (8-fund / rating-transition credit). - inflation/cpi_model.pyInflationModel (goes.L5.cpi_inflation). - correlation/jump_process.pyGlobalJumpProcess (cross-asset tail jumps). - naic_comparison.py — official-file comparison harness.

Consumer / contract: - ecosystem/contracts/goes_output_v1.json (v1.0.0) — output CSV contract (producer goes; consumers insmodel, gold_copy). - InsModel/Models/firmmodel/data_providers/goes_data_provider.pyGOESDataProvider (InsModel ingestion). - gold_copy/scenarios/goes_reader.py — Gold Copy ingestion.

Tests: - goes/tests/test_treasury.py, test_equity.py, test_inflation.py, test_integration.py, test_acceptance.py, test_naic_comparison.py. - InsModel/Models/tests/test_goes_data_provider.py, test_projection_goes_integration.py, test_vm20_stochastic_goes.py.

Internal: - Decision 023 (Policy-Based Binding Architecture) — @engine decorators / goes.L5.* engine IDs. - BV-032 — firm-data divergence note; M-120 is synthetic-only and makes no firm-specific claim.


Change Log

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, Limitations and Known Gaps, Output Snapshot, Validation Packet, and References from GOES engine source (cir_three_factor.py, svj_model.py, credit_model.py, cpi_model.py, generator.py) + legacy_metadata + the model/contract registries. Stub marker advanced from to.
  • 2026-06-06 — code-grounded documentation pass. Corrected the Components output-contract id from the non-existent goes_scenarios_v1 to the canonical goes_output_v1 (v1.0.0), reconciling Components with the card's existing Methodology / Validation Packet / References and with contract_registry.yaml (the registry model_registry.yaml line 1557 still lists goes_scenarios_v1 — registry fix left as governance work). Added a dedicated Standards Coverage section (asop_56 / naic_ag53 / sr_26_2 / internal, each grounded in the implementing source). Added a dedicated Dependencies section enumerating the downstream consumers from model_registry upstream_models edges (M-001/M-002/M-004/M-005/M-020/M-021/M-050/M-051/M-070/M-110). Expanded the stub Description to a self-contained paragraph. Noted gated/ratification items (INV-009, INV-010 cascade, validation-evidence pack + lifecycle promotion, doc-currency surface mapping) under Limitations. No model outputs, back-test numbers, or validation results were fabricated or changed.

2L Inventory Review

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.

HIGH INV-029 · P5 · validation-gap

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.


Validation Coverage

Per-tier expectations

Per MRM Framework §10.2 + §10.3, this model's regulatory_frameworks tag list activates the following overlays:

asop_56 naic_ag53 sr_26_2 internal
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 attested

Ratification

Not required (Tier 3)

Per MRM Framework §10.2, Tier-3 models are revalidated on material change only and do not require formal ratification.