Skip to content

What-If Analysis

Gaspatchio supports a declarative config format that lets you define scenario shocks without writing Python code. This is designed for:

  • Actuaries: Ask natural language questions about assumption changes
  • LLMs: Generate executable scenario configs from user questions
  • Audit: Clear, JSON-serializable shock specifications

For deeper context on why configs matter (and why you don’t need to regenerate assumption tables) plus more realistic actuarial prompts, see Natural Language → Executable Configs.

The Concept

Instead of creating separate assumption files for each scenario or writing Python shock classes, you specify simple JSON configs:

{"table": "mortality", "multiply": 1.2}

The framework parses this into shock objects and applies them to your existing tables automatically. Your base assumptions stay untouched - shocks create modified copies at runtime.


Asking What-If Questions

Here are example questions and the configs they translate to:

"What if mortality increases by 20%?"

[
    {"id": "BASE"},
    {"id": "MORT_UP_20", "shocks": [{"table": "mortality", "multiply": 1.2}]}
]

"What happens if lapse rates drop by half?"

[
    {"id": "BASE"},
    {"id": "LAPSE_DOWN_50", "shocks": [{"table": "lapse", "multiply": 0.5}]}
]

"Show me the impact of a flat 5% discount rate"

[
    {"id": "BASE"},
    {"id": "DISC_FLAT_5PCT", "shocks": [{"table": "disc_rates", "set": 0.05}]}
]

"What if interest rates increase by 100 basis points?"

[
    {"id": "BASE"},
    {"id": "RATES_UP_100BPS", "shocks": [{"table": "disc_rates", "add": 0.01}]}
]

"What's the worst case if mortality is 20% higher AND lapses drop 10%?"

[
    {"id": "BASE"},
    {"id": "ADVERSE", "shocks": [
        {"table": "mortality", "multiply": 1.2},
        {"table": "lapse", "multiply": 0.9}
    ]}
]

Config Format Reference

Scenario Config Structure

A scenario config is a list where each element defines a scenario:

[
    {"id": "SCENARIO_NAME"},
    {"id": "SCENARIO_NAME", "shocks": [shock, ...]}
]
Field Type Required Description
id string Yes Unique scenario identifier
shocks list No List of shock configs to apply

Shock Config Structure

Each shock specifies which table to shock and how to shock it:

{
    "table": "table_name",
    "column": "column_name",
    "multiply": 1.2
}
Field Type Required Description
table string Yes Target assumption table name
column string No Specific column (defaults to value column)
Operation float Yes One of: multiply, add, or set

Shock Operations

Operation Effect Example Result
multiply Scale by factor {"table": "mortality", "multiply": 1.2} value * 1.2
add Add constant {"table": "rates", "add": 0.01} value + 0.01
set Replace with value {"table": "lapse", "set": 0.0} value = 0.0

Rules:

  • Exactly one operation per shock (multiply, add, or set)
  • Multiple shocks can target the same table (applied sequentially)
  • Different shocks can target different tables in the same scenario

Realistic Scenario Examples

Interest Rate Sensitivity

Question: "Show me PV impact of rates moving +/-50bps and +/-100bps"

[
    {"id": "BASE"},
    {"id": "RATES_DOWN_100BPS", "shocks": [{"table": "disc_rates", "add": -0.01}]},
    {"id": "RATES_DOWN_50BPS", "shocks": [{"table": "disc_rates", "add": -0.005}]},
    {"id": "RATES_UP_50BPS", "shocks": [{"table": "disc_rates", "add": 0.005}]},
    {"id": "RATES_UP_100BPS", "shocks": [{"table": "disc_rates", "add": 0.01}]}
]

Mortality Sensitivity

Question: "What's the impact of mortality being 10%, 20%, or 30% higher than expected?"

[
    {"id": "BASE"},
    {"id": "MORT_UP_10", "shocks": [{"table": "mortality", "multiply": 1.1}]},
    {"id": "MORT_UP_20", "shocks": [{"table": "mortality", "multiply": 1.2}]},
    {"id": "MORT_UP_30", "shocks": [{"table": "mortality", "multiply": 1.3}]}
]

Combined Stress Scenarios

Question: "Show me best case, base case, and worst case scenarios"

[
    {"id": "BEST_CASE", "shocks": [
        {"table": "mortality", "multiply": 0.9},
        {"table": "lapse", "multiply": 1.1},
        {"table": "disc_rates", "add": 0.005}
    ]},
    {"id": "BASE"},
    {"id": "WORST_CASE", "shocks": [
        {"table": "mortality", "multiply": 1.2},
        {"table": "lapse", "multiply": 0.8},
        {"table": "disc_rates", "add": -0.01}
    ]}
]

Regulatory Stress Test

Question: "Run the standard regulatory stress scenarios"

[
    {"id": "BASE"},
    {"id": "EQUITY_DOWN_40", "shocks": [{"table": "equity_returns", "multiply": 0.6}]},
    {"id": "RATES_DOWN_200BPS", "shocks": [{"table": "disc_rates", "add": -0.02}]},
    {"id": "MORT_PANDEMIC", "shocks": [{"table": "mortality", "multiply": 1.5}]},
    {"id": "COMBINED_STRESS", "shocks": [
        {"table": "equity_returns", "multiply": 0.8},
        {"table": "disc_rates", "add": -0.01},
        {"table": "mortality", "multiply": 1.2},
        {"table": "lapse", "multiply": 1.3}
    ]}
]

Flat Assumption Override

Question: "What if we assume zero lapses and mortality?"

[
    {"id": "BASE"},
    {"id": "NO_DECREMENTS", "shocks": [
        {"table": "mortality", "set": 0.0},
        {"table": "lapse", "set": 0.0}
    ]}
]

Common Table Names

When generating configs, use these standard table names:

Table Name Description Typical Shocks
mortality Death rates by age/duration multiply (+/-10-50%)
lapse Withdrawal/surrender rates multiply (+/-20-50%)
disc_rates Discount/interest rates add (+/-50-200bps)
expense Per-policy expenses multiply (+/-10-30%)
inflation Expense inflation add (+/-1-3%)
equity_returns Fund returns multiply (stress), add (drift)
premium_rates Premium loading multiply (rare)

Shock Magnitude Guidelines

Typical ranges for sensitivity analysis:

Assumption Typical Range Notes
Mortality +/-10% to +/-50% Higher for pandemic stress
Lapse +/-20% to +/-50% Direction depends on product
Interest rates +/-50bps to +/-200bps Use add not multiply
Expenses +/-10% to +/-30% Often paired with inflation
Equity -20% to -40% Stress scenarios

Naming Conventions

Use clear, descriptive scenario IDs:

Pattern Example Use Case
{TABLE}_{DIRECTION}_{AMOUNT} MORT_UP_20 Single assumption shock
{DIRECTION}_{AMOUNT}BPS RATES_DOWN_100BPS Interest rate moves
Descriptive ADVERSE, BEST_CASE Combined scenarios
Regulatory SFCR_STRESS_1 Standard tests

Best Practices

Always Include BASE

Every config should include an unshocked BASE scenario for comparison:

[
    {"id": "BASE"},
    {"id": "STRESS_1", "shocks": [...]},
    {"id": "STRESS_2", "shocks": [...]}
]

Validation Errors

The parser validates configs and provides clear error messages:

Error Cause
"Shock config must include 'table' key" Missing table field
"Shock config must include exactly one operation" Missing or multiple operations
"Duplicate scenario ID" Same id used twice

LLM Integration

The config format is designed for LLM generation. An LLM can translate natural language to executable configs:

User Question Generated Config
"What if mortality is 20% higher?" {"table": "mortality", "multiply": 1.2}
"Add 50bps to discount rates" {"table": "disc_rates", "add": 0.005}
"Set lapse rates to zero" {"table": "lapse", "set": 0.0}
"What's the worst case?" Multiple shocks combined

This enables actuaries to ask questions in plain English and get executable scenario analysis without writing code.


See Also