SkillAgentSearch skills...

Rslife

A comprehensive Rust library for actuarial mortality table calculations and life insurance mathematics

Install / Use

/learn @hnlearndev/Rslife

README

<h1 align="center"> <a href="https://crates.io/crates/rslife"> <picture> <source srcset="https://raw.githubusercontent.com/hnlearndev/static/refs/heads/main/rslife/banner/banner_dark.svg" media="(prefers-color-scheme: dark)"> <img src="https://raw.githubusercontent.com/hnlearndev/static/refs/heads/main/rslife/banner/banner_light.svg" alt="RSLife logo"> </picture> </a> </h1> <div align="center">

crates.io Latest Release Documentation codecov Performance

Crates.io Total Downloads

| Rust Version | Build Status | Test Status | |:------------:|:------------:|:-----------:| | Stable | Build Status - Stable | Test Status - Stable | | 1.89.0 | Build Status - 1.89.0 | Test Status - 1.89.0 | | Nightly | Build Status - Nightly | Test Status - Nightly |

</div>

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 qx rates or lx survivor 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
View on GitHub
GitHub Stars9
CategoryDevelopment
Updated20d ago
Forks1

Languages

Rust

Security Score

90/100

Audited on Mar 18, 2026

No findings