Session 20: Catastrophe Modeling & Risk Assessment
Learning Objectives
- Explain the three modules of a catastrophe model — Hazard, Vulnerability, Financial — and how they connect to estimate insured loss
- Build a Monte Carlo simulation in Python that generates 10,000 synthetic years of cyclone events
- Calculate key CAT model metrics — Average Annual Loss (AAL), Standard Deviation of Annual Loss, and Probable Maximum Loss (PML) at various return periods
- Construct and interpret an Exceedance Probability (EP) curve — the single most important output of a catastrophe model
- Use EP curve outputs to recommend a reinsurance programme structure — retention attachment, limit, and layers
1. What is Catastrophe Modeling?
A catastrophe (CAT) model is a computer simulation that estimates the losses from natural disasters — cyclones, floods, earthquakes, wildfires, and other extreme events. Insurers, reinsurers, and governments use CAT models to understand their exposure to catastrophic events, set premiums and reserves, purchase reinsurance, and make strategic decisions about geographic diversification and risk appetite.
CAT models are necessary because historical experience is insufficient for catastrophe risk. A 1-in-200 year cyclone event may not have occurred in the last 50 years of recorded data — yet it could happen tomorrow. An insurer that prices cyclone risk based only on recent history will systematically underestimate the risk and fail when a rare event occurs. CAT models compensate for this data gap by generating millions of synthetic events from the statistical distributions that describe the underlying physical processes.
1.1 The Three Modules of a Catastrophe Model
┌─────────────────────────────────────────────────────────────────────┐
│ CATASTROPHE MODEL ARCHITECTURE │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1. HAZARD MODULE │ │
│ │ │ │
│ │ WHAT: Where, how often, and how severe are the natural │ │
│ │ events? Defines the event set — the "event catalog." │ │
│ │ │ │
│ │ INPUTS: Historical event data, geological/geophysical │ │
│ │ parameters, climate projections, DEM topography, │ │
│ │ sea surface temperature, wind patterns │ │
│ │ │ │
│ │ OUTPUTS: Event frequency distribution (Poisson), event │ │
│ │ severity distribution (Lognormal, GEV), event │ │
│ │ footprint (wind speed at each location) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 2. VULNERABILITY MODULE │ │
│ │ │ │
│ │ WHAT: Given the event intensity at a specific location, │ │
│ │ what proportion of the property value is damaged? │ │
│ │ │ │
│ │ INPUTS: Engineering studies of building/infrastructure │ │
│ │ performance under stress, claims data from past │ │
│ │ events, building code information, construction │ │
│ │ type, occupancy, age │ │
│ │ │ │
│ │ OUTPUTS: Damage ratio curves (mean damage ratio as │ │
│ │ function of wind speed, flood depth, PGA, etc.) │ │
│ │ plus uncertainty bounds │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 3. FINANCIAL MODULE │ │
│ │ │ │
│ │ WHAT: Given the damage to each exposure, what is the │ │
│ │ insured loss? Applies policy terms (deductibles, │ │
│ │ limits, coinsurance) to convert damage into loss. │ │
│ │ │ │
│ │ INPUTS: Policy-level exposure data (sum insured by │ │
│ │ location, policy conditions, deductibles, limits, │ │
│ │ reinsurance structure) │ │
│ │ │ │
│ │ OUTPUTS: Insured loss for each event in the catalog, │ │
│ │ aggregated to portfolio level │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ OUTPUTS: │ │
│ │ • Event Loss Table — loss for each of 10,000+ events │ │
│ │ • EP Curve — exceedance probability for each loss level │ │
│ │ • AAL (Average Annual Loss) │ │
│ │ • PML (Probable Maximum Loss) at return periods │ │
│ │ • Standard Deviation and Coefficient of Variation │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
2. Monte Carlo Simulation Fundamentals
Monte Carlo simulation is the mathematical engine of catastrophe modelling. It generates thousands (or millions) of "synthetic years" of natural disaster events by randomly sampling from the statistical distributions that describe event frequency and severity. The law of large numbers ensures that the aggregate of these synthetic years converges to the "true" underlying risk distribution — even if the real-world historical record is too short to observe the most extreme events.
2.1 The Two Building Blocks — Frequency and Severity
Every Monte Carlo CAT model starts with two fundamental distributions:
- Event frequency — Poisson distribution: The number of cyclone events per year is modelled as a Poisson random variable with parameter λ (the average number of events per year). The Poisson distribution is used because events are assumed to occur independently of each other — one cyclone does not make another more or less likely (for well-separated locations).
- Event severity — Lognormal distribution: The intensity (wind speed, flood depth, earthquake magnitude) of each event follows a distribution that captures the right-skewed nature of natural disasters — most events are moderate, a few are extreme. The lognormal distribution is the most common choice because it captures this skew and is well-behaved mathematically.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Set random seed for reproducibility
np.random.seed(42)
# ==========================================
# FREQUENCY MODULE: Poisson Distribution
# ==========================================
LAMBDA = 1.8 # Average cyclones per year affecting the portfolio
# Generate 100 years of event counts
n_years = 100
yearly_event_counts = np.random.poisson(LAMBDA, n_years)
print("FREQUENCY MODULE — Poisson Distribution")
print(f"Average events/year (λ): {LAMBDA}")
print(f"Simulated {n_years} years:")
print(f" Years with 0 events: {(yearly_event_counts == 0).sum()} ({(yearly_event_counts == 0).mean()*100:.1f}%)")
print(f" Years with 1 event: {(yearly_event_counts == 1).sum()} ({(yearly_event_counts == 1).mean()*100:.1f}%)")
print(f" Years with 2 events: {(yearly_event_counts == 2).sum()} ({(yearly_event_counts == 2).mean()*100:.1f}%)")
print(f" Years with 3+ events:{(yearly_event_counts >= 3).sum()} ({(yearly_event_counts >= 3).mean()*100:.1f}%)")
print(f" Maximum events in a year: {yearly_event_counts.max()}")
# ==========================================
# SEVERITY MODULE: Lognormal Distribution
# ==========================================
MU = np.log(120) # Log of median wind speed (km/h)
SIGMA = 0.55 # Shape parameter — higher = fatter tail
# Generate severity for many events
n_events = 10000
wind_speeds = np.random.lognormal(MU, SIGMA, n_events)
print(f"\nSEVERITY MODULE — Lognormal Distribution")
print(f"Parameters: μ={MU:.2f}, σ={SIGMA}")
print(f"Simulated {n_events:,} events:")
print(f" Mean wind speed: {wind_speeds.mean():.0f} km/h")
print(f" Median wind speed: {np.median(wind_speeds):.0f} km/h")
print(f" 95th percentile: {np.percentile(wind_speeds, 95):.0f} km/h")
print(f" 99th percentile: {np.percentile(wind_speeds, 99):.0f} km/h")
print(f" Maximum: {wind_speeds.max():.0f} km/h")
2.2 Visualising the Distributions
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Left: Frequency histogram (Poisson)
ax = axes[0]
ax.hist(yearly_event_counts, bins=range(0, 8), color='#6c5ce7',
edgecolor='white', alpha=0.7, align='left', rwidth=0.7)
ax.set_xlabel('Number of Cyclone Events in a Year')
ax.set_ylabel('Number of Years (out of 100)')
ax.set_title('Event Frequency Distribution — Poisson(λ=1.8)', fontweight='bold')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Right: Severity histogram (Lognormal) with key percentiles
ax = axes[1]
ax.hist(wind_speeds, bins=60, color='#e17055', edgecolor='white', alpha=0.7)
ax.axvline(x=np.percentile(wind_speeds, 50), color='green', linestyle='--',
linewidth=1.5, label='Median (50th %ile)')
ax.axvline(x=np.percentile(wind_speeds, 95), color='orange', linestyle='--',
linewidth=1.5, label='95th %ile')
ax.axvline(x=np.percentile(wind_speeds, 99), color='red', linestyle='--',
linewidth=1.5, label='99th %ile')
ax.set_xlabel('Maximum Wind Speed (km/h)')
ax.set_ylabel('Number of Events')
ax.set_title('Event Severity Distribution — Lognormal(μ=4.79, σ=0.55)', fontweight='bold')
ax.legend()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()
3. Building a Complete CAT Model in Python
Now we combine the frequency and severity modules with a vulnerability function to build a complete CAT model that produces an Event Loss Table — the foundation of all CAT model outputs.
3.1 The CAT Model Simulation
# ==========================================
# COMPLETE CAT MODEL — TROPICAL CYCLONE
# ==========================================
# Parameters
LAMBDA = 1.8 # Average cyclones per year
MU = np.log(120) # Lognormal μ (median wind speed ~120 km/h)
SIGMA = 0.55 # Lognormal σ
TOTAL_INSURED = 5000e7 # Total insured portfolio value: ₹5,000 crore
N_SIMULATIONS = 10000 # Number of synthetic years to simulate
def cyclone_vulnerability(wind_speed_kmph):
"""
Vulnerability function: mean damage ratio given wind speed.
Based on typical cyclone vulnerability curves for residential/commercial
construction in India.
"""
if wind_speed_kmph < 50:
return 0.0
elif wind_speed_kmph < 90:
return 0.01 * (wind_speed_kmph - 50) / 40 # 0–1% damage
elif wind_speed_kmph < 120:
return 0.01 + 0.04 * (wind_speed_kmph - 90) / 30 # 1–5% damage
elif wind_speed_kmph < 160:
return 0.05 + 0.15 * (wind_speed_kmph - 120) / 40 # 5–20% damage
elif wind_speed_kmph < 200:
return 0.20 + 0.30 * (wind_speed_kmph - 160) / 40 # 20–50% damage
elif wind_speed_kmph < 260:
return 0.50 + 0.30 * (wind_speed_kmph - 200) / 60 # 50–80% damage
else:
return 0.80 + 0.20 * min((wind_speed_kmph - 260) / 100, 1) # 80–100% damage
# Run the simulation
np.random.seed(42)
annual_losses = []
all_events = []
for sim_year in range(N_SIMULATIONS):
# 1. Number of events this year
n_events = np.random.poisson(LAMBDA)
year_loss = 0
for _ in range(n_events):
# 2. Event severity (wind speed)
wind = np.random.lognormal(MU, SIGMA)
# 3. Vulnerability — mean damage ratio
mean_dr = cyclone_vulnerability(wind)
# 4. Stochastic uncertainty around the mean damage ratio (Beta distribution)
# This captures the fact that two identical buildings at the same wind speed
# may experience different damage due to construction quality, directionality, etc.
beta_param = mean_dr * 10 + 0.5
actual_dr = np.random.beta(
max(beta_param, 0.5),
max(10 - beta_param + 1, 0.5)
) if mean_dr > 0 and mean_dr < 1 else mean_dr
# 5. Financial module — apply deductible and limit
# Portfolio deductible: 1% of total insured
# Portfolio limit: 100% (simplified for this example)
deductible = 0.01 * TOTAL_INSURED
loss_before_deductible = actual_dr * TOTAL_INSURED
insured_loss = max(0, loss_before_deductible - deductible)
year_loss += insured_loss
all_events.append({
'sim_year': sim_year,
'wind_speed': wind,
'damage_ratio': actual_dr,
'insured_loss': insured_loss
})
annual_losses.append(year_loss)
annual_losses = np.array(annual_losses)
event_df = pd.DataFrame(all_events)
print("=" * 60)
print("CAT MODEL SIMULATION — 10,000 YEARS")
print("=" * 60)
print(f"Portfolio total insured value: ₹{TOTAL_INSURED/1e7:.0f} Cr")
print(f"Years simulated: {N_SIMULATIONS:,}")
print(f"Total events generated: {len(event_df):,}")
print(f"Years with at least 1 event: {(annual_losses > 0).sum():,} ({(annual_losses > 0).mean()*100:.1f}%)")
print(f"Average events per year: {len(event_df)/N_SIMULATIONS:.2f}")
print(f"Max events in a year: {(event_df.groupby('sim_year').size().max())}")
print(f"\nPortfolio loss statistics (annual):")
print(f" Average Annual Loss (AAL): ₹{annual_losses.mean()/1e7:.2f} Cr")
print(f" Standard Deviation: ₹{annual_losses.std()/1e7:.2f} Cr")
print(f" Coefficient of Variation: {annual_losses.std()/annual_losses.mean():.2f}")
print(f" Maximum annual loss: ₹{annual_losses.max()/1e7:.2f} Cr")
print(f" Minimum non-zero loss: ₹{annual_losses[annual_losses > 0].min()/1e7:.2f} Cr")
4. Key CAT Model Metrics
The Monte Carlo simulation produces an Event Loss Table — 10,000 rows, each representing the annual portfolio loss for one synthetic year. From this table, we calculate the metrics that drive reinsurance decisions, pricing, and risk management.
4.1 The Three Core Metrics
Average Annual Loss (AAL): The mean of all 10,000 simulated annual losses. This is the "pure premium" for catastrophe risk — the amount the insurer should expect to pay out on average each year over the long run. Reinsurers use AAL as the basis for pricing catastrophe reinsurance treaties.
Standard Deviation of Annual Loss: The volatility of annual losses around the mean. A high standard deviation relative to the AAL (coefficient of variation > 1.5) indicates that catastrophe risk is "spiky" — most years have no losses or small losses, but occasional years have enormous losses. This spikiness is what makes catastrophe risk fundamentally different from attritional claims (motor accidents, health claims) which are high-frequency, low-severity, and stable.
Probable Maximum Loss (PML): The loss amount at a specific return period. The "1-in-100 year PML" (also called the 100-year PML, or the 1% exceedance probability loss) is the loss amount that has a 1% probability of being exceeded in any given year. This is the most important metric in catastrophe risk management — it determines the amount of capital an insurer must hold and the limit of reinsurance they must purchase.
# ==========================================
# CAT MODEL METRICS
# ==========================================
# Sort annual losses for exceedance probability calculation
sorted_losses = np.sort(annual_losses)
# Key return periods
return_periods = [5, 10, 20, 50, 100, 250, 500, 1000]
results = []
for rp in return_periods:
# Exceedance probability = 1/return_period
ep = 1.0 / rp
# Index in sorted array: (1 - ep) * N_SIMULATIONS
idx = min(int((1 - ep) * N_SIMULATIONS), N_SIMULATIONS - 1)
pml = sorted_losses[idx]
results.append({'return_period': rp, 'ep': ep, 'pml': pml})
pml_df = pd.DataFrame(results)
print("=" * 60)
print("PROBABLE MAXIMUM LOSS (PML) BY RETURN PERIOD")
print("=" * 60)
print(f"Portfolio Total Insured Value: ₹{TOTAL_INSURED/1e7:.0f} Cr")
print(f"{'Return Period':15s} {'Exceedance':15s} {'PML':15s} {'% of TIV':12s}")
print("-" * 57)
for _, row in pml_df.iterrows():
print(f"{row['return_period']:>3d}-year {row['ep']*100:.2f}% ₹{row['pml']/1e7:>5.1f} Cr {row['pml']/TOTAL_INSURED*100:>4.2f}%")
# AAL comparison
aal = annual_losses.mean()
pml_100 = pml_df[pml_df['return_period'] == 100]['pml'].values[0]
pml_250 = pml_df[pml_df['return_period'] == 250]['pml'].values[0]
print(f"\n{'─' * 57}")
print(f"AAL: ₹{aal/1e7:.2f} Cr")
print(f"100-year PML: ₹{pml_100/1e7:.1f} Cr")
print(f"100yr PML / AAL ratio: {pml_100/aal:.1f}x")
print(f"250-year PML: ₹{pml_250/1e7:.1f} Cr")
print(f"250yr PML / AAL ratio: {pml_250/aal:.1f}x")
print(f"\nInterpretation:")
print(f"The 1-in-100 year PML of ₹{pml_100/1e7:.1f} Cr means there is a 1% chance")
print(f"in any given year that cyclone losses will exceed ₹{pml_100/1e7:.1f} Cr.")
print(f"The 250-year PML of ₹{pml_250/1e7:.1f} Cr means there is a 0.4% chance")
print(f"that losses will exceed that amount. The ratio of PML100 to AAL of")
print(f"{pml_100/aal:.0f}x reflects the 'spikiness' of cyclone risk.")
5. Interpreting the Exceedance Probability (EP) Curve
The Exceedance Probability (EP) curve is the single most important visual output of a catastrophe model. It plots the relationship between loss amount (x-axis) and the probability of exceeding that loss in any given year (y-axis). The EP curve summarises the entire 10,000-year simulation in a single, boardroom-friendly chart.
5.1 Building and Reading the EP Curve
# Build full EP curve
ep_levels = np.linspace(0.001, 0.999, 1000)
loss_at_ep = np.percentile(annual_losses, (1 - ep_levels) * 100)
fig, ax = plt.subplots(figsize=(12, 7))
# Main EP curve
ax.plot(loss_at_ep / 1e7, ep_levels, color='#6c5ce7', linewidth=2.5, label='EP Curve')
# Shade confidence interval (simplified — using bootstrap)
# Upper and lower bounds represent ±1 standard error
ax.fill_between(loss_at_ep / 1e7,
np.clip(ep_levels + 0.03, 0, 1),
np.clip(ep_levels - 0.03, 0, 1),
alpha=0.15, color='#6c5ce7', label='±1 SE Confidence Band')
# Annotate key return periods
key_rps = [5, 10, 20, 50, 100, 250, 500]
for rp in key_rps:
ep = 1.0 / rp
idx = np.argmin(np.abs(ep_levels - ep))
loss_val = loss_at_ep[idx] / 1e7
ax.plot(loss_val, ep, 'o', color='#e17055', markersize=8)
ax.annotate(f'{rp}-year\n₹{loss_val:.1f}Cr',
xy=(loss_val, ep), xytext=(loss_val + 5, ep + 0.01),
fontsize=9, fontweight='bold', color='#e17055',
arrowprops=dict(arrowstyle='->', color='#e17055'))
# AAL line
aal_ep = (annual_losses >= aal).mean()
ax.axvline(x=aal / 1e7, color='green', linestyle='--', linewidth=1.5,
label=f'AAL = ₹{aal/1e7:.2f} Cr')
ax.set_xlabel('Annual Portfolio Loss (₹ Crores)')
ax.set_ylabel('Exceedance Probability (EP)')
ax.set_title('Exceedance Probability (EP) Curve — Cyclone CAT Model', fontweight='bold')
ax.legend(loc='upper right')
ax.set_xlim(0, loss_at_ep[-2] / 1e7 * 1.05)
ax.set_ylim(0, 1.05)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Add secondary x-axis for return period
ax_sec = ax.secondary_xaxis('top')
sec_ticks = [round(1/ep_levels[len(loss_at_ep)//2]), 100, 50, 20, 10, 5, 2]
ax_sec.set_xticks([np.percentile(annual_losses, (1 - 1/t)*100) / 1e7 for t in sec_ticks])
ax_sec.set_xticklabels([str(t) for t in sec_ticks])
ax_sec.set_xlabel('Return Period (Years)', fontsize=10)
plt.tight_layout()
plt.show()
print("HOW TO READ THE EP CURVE:")
print("────────────────────────")
print("1. The curve shows the probability that losses will EXCEED a given amount.")
print("2. The AAL is the 'average' loss — but note that most years have losses")
print(" BELOW the AAL because of the right-skewed distribution.")
print("3. The 1-in-100 year PML (₹{:.1f}Cr) is the loss with 1% exceedance probability.".format(pml_100/1e7))
print("4. The area under the EP curve equals the AAL — this is a key consistency check.")
print("5. The steepness of the curve tells you about risk concentration:")
print(" • Steep drop → losses are concentrated in a few events")
print(" • Gradual slope → losses are spread across many events")
print("6. The confidence band shows the uncertainty around the EP estimate.")
print(" Wider bands = more uncertain model = need more conservative risk management.")
5.2 The "Layer" View — Understanding Reinsurance Attachment
# Visualising reinsurance layers on the EP curve
# Layer structure example:
attachment = 75e7 # ₹75 Cr retention (insurer retains first ₹75 Cr of losses)
layer_limit = 150e7 # ₹150 Cr layer (reinsurer covers ₹75–₹225 Cr)
total_limit = 225e7 # Total limit purchased
fig, ax = plt.subplots(figsize=(12, 5))
# Histogram of annual losses
counts, bins, _ = ax.hist(annual_losses / 1e7, bins=80, color='#a29bfe',
edgecolor='white', alpha=0.6)
# Add shaded layers
ax.axvspan(0, attachment / 1e7, alpha=0.3, color='green', label=f'Insurer Retention: ₹0–{attachment/1e7:.0f} Cr')
ax.axvspan(attachment / 1e7, total_limit / 1e7, alpha=0.3, color='orange',
label=f'Reinsurance Layer: ₹{attachment/1e7:.0f}–{total_limit/1e7:.0f} Cr')
ax.axvspan(total_limit / 1e7, annual_losses.max() / 1e7, alpha=0.3, color='red',
label=f'Uncovered / Retrocession: > ₹{total_limit/1e7:.0f} Cr')
ax.set_xlabel('Annual Portfolio Loss (₹ Crores)')
ax.set_ylabel('Frequency (out of 10,000 years)')
ax.set_title('Distribution of Annual Losses — Reinsurance Layer Structure', fontweight='bold')
ax.legend()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()
# Calculate expected losses by layer
retention_losses = np.minimum(annual_losses, attachment)
layer_losses = np.maximum(0, np.minimum(annual_losses - attachment, layer_limit))
excess_losses = np.maximum(0, annual_losses - total_limit)
print("EXPECTED LOSS BY LAYER")
print("=" * 45)
print(f"Insurer retention: ₹{retention_losses.mean()/1e7:.2f} Cr/year")
print(f"Reinsurance layer payout: ₹{layer_losses.mean()/1e7:.2f} Cr/year")
print(f"Excess (uncovered): ₹{excess_losses.mean()/1e7:.2f} Cr/year")
print(f"Total (should = AAL): ₹{(retention_losses.mean() + layer_losses.mean() + excess_losses.mean())/1e7:.2f} Cr/year")
# Probability of attaching reinsurance
attach_prob = (annual_losses > attachment).mean()
exhaust_prob = (annual_losses > total_limit).mean()
print(f"\nProbability of attaching reinsurance layer: {attach_prob*100:.1f}%")
print(f"Probability of exhausting reinsurance layer: {exhaust_prob*100:.1f}%")
print(f"Expected number of years between attachments: {1/attach_prob:.0f} years")
6. Reinsurance Decision-Making Using CAT Models
The CAT model's output — the EP curve and the PML estimates — directly drives the most important financial decision an insurer makes: how much reinsurance to buy, and how much to pay for it. The reinsurance programme structure defines the risk transfer relationship between the insurer (the "cedent") and the reinsurer.
6.1 Key Reinsurance Structures
| Structure | How It Works | Best For | CAT Model Input |
|---|---|---|---|
| Excess of Loss (XoL) per Event | Reinsurer pays losses exceeding a threshold for a single event. Example: ₹50 Cr excess of ₹50 Cr — the reinsurer covers losses between ₹50 Cr and ₹100 Cr from a single cyclone. | Protecting against a single large event. Standard structure for cyclone/earthquake CAT cover. | Per-event loss distribution from the CAT model — what is the 1-in-100 year event loss? |
| Excess of Loss (XoL) Aggregate | Reinsurer pays cumulative annual losses exceeding a threshold — covers the aggregation of multiple smaller events in a single year. | Protecting against an active season with many moderate events (multiple cyclones, flood season). | Annual aggregate loss distribution — accounts for multiple events per year. |
| Catastrophe Bond (Cat Bond) | Insurer issues a bond to capital market investors. If a predefined catastrophe trigger occurs (parametric or modelled), the bond principal is partially or fully forgiven and paid to the insurer. If not, investors get their principal back plus interest. | Transferring peak tail risk (200-year+ events) to capital markets, reducing reliance on traditional reinsurance. | EP curve at very low exceedance probabilities (0.5%–0.2%) — the "tail" of the distribution. |
| Quota Share | Reinsurer takes a fixed percentage of every policy in a defined portfolio (e.g., 30% of all cyclone-exposed property policies). Shares premiums and losses in the same proportion. | Reducing overall portfolio volatility, not just peak risk. Often combined with XoL for a complete programme. | Full loss distribution — all events, not just the tail. |
6.2 The Reinsurance Pricing Model
# Reinsurance pricing based on CAT model output
# Current portfolio CAT model results
aal = annual_losses.mean()
pml_100 = np.percentile(annual_losses, 99) # 1% EP
pml_250 = np.percentile(annual_losses, 99.6) # 0.4% EP
# Proposed XoL structure
retention = 75e7 # ₹75 Cr retention
limit = 150e7 # ₹150 Cr layer (attaching at ₹75 Cr, exhausting at ₹225 Cr)
# Burning cost = expected loss to the layer
layer_losses = np.maximum(0, np.minimum(annual_losses - retention, limit))
burning_cost = layer_losses.mean()
# Reinsurance premium components
risk_loading = burning_cost * 0.20 # 20% risk load
expense_loading = burning_cost * 0.15 # 15% expense load (brokerage, administration)
capital_charge = limit * 0.03 # 3% charge on limit (cost of capital)
profit_loading = burning_cost * 0.10 # 10% profit margin (reinsurer target)
total_reinsurance_premium = burning_cost + risk_loading + expense_loading + capital_charge + profit_loading
print("=" * 60)
print("REINSURANCE PRICING — XoL STRUCTURE")
print("=" * 60)
print(f"Portfolio metrics:")
print(f" AAL: ₹{aal/1e7:.2f} Cr")
print(f" 100-year PML: ₹{pml_100/1e7:.1f} Cr")
print(f" 250-year PML: ₹{pml_250/1e7:.1f} Cr")
print(f"\nProposed XoL structure:")
print(f" Retention: ₹{retention/1e7:.0f} Cr")
print(f" Limit: ₹{limit/1e7:.0f} Cr")
print(f" Layer: ₹{retention/1e7:.0f} Cr xs ₹{(retention+limit)/1e7:.0f} Cr")
print(f"\nPremium calculation:")
print(f" Burning cost (expected loss): ₹{burning_cost/1e7:.2f} Cr")
print(f" Risk loading (20%): ₹{risk_loading/1e7:.2f} Cr")
print(f" Expense loading (15%): ₹{expense_loading/1e7:.2f} Cr")
print(f" Capital charge (3% of limit): ₹{capital_charge/1e7:.2f} Cr")
print(f" Profit loading (10%): ₹{profit_loading/1e7:.2f} Cr")
print(f" ─────────────────────────────────────────────")
print(f" Total reinsurance premium: ₹{total_reinsurance_premium/1e7:.2f} Cr")
print(f"\nPremium as % of limit: {total_reinsurance_premium/limit*100:.1f}%")
print(f"Premium as % of AAL: {total_reinsurance_premium/aal*100:.0f}%")
print(f"Rate-on-line: {total_reinsurance_premium/limit*100:.1f}%")
# Compare to market benchmark
print(f"\n{'─' * 60}")
print(f"MARKET COMPARISON")
market_rate = 0.05 # 5% rate-on-line for mid-layer cyclone cover
market_premium = limit * market_rate
print(f"Market benchmark rate: {market_rate*100:.0f}% = ₹{market_premium/1e7:.2f} Cr")
print(f"Our modelled rate: {total_reinsurance_premium/limit*100:.1f}% = ₹{total_reinsurance_premium/1e7:.2f} Cr")
if total_reinsurance_premium < market_premium:
print(f"✓ Our modelled premium is BELOW market — evidence-based negotiation position.")
else:
print(f"△ Our modelled premium is ABOVE market — our portfolio may be riskier than average.")
7. Catastrophe Model Limitations
CAT models are powerful tools — but they are not crystal balls. Every CAT model has limitations that must be understood before the outputs are used for decision-making. An insurer that treats CAT model outputs as precise estimates rather than ranges of uncertainty is making a dangerous mistake.
7.1 The Five Critical Limitations
| Limitation | Description | Impact | Mitigation |
|---|---|---|---|
| 1. Climate non-stationarity | CAT models are calibrated on historical data (typically 30–100 years of observations). But climate change is shifting the frequency and severity distributions of extreme events. A model calibrated on 1980–2020 data will underestimate risk for 2020–2060. | PML estimates may be too low. The 100-year PML from a historical model may be the 50-year PML under climate change — meaning the insurer is holding half the capital it needs. | Use "climate-conditioned" CAT models that incorporate climate projections. Apply safety loadings to PML estimates for climate-sensitive perils (cyclone, flood, wildfire). Stress-test the model with ±20% frequency and ±10% severity perturbations. |
| 2. Model uncertainty | Different CAT model vendors (RMS, AIR, JRC) produce materially different loss estimates for the same portfolio. The choice of vulnerability function, event set, and financial module assumptions can produce PML100 estimates that vary by ±30% or more across models. | An insurer using a single model may have a false sense of precision. The "true" PML could be 30% higher or lower than what the model says. | Use multiple models (at least 2–3). Use the range of model outputs — the average "model ensemble" PML is more reliable than any single model. Inform the board that PML is a range, not a single number. |
| 3. Exposure data quality | CAT model output is only as good as the exposure data input. If policies are not accurately geo-coded (most are not — addresses may be incomplete or incorrect), the model's wind speed at location calculation is wrong, and the loss estimate is wrong. | Exposure data errors can produce PML errors of 20–50%. Incomplete geo-coding is the single largest source of error in most insurers' CAT models. | Invest in exposure data quality: accurate geo-coding of all policy addresses, validation against known building stock characteristics, and regular data audits. A good CAT model with bad data produces worse results than a bad CAT model with good data. |
| 4. Secondary perils | Most CAT models focus on "primary" perils (cyclone wind, earthquake shaking). But the largest losses often come from "secondary" perils — storm surge from cyclones, liquefaction from earthquakes, post-fire landslides, flood after cyclone. | Losses from secondary perils are often unmodelled or under-modelled, meaning the true 100-year PML may be significantly higher than the model says. | Ensure the CAT model includes secondary perils (many vendors now offer storm surge modules). If not, apply a secondary peril loading (typically 15–25% of the primary PML). |
| 5. Correlation across policies | CAT models assume that all policies in the same geographic zone are perfectly correlated (they all experience the same wind speed from the same cyclone). This is a simplification — actual correlation is less than perfect but cannot be accurately measured. | Perfect correlation assumption overstates PML (it assumes every building in the zone suffers the same wind speed, which is not true). But the degree of overstatement is unknown — leading to another layer of uncertainty. | Apply "correlation uncertainty" as a separate component in the capital model. Do not assume that the CAT model's perfect-correlation assumption is conservative — it may mask important differences in vulnerability that affect loss aggregation. |
Hands-On Project: Build a CAT Model and Design a Reinsurance Programme
You are the catastrophe risk analyst at "CoastalGuard Insurance" (from Session 18). The company has a property insurance portfolio concentrated in India's cyclone-prone coastal regions, with a total insured value of ₹8,000 crore. Your CEO needs a catastrophe risk assessment and a recommendation for the annual reinsurance programme renewal.
Steps
- Build the CAT model: Create a Monte Carlo simulation with 10,000 years for cyclone risk. Parameters: λ=1.5 cyclones/year, lognormal wind speeds (μ=4.7, σ=0.55), total insured portfolio = ₹8,000 crore, portfolio-level deductible = 0.5% of TIV. Use the same vulnerability function from Section 3.1.
- Calculate key metrics: AAL, standard deviation, PML at 10, 20, 50, 100, 250, and 500-year return periods. Plot the EP curve. Which PML values would you present to the board?
- Sensitivity analysis: Rerun the model with: (a) λ increased by 20% (to 1.8), (b) vulnerability increased by 15% (all damage ratios × 1.15). How much does the 100-year PML change? Which sensitivity matters more?
- Reinsurance design: Design an XoL reinsurance programme. Choose a retention level and a layer limit. Use the EP curve to justify your choices. Calculate the burning cost and estimate the reinsurance premium.
- Model limitations assessment: Which two limitations from Section 7 are most relevant to your portfolio? How would you mitigate them? How would you communicate PML uncertainty to the board?
- Write a 500-word CAT risk report to the Chief Risk Officer covering: (a) Portfolio overview and CAT model setup, (b) Key risk metrics (AAL, PML100, PML250), (c) Reinsurance programme recommendation with rationale, (d) Model uncertainty and limitations, (e) Recommended next steps for improving the CAT modelling capability.
View Solution / Walkthrough
CAT Risk Report (Sample)
To: Chief Risk Officer, CoastalGuard Insurance
From: CAT Risk Analyst
Subject: Cyclone Catastrophe Risk Assessment — Portfolio & Reinsurance Recommendation
Portfolio and Model Setup:
The property portfolio has a total insured value (TIV) of ₹8,000 crore, concentrated in cyclone-prone coastal districts of Odisha, Andhra Pradesh, and Gujarat. The CAT model uses a Monte Carlo simulation of 10,000 synthetic years with a Poisson frequency (λ=1.5 cyclones/year affecting the portfolio) and lognormal severity distribution calibrated to historical cyclone wind speeds in the Bay of Bengal and Arabian Sea. The vulnerability function is based on published engineering studies of Indian residential and commercial construction under cyclone wind loading, with a Beta-distributed uncertainty term. The portfolio-level deductible of 0.5% of TIV (₹40 crore) reflects the average policy deductible across the portfolio.
Key Risk Metrics:
The Average Annual Loss (AAL) is ₹79.2 crore (0.99% of TIV). The portfolio is "spiky" — many years have zero or low cyclone losses, but tail risk is significant. The 100-year PML is ₹259 crore (3.2% of TIV, 3.3× AAL), and the 250-year PML is ₹394 crore (4.9% of TIV). These PML estimates are within the range expected for a coastal-concentrated Indian property portfolio but warrant careful reinsurance structuring. The ratio of PML100 to AAL (3.3×) indicates moderate "spikiness" — lower than, for example, a Florida hurricane portfolio (PML100/AAL = 5–7×) because cyclone frequency in the Bay of Bengal is higher (distributing losses across more years) and building vulnerability varies across the portfolio.
Reinsurance Programme Recommendation:
We recommend an Excess of Loss (XoL) structure with a ₹125 crore retention and a ₹225 crore layer (attaching at ₹125 crore, exhausting at ₹350 crore). The retention is set at approximately 50% of the 100-year PML — conservative enough to retain the "working layer" of frequent moderate events while transferring the tail risk. The layer limit of ₹225 crore caps the portfolio's net exposure at ₹350 crore, which is approximately 90% of the PML250 and 135% of the PML100, providing adequate headroom for the 250-year event. The modelled burning cost of the layer is ₹18.6 crore per year. Applying a total loading of 55% (risk, expense, capital, profit) produces an estimated annual reinsurance premium of ₹28.8 crore — a 12.8% rate-on-line, which is competitive with the current market benchmark of 14–16% for similar coastal Indian portfolios. We recommend entering renewal negotiations with this modelled rate as the target.
Model Uncertainty and Limitations:
The two most critical limitations for this portfolio are climate non-stationarity and exposure data quality. On climate non-stationarity: sea surface temperatures in the Bay of Bengal have been rising, and the proportion of cyclones reaching Category 4–5 intensity is increasing. Our model, calibrated on 1990–2020 data, may underestimate the current risk. We recommend stress-testing the model with λ=1.8 (+20% frequency) which increases the PML100 by approximately 17% to ₹303 crore — a number that would push the recommended retention higher. On exposure data quality: approximately 35% of our property policies in coastal zones use Pincode-level geo-coding rather than precise coordinates. This introduces spatial uncertainty of 5–15 km in the wind speed assigned to each policy. We are initiating a data quality project to improve geo-coding accuracy to address this.
Recommended Next Steps:
(1) Complete the exposure data quality project within 6 months — this is the highest-ROI investment in CAT modelling accuracy. (2) Implement a multi-model framework: complement our internal model with one commercial vendor model (to be selected via RFP in Q3) and compare PML estimates across models. (3) Develop a climate-conditioned scenario (λ+20%, severity+10%) to be used for internal capital planning, even if the primary reinsurance purchase is based on the base model. (4) Present the PML as a range to the board — "₹260 crore ± 35%" — rather than a single number. The word "approximately" should precede every PML number in board reporting.
Key Takeaways
A catastrophe model has three connected modules — Hazard (where, how often, how severe), Vulnerability (damage given intensity), and Financial (insured loss given damage). CAT models compensate for the data gap between short historical records and long return periods by generating millions of synthetic events.
Monte Carlo simulation generates synthetic years by combining a Poisson distribution for event frequency with a Lognormal distribution for event severity. The vulnerability function translates physical intensity (wind speed) into financial loss through damage ratios and policy conditions.
The three core CAT metrics are AAL (average annual loss — the "pure premium"), standard deviation (volatility), and PML (probable maximum loss — the loss at a given return period). The 100-year PML is the single most important metric for reinsurance and capital planning.
The EP curve plots exceedance probability against loss amount. The histogram of annual losses with reinsurance layers overlaid is more intuitive for non-technical audiences than the EP curve alone. Start with the histogram, then show the EP curve as a summary.
CAT models have five critical limitations: climate non-stationarity, model uncertainty (vendor divergence), exposure data quality, secondary perils, and correlation assumptions. PML should always be presented as a range, not a single number — "₹260 crore ± 35%" is honest risk communication.
Test Your Understanding
1. In a catastrophe model, the Hazard Module determines:
2. A CAT model produces an AAL of ₹30 crore and a 100-year PML of ₹180 crore. The ratio of PML100 to AAL is 6.0. This ratio indicates:
3. A 1-in-100 year PML of ₹200 crore means:
4. An insurer's CAT model uses a single commercial vendor, and the exposure data is 50% Pincode-geocoded (not precise coordinates). The board is told the 100-year PML is "exactly ₹175 crore." The MOST important risk management action is:
5. An insurer's XoL reinsurance layer has attachment of ₹100 crore and limit of ₹200 crore. The portfolio's 100-year PML is ₹250 crore. This means: