Getting Started with PAL
This tutorial introduces the core concepts of the Proteus Actuarial Library through a short, end-to-end example.
Setup
import numpy as np
from pal import config, copulas, distributions, set_random_seed
config.n_sims = 10_000
set_random_seed(42)
config.n_sims controls how many Monte Carlo simulations are generated
globally. set_random_seed makes results reproducible.
Generating Stochastic Variables
Create a loss variable from a LogNormal distribution:
loss = distributions.LogNormal(mu=14, sigma=0.5).generate()
This returns a StochasticScalar — a vector of 10,000 simulated values.
You can inspect it immediately:
loss.mean() # => 1,358,389
loss.std() # => 732,520
np.median(loss.values) # => 1,200,047
np.percentile(loss.values, 99.5) # => 4,443,841
Arithmetic on Stochastic Variables
Standard arithmetic works element-wise across simulations:
expenses = loss * 0.10 # 10% expense loading
total = loss + expenses # gross = loss + expenses
total.mean() # => 1,494,228
Because expenses and total are derived from loss, they
automatically join the same coupling group — if loss is later
reordered by a copula, expenses and total are reordered in
lockstep.
Combining Multiple Lines of Business
set_random_seed(42)
motor = distributions.LogNormal(mu=14, sigma=0.5).generate()
prop = distributions.LogNormal(mu=15, sigma=0.8).generate()
combined = motor + prop
Motor mean: 1,358,389
Property mean: 4,545,084
Combined mean: 5,903,473
Combined std: 4,399,880
The combined standard deviation reflects the fact that motor and
prop are independent — their peaks and troughs don’t coincide.
Adding Dependencies with Copulas
In reality, lines of business are often correlated (e.g. through economic conditions). Use a copula to introduce dependence:
set_random_seed(42)
motor = distributions.LogNormal(mu=14, sigma=0.5).generate()
prop = distributions.LogNormal(mu=15, sigma=0.8).generate()
copulas.GaussianCopula([[1, 0.5], [0.5, 1]]).apply([motor, prop])
combined = motor + prop
Combined mean: 5,903,473 (unchanged)
Combined std: 4,694,549 (higher — dependence adds volatility)
Combined 99.5th: 29,435,135
The mean is unchanged because copulas only reorder simulations — they don’t change the individual values. But the standard deviation increases because bad years now tend to coincide across both lines.
See the Coupling Groups, Copulas and Variable Reordering tutorial for a deeper treatment.
Frequency-Severity Models
For compound distributions (random number of random-sized claims):
from pal.frequency_severity import FrequencySeverityModel
set_random_seed(42)
model = FrequencySeverityModel(
freq_dist=distributions.Poisson(mean=100),
sev_dist=distributions.LogNormal(mu=10, sigma=1.5),
)
events = model.generate()
This generates ~1,000,000 individual events across 10,000 simulations. Aggregate them to get total loss per simulation:
agg = events.aggregate()
Events generated: 1,000,131
Aggregate mean: 6,783,564
Aggregate 99.5th: 15,098,023
See the Frequency-Severity Modelling tutorial for more detail.
Visualisation
Every StochasticScalar has a show_cdf() method that displays an
interactive CDF plot:
agg.show_cdf("Aggregate Loss")
Set the environment variable PAL_SUPPRESS_PLOTS=true to disable
plot display in headless environments.
Configuration Summary
Setting |
Default |
Description |
|---|---|---|
|
100,000 |
Number of Monte Carlo simulations |
|
|
Random number generator |
|
— |
Set seed for reproducibility |
|
unset |
Set to |
|
unset |
Set to |
Next Steps
Distributions Guide — choosing and fitting distributions
Frequency-Severity Modelling — compound models and event-level analysis
Coupling Groups, Copulas and Variable Reordering — dependency structures
Pricing an XoL Reinsurance Program — layers, towers and reinstatements