Rslife
A comprehensive Rust library for actuarial mortality table calculations and life insurance mathematics
Install / Use
/learn @hnlearndev/RslifeREADME
| Rust Version | Build Status | Test Status |
|:------------:|:------------:|:-----------:|
| Stable | |
|
| 1.89.0 |
|
|
| Nightly |
|
|
A comprehensive Rust library for actuarial mortality table calculations and life insurance mathematics, featuring an elegant builder pattern that makes complex actuarial calculations intuitive and type-safe.
Why RSLife?
🚀 Performance & Memory Efficiency:
- Leveraging Rust's zero-cost abstractions for maximum performance
- Polars integration for efficient DataFrame operations with zero-copy optimization
- Minimal memory allocation with smart data reuse and lazy evaluation
- Compile-time optimizations eliminate runtime overhead
🎯 Developer Experience:
- Intuitive Builder Pattern: Only specify parameters you need, no confusing parameter lists. RSLife ensures that the low level interfaces are even more approachable than the high level ones.
- Type Safety: Compile-time validation prevents common actuarial calculation errors
- Auto-Completion: IDEs provide intelligent suggestions for all parameters
- Self-Documenting: Parameter names make code intent crystal clear
- Cross-Field Validation: Parameter combinations validated automatically
📊 Intelligent Data Processing:
- Universal Input: DataFrames, XLSX/ODS files, and loading directly from Society of Actuary (US) and Institute and Faculty of Actuaries (UK) Mortality Database with automatic format detection
- Format Agnostic: Seamlessly detects
qxrates orlxsurvivor functions without manual specification - Smart Table Recognition: Automatically determines ultimate vs select mortality tables
- Validation Built-In: Comprehensive data integrity checks prevent runtime errors before calculations
- Select & Ultimate: Full support for both table types with automatic recognition
🔧 Production Ready:
- Complete Actuarial Coverage: Life insurance, annuities,survival functions and commutations with standard notation
- Multiple Assumptions: Uniform Death Distribution (UDD), Constant Force of Mortality (CFM), and Hyperbolic (HPB) methods for fractional age calculations
- Multiple Parametric Life Table Models: Constant Force Law, Gompertz, and Makeham, Weibull etc...
- Consistent API: All functions use the same parameter structure with builder pattern
- Battle-Tested: Validated against standard actuarial references from SOA and IFOA most trusted materials.
- Error Handling: Clear, actionable error messages for debugging
Quick Start
Add the crate dependency
cargo add rslife
Or add this to your Cargo.toml:
[dependencies]
rslife = "0.2.8"
The crate is designed with three main layers to make actuarial computations convenient (more on architecture from Wiki), as illustrated below:
use rslife::prelude::*;
fn main() -> RSLifeResult<()> {
// ========= FIRST LAYER - MORTALILITY DATA LOAD=========
// Load mortality data
// This seperation layer consists of multiple methods with flexibility at user hand to formulate the mortality or morbidity data
let data = MortData::from_ifoa_url_id("AM92")?;
// ========= SECOND LAYER - MORTALILITY TABLE CONFIGURATION =========
// Construct Mortality Table Config
// This layer is more rigid but still allows some configuration to mortality table
let mt = MortTableConfig::builder()
.data(data)
.radix(100_000) // Radix of 100k instead of default 10k
.pct(1.5) // 150% mortality rate instead of default 100%
.assumption(AssumptionEnum::CFM) // CFM assumption instead of default UDD assumtpion
.build()?;
// ========= THIRD LAYER - CALCULATIONS =========
// New builder pattern for actuarial calculations!
// This is the layer to perform calculation. Variables are only declared when needed - Consistent with actuarial notation principle.
let fractional_age_time_survival_rate = tpx()
.mt(&mt)
.x(35.5)
.t(5.8)
.entry_age(33)
.call()?;
let life_annuity = aax()
.mt(&mt)
.i(0.03)
.x(65)
.m(12) // monthly payable m=12
.call()?;
let deferred_term = Ax1n()
.mt(&mt)
.i(0.03)
.x(35)
.n(15)
.entry_age(34) // Entry age for selected effect - duration
.t(5) // Deferred 5 years
.call()?;
Ok(())
}
Data sources - Layer 1 in zoom
RSLife supports flexible mortality data input with automatic qx/lx detection.
Detail guide can be found on project wiki
An example of parametric life table model
// Parametric life table model
let makeham_model_data = MortData::from_Makeham_law()
.A(0.00022)
.B(2.7e-6)
.C(1.124)
.start_age(20)
.call()?;
Life table can also be formulated from dataframe
// DataFrames - mortality rates or survivor functions
// qx data
let df_qx = df! {
"age" => [25_u32, 26, 27],
"qx" => [0.001_f64, 0.0012, 0.0015],
}?;
let data_from_df_with_qx = MortData::from_df(df_qx)?;
// lx data
let df_lx = df! {
"age" => [25_u32, 26.0, 27.0],
"lx" => [100000.0_f64, 99900.0, 99780.0],
}?;
let data_from_df_with_lx = MortData::from_df(df_lx)?;
// Macro to directly form MortData
// This is equivalent to forming dataframe then using from_df method
let data_from_macro = mddf! {
"age" => [25_u32, 26, 27],
"qx" => [0.001_f64, 0.0012, 0.0015],
}
There are various other methods to formulate life table. For examples, from spreadsheets
// Custom data from spreadsheet XLSX
let data_from_xlsx = MortData::from_xlsx("data/mortality.xlsx", "select")?;
// Custom data from spreadsheet ODS
let data_from_ods = MortData::from_ods("data/mortality.ods", "select")?;
Direct ingestion from SOA, IFOA and Australian Government Actuary mortality and morbidity database
More direct API are coming in the next releases. Please feel free to suggest your favorite database.
// ELT No.15 Female
let data_from_soa = MortData::from_soa_url_id(1704)?;
// AM92 Selected Mortality Table
let data_from_ifoa = MortData::from_ifoa_url_id("AM92")?;
// Male mortality rate in 2020-2022
let data_from_aga = MortData::from_aus_gov_act("Male", "2020-22")?;
The Builder Pattern Advantage - IMMERSE in C4 principles
RSLife is designed to deliver actuarial developer experience founded on 💥 C4 💥 pillars - Clear, Concise, Coherent and Comprehensive, letting you IMMERSE yourselves in what truly matter for the core actuarial computation.
- 🎯 Intentional: Only specify parameters that matter for each calculation
- 🗒️ Manageable: Avoid clutter from declaring all parameters
- 🔧 Maintainable: Adding new parameters doesn't break existing code
- ⚡ Efficient: Automatic cross-field validation catches errors early
- 📖 Readable: Self-documenting code that's easy to understand
- 🔒 Safe: Compile-time validation prevents parameter mistakes
- 🧁 Effortless: Capable to construct complex calculations with minial code
vs. Traditional Approaches:
// ❌ Other libraries: **verbose** structs, need to declare all parameters, easy to mess up order
let params = ComplexConfig {
mt: config,
i: 0.03,
x: 35,
n: None,
t: 10,
m: 1,
moment: 1,
entry_age: None,
};
// ❌ What does this even mean? Not intuitive but a common practise
let result = some_function(&config, 35, 0.03, 1, 0, 1, 1, Some(30))?;
// ✅ RSLife: crystal clear, onl
