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.
Inputs, processing, outputs
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:
- Treasury — 3-factor CIR with deterministic shift (
goes.L5.cir_treasury,CIRThreeFactorModel). Three independent Cox-Ingersoll-Ross factorsdX_i = (κ_i − λ1_i)((θ_i + λ0_i) − X_i) dt + σ_i √X_i dW_icombine additively into the overnight rate, plus a deterministic shift functionl(s)calibrated so the month-0 curve reproduces the initial market curve exactly (verified bytest_initial_curve_match). Yields come from the closed-form CIR zero-coupon bond coefficientsP_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 = 3per 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-coefficientyield_amplifierfor tail widening. This is the sub-generator snapshotted below. - 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. - 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. - 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. - 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 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
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 viapython3 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 and Known Gaps
- 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 toactiveand ratified. - 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. - 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.3plus 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 prioryield_amplifier=1.0default), the official-file reconciliation is 444/481 (Treasury 60/60), and the acceptance scorecard is 35/40, 26/26 primary (therate_ceilingis 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 anddocs/naic_validation_report.md §5), bounded overshoot, not a defect. - 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.
- 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.
- 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_assumptionsreturns empty by design). - 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_v1CSV 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 intoecosystem/goes/data/naic_official/20241231/, the 11 previously-skippedtest_naic_comparison.pytests are live and passing, and--comparereconciles 444/481 (Treasury 60/60). COND-003 (doc-currency surface mapping) was cleared in the prior tranche (GOES engine files now resolve to the realecosystem/goesrepo 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 lifecycleunder_development → activepromotion itself (2L re-review of this remediation;last_validated_onto be set on promotion).
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
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.py — GOESGenerator / GOESConfig orchestrator (goes.L5.orchestrator).
- treasury/cir_three_factor.py — CIRThreeFactorModel (goes.L5.cir_treasury).
- equity/svj_model.py — SVJEquityModel (goes.L5.svj_equity).
- corporate/credit_model.py — CreditSpreadModel (8-fund / rating-transition credit).
- inflation/cpi_model.py — InflationModel (goes.L5.cpi_inflation).
- correlation/jump_process.py — GlobalJumpProcess (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.py — GOESDataProvider (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
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 fromto. - 2026-06-06 — code-grounded documentation pass. Corrected the Components output-contract id from the non-existent
goes_scenarios_v1to the canonicalgoes_output_v1(v1.0.0), reconciling Components with the card's existing Methodology / Validation Packet / References and withcontract_registry.yaml(the registrymodel_registry.yamlline 1557 still listsgoes_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 frommodel_registryupstream_modelsedges (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.
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 | attested |
Not required (Tier 3)
Per MRM Framework §10.2, Tier-3 models are revalidated on material change only and do not require formal ratification.