CLI · commit 747e2a7

actuarial

Gratuity & leave encashment valuation under Ind AS 19 / AS 15

Download actuarial.zip
Quickstart
unzip actuarial.zip && cd actuarial
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'
.venv/bin/python -m pytest -q

actuarial

actuarial values the two employee benefit obligations that almost every Indian company has to report each year. The first is gratuity under the Payment of Gratuity Act, which is a post-employment defined benefit. The second is leave encashment, which is an other long-term benefit. Both are valued under Ind AS 19 / AS 15 (revised) with the Projected Unit Credit (PUC) method. Give it an employee census CSV, a YAML file of assumptions and the year's plan-asset cash flows. It returns the DBO, current service cost and interest cost, and a full roll-forward from the prior year in which actuarial gains and losses are split into financial, demographic and experience components. It also produces the Ind AS 19 sensitivity table, the maturity profile and the weighted average duration. Results come out as an Excel workbook and as a disclosure note in Markdown and HTML.

Features

Install

cd actuarial
uv venv .venv && uv pip install -p .venv/bin/python -e '.[dev]'

Requires Python 3.12+. The runtime dependencies are click, PyYAML, openpyxl and rich.

Quickstart

# single-date valuation
.venv/bin/actuarial value -c examples/census_2025.csv -a examples/assumptions_2025.yaml -o out/fy25

# two-year roll-forward with full disclosure (the main use case)
.venv/bin/actuarial reconcile \
  --prior-census examples/census_2025.csv --prior-assumptions examples/assumptions_2025.yaml \
  -c examples/census_2026.csv -a examples/assumptions_2026.yaml \
  --cashflows examples/cashflows_2025_26.yaml -o out/fy26 --company "Example Industries Ltd"

Usage

actuarial reconcile

This is real output from the bundled 300-employee example (FY 2025-26, gratuity part):

──────────────────────────── Gratuity (post-employment defined benefit) ────────────────────────────
Change in present value of obligation
 Item                                            ┃ Amount (INR)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━
 Opening defined benefit obligation              │  6,39,12,385
 Current service cost                            │    59,78,264
 Past service cost (plan amendment)              │            0
 Interest cost                                   │    45,37,779
 Benefits paid                                   │  (64,30,620)
 Actuarial (gain)/loss - financial assumptions   │    31,14,364
 Actuarial (gain)/loss - demographic assumptions │       10,578
 Actuarial (gain)/loss - experience adjustments  │    11,41,285
 Closing defined benefit obligation              │  7,22,64,035
Change in fair value of plan assets
 Item                                            ┃ Amount (INR)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━
 Opening fair value                              │  5,24,08,000
 Interest income                                 │    37,20,968
 Return on plan assets excluding interest income │     (26,168)
 Employer contributions                          │    76,69,000
 Benefits paid from the fund                     │  (57,87,558)
 Closing fair value                              │  5,79,84,242
Net liability recognised in the balance sheet
 Item                        ┃       Opening ┃       Closing
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━
 Present value of obligation │   6,39,12,385 │   7,22,64,035
 Fair value of plan assets   │ (5,24,08,000) │ (5,79,84,242)
 Net liability / (asset)     │   1,15,04,385 │   1,42,79,793
Expense recognised in the statement of profit and loss
 Item                                 ┃ Amount (INR)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━
 Current service cost                 │    59,78,264
 Past service cost                    │            0
 Net interest cost                    │     8,16,811
 Expense recognised in profit or loss │    67,95,075
Remeasurements recognised in other comprehensive income
 Item                                            ┃ Amount (INR)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━
 Actuarial (gain)/loss on obligation             │    42,66,227
 Return on plan assets excluding interest income │       26,168
 Net (income)/expense recognised in OCI          │    42,92,395
Sensitivity analysis
 Assumption        ┃ Change ┃   DBO (INR) ┃ Impact (INR) ┃ Impact %
━━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━
 Discount rate     │  -1.0% │ 7,89,00,856 │    66,36,821 │   +9.18%
 Discount rate     │  -0.5% │ 7,54,63,498 │    31,99,463 │   +4.43%
 Discount rate     │  +0.5% │ 6,92,81,883 │  (29,82,153) │   -4.13%
 Discount rate     │  +1.0% │ 6,64,98,511 │  (57,65,525) │   -7.98%
 Salary escalation │  -1.0% │ 7,01,20,851 │  (21,43,184) │   -2.97%
 Salary escalation │  -0.5% │ 7,12,30,844 │  (10,33,192) │   -1.43%
 Salary escalation │  +0.5% │ 7,32,53,701 │     9,89,666 │   +1.37%
 Salary escalation │  +1.0% │ 7,42,43,826 │    19,79,791 │   +2.74%
 Attrition rate    │  -1.0% │ 6,86,94,408 │  (35,69,627) │   -4.94%
 Attrition rate    │  -0.5% │ 7,05,48,479 │  (17,15,556) │   -2.37%
 Attrition rate    │  +0.5% │ 7,38,51,735 │    15,87,699 │   +2.20%
 Attrition rate    │  +1.0% │ 7,53,21,382 │    30,57,347 │   +4.23%
Weighted average duration: 9.12 years

The leave encashment section follows. Its actuarial gain or loss goes to P&L, not OCI:

Expense recognised in the statement of profit and loss
 Current service cost                 │    82,91,398
 Past service cost                    │            0
 Net interest cost                    │    27,23,872
 Net actuarial (gain)/loss            │     2,56,334
 Expense recognised in profit or loss │  1,12,71,604

With -o DIR the command writes these files:

File Contents
valuation.xlsx Summary, plus Detail, Reconciliation, Sensitivity and Maturity sheets for each benefit (one row per employee in Detail)
disclosure.md Disclosure note: assumptions (current vs prior), membership data, every table above, maturity profile, duration
disclosure.html The same note as a styled standalone HTML page

actuarial value

value runs a valuation at a single date with no roll-forward. It prints the DBO, the service cost and interest cost for the next year, and the sensitivity table. Use -b gratuity|leave|all to choose benefits.

actuarial explain

$ actuarial explain -c examples/census_2026.csv -a examples/assumptions_2026.yaml --id E0004 --rows 3
E0004 age 31.41, past service 9.29 yrs, salary 21,200
     t ┃ Age ┃      Cause ┃     Prob ┃ Service ┃  Salary ┃   Benefit ┃ Accrued ┃ PV accrued
━━━━━━━╇━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━
  0.50 │  31 │      death │ 0.000978 │    9.79 │  22,083 │   127,400 │ 120,891 │        114
  0.50 │  31 │ withdrawal │ 0.079959 │    9.79 │  22,083 │   127,400 │ 120,891 │      9,356
  1.50 │  32 │      death │ 0.000937 │   10.79 │  23,960 │   152,052 │ 130,907 │        111
  ...
 28.59 │  60 │ retirement │ 0.180007 │   37.88 │ 218,434 │ 2,000,000 │ 490,351 │     13,637
DBO 118,238.26   current service cost 12,222.00

Configuration

Census CSV: employee_id, dob, doj, monthly_salary, leave_balance_days, gender, category. Dates use ISO format, and monthly_salary is basic + DA.

Assumptions YAML (see examples/assumptions_2026.yaml):

valuation_date: 2026-03-31
discount_rate: 0.0675
salary_escalation: 0.085
retirement_age: 60
mortality: {scale: 1.0, female_setback_years: 0}   # optional custom_table: {age: qx}
attrition:
  default:   [{up_to_age: 30, rate: 0.12}, {up_to_age: 44, rate: 0.05}, {up_to_age: 100, rate: 0.02}]
  categories: {Worker: [...]}                       # optional per-category bands
gratuity: {days: 15, divisor: 26, cap: 2000000, vesting_years: 5}
leave: {encashment_divisor: 30, max_accumulation_days: 300, annual_accrual_days: 30, availment_rate: 0.05}

Cash flows YAML (passed with --cashflows):

gratuity:
  plan_assets: {opening_fair_value: 52408000, contributions: 7669000, benefits_paid: 5787558, actual_return: 3694800}
  benefits_paid_directly: 643062
leave:
  benefits_paid_directly: 4375554

examples/generate.py regenerates the synthetic two-year example deterministically. It produces 300 employees at 31-Mar-2025 and simulates exits, raises and new joiners up to 31-Mar-2026.

Architecture

Module Responsibility
census.py Census CSV loading and validation, Employee
assumptions.py Assumptions YAML, attrition bands, mortality wrapper, gratuity and leave rules
mortality.py Compact IALM 2012-14 approximation and log-linear interpolation
engine.py PUC core: exit projection (multiple decrement), gratuity and leave valuation, duration, maturity profile
analysis.py Plan assets, year-on-year reconciliation with gain/loss attribution, sensitivity
report.py Disclosure sections rendered to Markdown, HTML and Excel
cli.py value, reconcile and explain commands

The methodology, formulas and a worked example are in docs/methodology.md.

Tests

.venv/bin/pytest -q      # 34 tests

The tests cover the following:

Limitations

License

MIT. See LICENSE.