| Type: | Package |
| Title: | Tidy Tools for Actuarial Mathematics and Life Contingencies |
| Version: | 0.1.3 |
| Description: | Provides tidyverse-aligned tools for actuarial mathematics and life contingencies, including life tables, survival probabilities, actuarial present values of cash flows, life annuities, life insurance, premiums, reserves, multiple-life calculations, Monte Carlo simulation, and deterministic cash-flow diagrams. The package emphasizes clear actuarial notation, reproducible workflows, and pipe-friendly tools for actuarial education and applied actuarial analysis. |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Depends: | R (≥ 4.1.0) |
| Imports: | dplyr, ggplot2, rlang, scales, stats, tibble, utils |
| Suggests: | purrr, testthat (≥ 3.0.0) |
| LazyData: | true |
| LazyDataCompression: | xz |
| RoxygenNote: | 7.3.3 |
| Config/testthat/edition: | 3 |
| NeedsCompilation: | no |
| Packaged: | 2026-05-28 22:32:46 UTC; LENOVO |
| Author: | Julian Fajardo [aut, cre] |
| Maintainer: | Julian Fajardo <julian.fajardo1908@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-05-28 22:50:02 UTC |
Internal helper: annual annuity payment times
Description
Internal helper: annual annuity payment times
Usage
.mc_annuity_payment_times_annual(
k,
annuity,
term,
deferral_years,
guarantee_years,
timing
)
Arguments
k |
Numeric scalar. Simulated curtate future lifetime. |
annuity |
Character string. Annuity type. |
term |
Numeric scalar or |
deferral_years |
Numeric scalar. |
guarantee_years |
Numeric scalar or |
timing |
Character string. Either |
Value
Numeric vector with annual payment times.
Internal helper: fractional annuity payment times
Description
Internal helper: fractional annuity payment times
Usage
.mc_annuity_payment_times_fractional(
tx,
annuity,
term,
deferral_years,
guarantee_years,
timing,
payment_interval
)
Arguments
tx |
Numeric scalar. Simulated complete future lifetime. |
annuity |
Character string. Annuity type. |
term |
Numeric scalar or |
deferral_years |
Numeric scalar. |
guarantee_years |
Numeric scalar or |
timing |
Character string. Either |
payment_interval |
Numeric scalar. Time between payments. |
Value
Numeric vector with fractional payment times.
Internal helper: validate character scalar
Description
Internal helper: validate character scalar
Usage
.mc_assert_character_scalar(x, arg)
Arguments
x |
Object to validate. |
arg |
Character string with the argument name. |
Value
Invisibly returns TRUE if validation is successful.
Internal helper: validate column existence
Description
Internal helper: validate column existence
Usage
.mc_assert_column(data, col, arg)
Arguments
data |
A data frame or tibble. |
col |
Character string with the column name. |
arg |
Character string with the argument name. |
Value
Invisibly returns TRUE if validation is successful.
Internal helper: validate numeric column
Description
Internal helper: validate numeric column
Usage
.mc_assert_numeric_column(data, col, arg)
Arguments
data |
A data frame or tibble. |
col |
Character string with the column name. |
arg |
Character string with the argument name. |
Value
Invisibly returns TRUE if validation is successful.
Internal helper: validate numeric scalar
Description
Internal helper: validate numeric scalar
Usage
.mc_assert_numeric_scalar(
x,
arg,
min = -Inf,
max = Inf,
strict_min = FALSE,
strict_max = FALSE,
allow_null = FALSE
)
Arguments
x |
Object to validate. |
arg |
Character string with the argument name. |
min |
Minimum allowed value. Default is |
max |
Maximum allowed value. Default is |
strict_min |
Logical. Should the lower bound be strict? |
strict_max |
Logical. Should the upper bound be strict? |
allow_null |
Logical. Should |
Value
Invisibly returns TRUE if validation is successful.
Internal helper: validate positive integer-like scalar
Description
Internal helper: validate positive integer-like scalar
Usage
.mc_assert_positive_integer(x, arg)
Arguments
x |
Object to validate. |
arg |
Character string with the argument name. |
Value
Invisibly returns TRUE if validation is successful.
Internal helper: compute annual discount factor
Description
Computes the annual discount factor from an interest rate convention.
Usage
.mc_discount_factor(
rate,
interest_type = c("effective", "nominal", "force"),
m = 1
)
Arguments
rate |
Numeric scalar. Interest rate. |
interest_type |
Character string. Interest rate convention. One of
|
m |
Numeric scalar. Number of interest conversion periods per year when
|
Value
Numeric scalar with the annual discount factor.
Internal helper: convert interest rate to annual effective rate
Description
Converts an interest rate supplied under a standard actuarial convention into an equivalent annual effective interest rate.
Usage
.mc_effective_rate(
rate,
interest_type = c("effective", "nominal", "force"),
m = 1
)
Arguments
rate |
Numeric scalar. Interest rate. |
interest_type |
Character string. Interest rate convention. One of
|
m |
Numeric scalar. Number of interest conversion periods per year when
|
Details
This helper separates the financial interest-rate convention from the
actuarial cash-flow structure. The argument m is used only to convert a
nominal annual rate into an equivalent annual effective rate. It does not
represent annuity payment frequency.
The conversion rules are:
Effective annual rate:
i_{\mathrm{eff}} = i.
Nominal annual rate convertible
mtimes per year:
i_{\mathrm{eff}} = \left(1 + \frac{i^{(m)}}{m}\right)^m - 1.
Constant force of interest:
i_{\mathrm{eff}} = e^\delta - 1.
Value
Numeric scalar with the equivalent annual effective interest rate.
Internal helper: determine order statistic rank for a multiple-life status
Description
Internal helper: determine order statistic rank for a multiple-life status
Usage
.mc_multilife_status_rank(status, k, n_lives)
Arguments
status |
Character string with the multiple-life status. |
k |
Optional positive integer. |
n_lives |
Positive integer. Number of lives. |
Value
Positive integer giving the order statistic rank.
Internal helper: safe payment times
Description
Creates a sequence of payment times. If the upper bound is smaller than the
lower bound, it returns numeric(0).
Usage
.mc_payment_times(from, to, by = 1)
Arguments
from |
Numeric scalar. First payment time. |
to |
Numeric scalar. Last payment time. |
by |
Numeric scalar. Step between payment times. Default is |
Value
Numeric vector.
Internal helper: create quantile column names
Description
Internal helper: create quantile column names
Usage
.mc_quantile_names(probs)
Arguments
probs |
Numeric vector of probabilities. |
Value
Character vector with syntactically valid quantile names.
Internal helper: benefit payment information for reserve calculations
Description
Determines whether a benefit is paid and at what time for each simulated scenario.
Usage
.mc_reserve_benefit_info(
Kx,
Tx,
death_time,
insurance,
term,
deferral_years,
payment_timing
)
Arguments
Kx |
Numeric vector of simulated curtate future lifetimes. |
Tx |
Numeric vector of simulated complete future lifetimes. |
death_time |
Numeric vector of death benefit payment times. |
insurance |
Character string specifying the insurance type. |
term |
Numeric scalar or |
deferral_years |
Numeric scalar. |
payment_timing |
Character string. Either |
Value
A list with benefit_indicator and benefit_time.
Level annuity factor a-angle-n
Description
Computes the actuarial present value factor for a level annuity.
Usage
a_angle(
n_years = NULL,
payments_per_year = 1L,
rate,
rate_type = "effective",
m = 1L,
deferral_years = 0,
timing = "immediate",
perpetuity = FALSE,
payment = 1,
output = c("value", "table")
)
Arguments
n_years |
Numeric vector of payment durations in years.
Ignored when |
payments_per_year |
Positive integer vector giving the number of discrete payments per year. Ignored for continuous annuities. |
rate |
Numeric vector of rate values. |
rate_type |
Character vector indicating the rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency
for nominal rates. Ignored for |
deferral_years |
Numeric vector of deferral times in years. Must be greater than or equal to 0. |
timing |
Character vector. One of |
perpetuity |
Logical vector. If |
payment |
Numeric vector of level payment amounts. Used only when
|
output |
Character string. Use |
Details
Supported timing conventions:
-
"immediate": annuity-immediate with discrete payments. -
"due": annuity-due with discrete payments. -
"continuous": continuous annuity.
For discrete annuities, payments_per_year = k means payments are made
every 1/k year. The function returns the annuity factor, assuming a
unit payment at each payment time.
Deferral is supported through deferral_years = h. For discrete
annuities, the deferral must align with the payment grid, that is,
h k must be an integer.
If perpetuity = TRUE, the infinite-term annuity factor is returned.
The function first converts the supplied rate to the equivalent annual
effective interest rate using standardize_interest.
For finite discrete annuities:
a_{\overline{n|}} = \frac{1 - v^n}{i}
For due annuities:
\ddot{a}_{\overline{n|}} = (1+i)a_{\overline{n|}}
For continuous annuities:
\bar{a}_{\overline{n|}} = \frac{1 - e^{-\delta n}}{\delta}
Input vectors must have length 1 or a common length. Missing values are propagated.
Value
If output = "value", a numeric vector of annuity factors.
If output = "table", a tibble with input values, equivalent rates,
annuity factors, payment amounts, and present values.
See Also
s_angle, standardize_interest,
present_value
Other annuities:
annuity_arith(),
annuity_geom(),
s_angle()
Examples
# Numeric annuity factor
a_angle(n_years = 10, rate = 0.05)
# Nominal interest converted monthly, with monthly payments
a_angle(
n_years = 10,
rate = 0.06,
rate_type = "nominal_interest",
m = 12,
payments_per_year = 12
)
# Continuous annuity
a_angle(
n_years = 15,
rate = 0.04,
rate_type = "force",
timing = "continuous"
)
# Tibble output for teaching or auditing
a_angle(
n_years = 10,
rate = 0.05,
payment = 1000,
output = "table"
)
# Vectorized example
a_angle(
n_years = c(5, 10, 20),
payments_per_year = c(1, 12, 1),
rate = c(0.05, 0.06, 0.04),
rate_type = c("effective", "nominal_interest", "force"),
m = c(1, 12, 1),
deferral_years = c(0, 0, 2),
timing = c("immediate", "immediate", "continuous"),
perpetuity = c(FALSE, FALSE, FALSE)
)
Amortization schedule with optional prepayment adjustment
Description
Builds an amortization schedule under a fixed annual interest-rate specification, allowing extra principal payments and optional adjustment of either the remaining term or the remaining payment amount.
Usage
amort_schedule(
principal,
n,
rate,
rate_type = "effective",
m = 1L,
periods_per_year = 1L,
timing = c("immediate", "due"),
payment = NULL,
extra_principal = NULL,
adjust = c("none", "term", "payment"),
tol = 1e-08
)
Arguments
principal |
Numeric scalar. Initial outstanding balance. |
n |
Positive integer. Number of contractual periods. |
rate |
Numeric scalar. Annual rate value. |
rate_type |
Character string indicating the annual rate type:
|
m |
Positive integer. Compounding frequency for nominal annual rates. |
periods_per_year |
Positive integer. Number of schedule periods per year. |
timing |
Character string. One of |
payment |
Optional numeric scalar. Initial regular payment per period.
If |
extra_principal |
Optional extra principal payments. Can be:
|
adjust |
Character string. One of |
tol |
Numeric tolerance for zero-balance detection. |
Details
The annual rate is converted internally to an effective rate per schedule
period using periods_per_year.
Adjustment policies after extra principal payments:
-
"none": keep the original payment and contractual term, unless the loan is fully repaid early. -
"term": keep the regular payment and shorten the term. No special logic is needed: the loop exits naturally when the outstanding balance reaches zero. -
"payment": keep the remaining contractual term and recalculate the regular payment after each period.
For timing = "immediate" (annuity-immediate), interest accrues on the
outstanding balance during the period, and the payment is made at the end.
For timing = "due" (annuity-due), the payment is made at the start of
the period and interest accrues on the balance after the payment.
If the user supplies a custom payment that is smaller than the
periodic interest, principal repayment will be negative (negative
amortization). This is permitted but the user should be aware.
Value
A tibble with one row per realized period and columns:
- period
Period index.
- ob_start
Outstanding balance at the start of the period.
- interest
Interest charged during the period.
- payment
Regular payment in the period.
- extra_principal
Extra principal paid in the period.
- principal
Principal repaid through the regular payment.
- total_principal
Total principal repaid in the period.
- cashflow
Total payment made in the period.
- ob_end
Outstanding balance at the end of the period.
- i_effective_annual
Equivalent annual effective rate.
- i_effective_period
Equivalent effective rate per schedule period.
- periods_per_year
Schedule frequency.
- timing
Payment timing convention.
- adjust
Adjustment rule used.
See Also
a_angle, present_value, pv_flow, standardize_interest
Other amortization:
sinking_fund_schedule()
Examples
amort_schedule(
principal = 100000,
n = 12,
rate = 0.12,
rate_type = "nominal_interest",
m = 12,
periods_per_year = 12
)
amort_schedule(
principal = 100000,
n = 24,
rate = 0.12,
rate_type = "nominal_interest",
m = 12,
periods_per_year = 12,
extra_principal = c("6" = 5000, "12" = 3000),
adjust = "term"
)
amort_schedule(
principal = 100000,
n = 24,
rate = 0.12,
rate_type = "nominal_interest",
m = 12,
periods_per_year = 12,
extra_principal = c("6" = 5000, "12" = 3000),
adjust = "payment"
)
Arithmetic annuity factor
Description
Computes the actuarial present value or accumulated value factor for an arithmetic annuity.
Usage
annuity_arith(
n_years,
payments_per_year = 1L,
rate,
rate_type = "effective",
m = 1L,
deferral_years = 0,
timing = "immediate",
pattern = "increasing",
first_payment = 1,
increment = 1,
valuation = c("present", "accumulated"),
output = c("value", "table")
)
Arguments
n_years |
Numeric vector of payment durations in years. Each value must be positive and finite. |
payments_per_year |
Positive integer vector giving the number of payments per year. |
rate |
Numeric vector of rate values. |
rate_type |
Character vector indicating the rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency for nominal rates. |
deferral_years |
Numeric vector of deferral times in years. Must be greater than or equal to 0. |
timing |
Character vector. One of |
pattern |
Character vector. One of |
first_payment |
Numeric vector. First payment for
|
increment |
Numeric vector. Arithmetic increment for
|
valuation |
Character string. Use |
output |
Character string. Use |
Details
This function covers increasing, decreasing, and custom arithmetic payment patterns. It replaces the more specific increasing and decreasing annuity functions.
Supported payment patterns:
-
"increasing": payments1, 2, \ldots, n. -
"decreasing": paymentsn, n-1, \ldots, 1. -
"custom": payments followingP_r = P_1 + (r - 1)g.
Supported timing conventions:
-
"immediate": payments at the end of each period. -
"due": payments at the beginning of each period.
The function first converts the supplied rate to the equivalent annual
effective interest rate using standardize_interest.
For each scenario, the total number of payments is
N = n \times k
where n is n_years and k is payments_per_year.
The present value is computed by summing the discounted payment stream.
The accumulated value is computed at the end of the annuity term. Under
this convention, deferral_years affects the present value factor but
not the accumulated value factor.
Value
If output = "value", a numeric vector of arithmetic annuity factors.
If output = "table", a tibble with input values, equivalent rates,
period quantities, and both present and accumulated value factors.
See Also
a_angle, s_angle,
standardize_interest
Other annuities:
a_angle(),
annuity_geom(),
s_angle()
Examples
# Increasing arithmetic annuity
annuity_arith(n_years = 10, rate = 0.05, pattern = "increasing")
# Decreasing arithmetic annuity
annuity_arith(n_years = 10, rate = 0.05, pattern = "decreasing")
# Custom arithmetic annuity
annuity_arith(
n_years = 10,
rate = 0.05,
pattern = "custom",
first_payment = 100,
increment = 25
)
# Accumulated value factor
annuity_arith(
n_years = 10,
rate = 0.05,
pattern = "increasing",
valuation = "accumulated"
)
# Tibble output
annuity_arith(
n_years = 10,
rate = 0.05,
pattern = "decreasing",
output = "table"
)
Geometric annuity factor
Description
Computes the present value or accumulated value factor for a geometric annuity.
Usage
annuity_geom(
n_years = NULL,
payments_per_year = 1L,
rate,
rate_type = "effective",
m = 1L,
growth_rate = 0,
growth_rate_type = "effective",
growth_m = 1L,
deferral_years = 0,
timing = "immediate",
first_payment = 1,
perpetuity = FALSE,
valuation = c("present", "accumulated"),
output = c("value", "table")
)
Arguments
n_years |
Numeric vector of payment durations in years.
Ignored only when |
payments_per_year |
Positive integer vector giving the number of payments per year. |
rate |
Numeric vector of annual interest-rate values. |
rate_type |
Character vector indicating the interest-rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency for nominal interest-rate inputs. |
growth_rate |
Numeric vector of annual growth-rate values for the payments. |
growth_rate_type |
Character vector indicating the growth-rate type.
Allowed values are |
growth_m |
Positive integer vector giving the compounding frequency for nominal growth-rate inputs. |
deferral_years |
Numeric vector of deferral times in years. Must be greater than or equal to 0. For present values, the deferral discounts the payment block. For accumulated values, it is recorded but does not change the factor under the adopted terminal-horizon convention. |
timing |
Character vector. One of |
first_payment |
Numeric vector giving the first payment of the geometric sequence. |
perpetuity |
Logical vector. If |
valuation |
Character string. Use |
output |
Character string. Use |
Details
This function replaces the more specific geometric present value and accumulated value functions.
Supported timing conventions:
-
"immediate": payments at the end of each period. -
"due": payments at the beginning of each period.
Let i_p be the effective interest rate per payment period,
g_p the effective growth rate per payment period, and
v_p = (1+i_p)^{-1}.
For a finite geometric annuity-immediate with N payment periods:
ga_N = \sum_{r=1}^{N}\frac{(1+g_p)^{r-1}}{(1+i_p)^r}
If i_p \neq g_p, then
ga_N = \frac{1-\left(\frac{1+g_p}{1+i_p}\right)^N}{i_p-g_p}
The accumulated value factor is computed at the standard terminal horizon:
gs_N = \sum_{r=1}^{N}(1+g_p)^{r-1}(1+i_p)^{N-r}.
For annuities-due, the corresponding immediate factor is multiplied by
1+i_p.
Value
If output = "value", a numeric vector.
If output = "table", a tibble with input values, equivalent rates,
period quantities, and both present and accumulated value factors.
See Also
a_angle, s_angle,
annuity_arith, standardize_interest
Other annuities:
a_angle(),
annuity_arith(),
s_angle()
Examples
# Present value of a geometric annuity
annuity_geom(
n_years = 10,
rate = 0.05,
growth_rate = 0.02,
valuation = "present"
)
# Accumulated value of a geometric annuity
annuity_geom(
n_years = 10,
rate = 0.05,
growth_rate = 0.02,
valuation = "accumulated"
)
# Tibble output
annuity_geom(
n_years = 10,
rate = 0.05,
growth_rate = 0.02,
output = "table"
)
Actuarial present value of a multi-life annuity (up to 3 independent lives)
Description
Computes the APV of a discrete annuity contingent on multiple independent lives. This implementation supports up to three lives (the most common practical case, e.g., parent–parent–child arrangements).
Usage
annuity_multi(
lt,
ages,
annuity = c("cohort", "reversionary"),
cohort = c("first", "last"),
alpha = NULL,
n = NULL,
m = 0L,
k = 1L,
timing = c("immediate", "due"),
woolhouse = c("none", "first", "second"),
i
)
Arguments
lt |
A lifetable object (data.frame/tibble) with column |
ages |
Integer vector of actuarial ages for the lives at issue. Must have length 1–3. |
annuity |
Type of annuity logic: |
cohort |
Survival status: |
alpha |
Reversionary fraction (typically |
n |
Integer term in years after deferment. If |
m |
Integer deferment in years. |
k |
Integer payments per year. If |
timing |
|
woolhouse |
|
i |
Annual effective interest rate. |
Details
Supports status-based annuities (joint-life / last-survivor) and a
joint-and-survivor style annuity ("reversionary") that pays 1 while all
lives are alive and then pays a fraction \alpha while at least one
life remains alive.
Under the assumption of independent future lifetimes (Finan, Section 51), the survival probability for the status is calculated as:
-
Joint-life (first-death):
{}_t p_{x_1 x_2 \dots x_n} = \prod_{j=1}^n {}_t p_{x_j} -
Last-survivor:
{}_t p_{\overline{x_1 x_2 \dots x_n}} = 1 - \prod_{j=1}^n (1 - {}_t p_{x_j})
For annuity = "reversionary", the APV is a weighted combination
of the two statuses (Finan, Section 53.3):
APV = APV(\text{joint-life}) + \alpha [APV(\text{last-survivor}) - APV(\text{joint-life})]
Value
A single numeric value representing the APV.
See Also
annuity_x for single-life annuities,
t_px for survival probabilities.
Actuarial present value of a life annuity
Description
Computes the actuarial present value of a discrete life annuity using a life table.
Usage
annuity_x(
mortality_table,
age,
rate,
rate_type = "effective",
m = 1L,
term_years = Inf,
deferral_years = 0L,
payments_per_year = 1L,
timing = c("immediate", "due"),
woolhouse = c("none", "first", "second"),
output = c("value", "table")
)
Arguments
mortality_table |
A life table as produced by |
age |
Integer actuarial age. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. Ignored
for |
term_years |
Integer term in years. Use |
deferral_years |
Integer deferral period in years. |
payments_per_year |
Positive integer. Number of payments per year.
For example, use |
timing |
Character string. Either |
woolhouse |
Character string. For |
output |
Character string. Use |
Details
The function supports:
whole-life annuities,
temporary annuities,
integer deferral,
annual or k-thly payments,
exact fractional survival under UDD,
first- and second-order Woolhouse approximations.
For annual annuities-due,
\ddot{a}_{x:\overline{n}|} =
\sum_{j=0}^{n-1} v^j\,{}_jp_x.
For annual annuities-immediate,
a_{x:\overline{n}|} =
\sum_{j=1}^{n} v^j\,{}_jp_x.
Deferral is handled through
v^h\,{}_hp_x
where h is deferral_years.
For k-thly payments with woolhouse = "none", fractional survival is
computed under UDD.
Value
If output = "value", a numeric scalar containing the actuarial present
value.
If output = "table", a one-row tibble with the main input values,
equivalent interest rate, deferral factor, pure endowment factor, and APV.
See Also
insurance_x, premium_x,
reserve_x, t_px, t_Ex
Other life-contingencies:
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:65,
lx = c(100000, 99000, 97500, 95500, 93000, 90000)
)
# Annual annuity-immediate
annuity_x(
mortality_table = lt,
age = 60,
rate = 0.06,
timing = "immediate"
)
# Annual annuity-due
annuity_x(
mortality_table = lt,
age = 60,
rate = 0.06,
timing = "due"
)
# Temporary annuity
annuity_x(
mortality_table = lt,
age = 60,
rate = 0.06,
term_years = 3,
timing = "due"
)
# Deferred annuity
annuity_x(
mortality_table = lt,
age = 60,
rate = 0.06,
deferral_years = 2,
timing = "due"
)
# Table output
annuity_x(
mortality_table = lt,
age = 60,
rate = 0.06,
term_years = 3,
timing = "due",
output = "table"
)
Actuarial present value of a two-life annuity
Description
Computes the actuarial present value of a discrete annuity contingent on two independent lives. Supports joint-life, last-survivor, and state-based reversionary-style payments.
Usage
annuity_xy(
mortality_table,
age_x = NULL,
age_y = NULL,
rate = NULL,
rate_type = NULL,
m = NULL,
cohort = c("first", "last"),
benefit = NULL,
term_years = Inf,
deferment_years = 0L,
payments_per_year = 1L,
timing = c("immediate", "due"),
woolhouse = c("none", "first", "second"),
frac,
output = c("value", "table")
)
Arguments
mortality_table |
Either a single life table used for both lives, a list
of two life tables |
age_x |
Integer actuarial age for the first life. |
age_y |
Integer actuarial age for the second life. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. |
cohort |
Character string. Use |
benefit |
Optional list with weights |
term_years |
Term in years. Use |
deferment_years |
Integer deferment period in years. |
payments_per_year |
Positive integer. Number of payments per year. |
timing |
Payment timing. Use |
woolhouse |
Woolhouse approximation for |
frac |
Fractional-age assumption for exact k-thly computation:
|
output |
Character string. Use |
Details
This function assumes independent future lifetimes.
Value
If output = "value", a numeric scalar. If output = "table",
a one-row tibble.
See Also
annuity_x(), insurance_xy(), premium_xy(), t_pxy()
Other life-contingencies:
annuity_x(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)
annuity_xy(
mortality_table = lt,
age_x = 60,
age_y = 62,
rate = 0.05,
cohort = "first",
timing = "due"
)
lt |>
life_contract(lives = "joint", age_x = 60, age_y = 62, rate = 0.05) |>
annuity_xy(cohort = "last", timing = "due")
Actuarial present value of a payment stream under mortality
Description
Computes the actuarial present value (APV) of a cash-flow stream contingent on survival. The life table is supplied as the first argument (pipe-friendly). Payments may be specified by numeric times (years from 0) or by calendar dates.
Usage
apv_life_flow(
lt,
ages,
time = NULL,
date = NULL,
start_date = NULL,
cf,
i,
status = c("single", "first", "last", "reversionary"),
alpha = NULL,
plot = FALSE
)
Arguments
lt |
A life table data frame with column |
ages |
Integer vector of actuarial ages (length 1 for single life, length 2+ for multiple lives). |
time |
Numeric vector of payment times in years ( |
date |
Optional vector of |
start_date |
Optional |
cf |
Numeric vector of cash flows (same length as |
i |
Annual effective interest rate (single numeric value). |
status |
Survival status: |
alpha |
Reversionary fraction for |
plot |
Logical; if |
Details
Multiple lives are supported under an independence assumption, through
common statuses: single-life, first-death (all alive), last-survivor
(any alive), and reversionary (joint-and-survivor) with fraction
alpha.
For each payment at time t, the contribution to the APV is
(Finan, Sections 33 and 37):
PV(t) = C(t) \times v^t \times P(StatusAliveMatT)
The survival probability depends on the status:
-
"single":{}_t p_x(single life). -
"first":{}_t p_{x_1} \cdot {}_t p_{x_2} \dots(all must be alive - joint life). -
"last":1 - \prod (1 - {}_t p_{x_j})(at least one alive - last survivor). -
"reversionary": full benefit while all alive, fraction\alphawhile partially alive.
Fractional-year survival is computed under UDD within each year (Finan, Section 24.1).
Value
A tibble with one row per payment and columns:
time, cf, surv_prob, discount,
expected_cf, pv, pv_cum.
If date was provided, a date column is included.
The total APV is stored as attr(result, "apv").
Book value of a level coupon bond at a coupon date
Description
Computes the book value of a level coupon bond at one or more coupon dates, under a specified yield basis.
Usage
bond_book_value(
face,
coupon_rate,
years_to_maturity,
valuation_time,
coupons_per_year = 1L,
yield_effective_per_period = NULL,
yield_rate = NULL,
yield_rate_type = "effective",
yield_m = 1L,
redemption = NULL,
tol = 1e-10,
check = TRUE,
output = c("value", "table")
)
Arguments
face |
Numeric scalar. Face or par value of the bond. |
coupon_rate |
Numeric scalar. Annual coupon rate as a proportion. |
years_to_maturity |
Numeric scalar. Final maturity in years. |
valuation_time |
Numeric vector. Valuation time(s) in years, measured
from issue. Each value must lie between |
coupons_per_year |
Positive integer. Number of coupon payments per year. |
yield_effective_per_period |
Optional numeric scalar. Effective yield per coupon period. If supplied, it is used directly. |
yield_rate |
Optional numeric scalar. Annual yield rate value. |
yield_rate_type |
Character string indicating the annual yield type:
|
yield_m |
Positive integer. Compounding frequency for nominal annual yields. |
redemption |
Numeric scalar. Redemption value at final maturity.
If |
tol |
Numeric scalar. Tolerance used in alignment checks. |
check |
Logical scalar. If |
output |
Character string. Use |
Details
The book value is interpreted prospectively: at a valuation time that lies on the coupon grid, it equals the present value at that time of all remaining future coupons and the final redemption amount, discounted at the bond's yield basis.
This function interprets valuation_time as a time immediately after any
coupon due at that date has been paid. Therefore:
at
valuation_time = 0, the book value equals the bond price,at
valuation_time = years_to_maturity, the book value is 0.
Assumptions:
Coupons are paid in arrears at regular intervals.
-
years_to_maturity * coupons_per_yearmust be an integer. Each
valuation_time * coupons_per_yearmust be an integer.Stub periods are not supported.
Valuation is performed at coupon dates; no accrued interest is included.
Yield input conventions:
If
yield_effective_per_periodis supplied, it takes precedence overyield_rate,yield_rate_type, andyield_m, and is interpreted as the effective yield per coupon period.Otherwise,
yield_rate,yield_rate_type, andyield_mdefine an annual yield specification, which is converted first to annual effective yield and then to effective yield per coupon period.
Let the valuation time correspond to coupon period k, with total
maturity period count N. If the remaining future cash flows are
C_{k+1}, \dots, C_N, and i_p is the effective yield per coupon
period, then the book value at time k is
BV_k = \sum_{j=k+1}^{N} C_j (1+i_p)^{-(j-k)}
This is the prospective book value on the bond's yield basis.
Value
If output = "value", a numeric vector of book values, one for each
valuation_time.
If output = "table", a tibble with valuation times, valuation periods,
book values, yield information, and bond inputs.
See Also
bond_price, bond_cash_flows,
bond_duration, bond_convexity
Other bonds:
bond_callable_price(),
bond_convexity(),
bond_duration(),
bond_price(),
bond_ytm(),
portfolio_convexity(),
portfolio_duration()
Examples
# Book value at time 0 equals price
bond_book_value(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
valuation_time = 0,
coupons_per_year = 2,
yield_rate = 0.06,
yield_rate_type = "effective"
)
# Book value at several coupon dates
bond_book_value(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
valuation_time = c(0, 1, 2, 3, 4, 5),
coupons_per_year = 1,
yield_rate = 0.06,
yield_rate_type = "effective"
)
# Table output
bond_book_value(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
valuation_time = c(0, 1, 2, 3, 4, 5),
coupons_per_year = 1,
yield_rate = 0.06,
yield_rate_type = "effective",
output = "table"
)
# Yield given directly per coupon period
bond_book_value(
face = 1000,
coupon_rate = 0.05,
years_to_maturity = 10,
valuation_time = c(0, 2, 4, 6),
coupons_per_year = 2,
yield_effective_per_period = 0.03
)
Price of a callable bond at a target minimum yield
Description
Computes the maximum price an investor should pay for a callable bond in order to guarantee a specified minimum yield.
Usage
bond_callable_price(
face,
coupon_rate,
years_to_maturity,
coupons_per_year = 1L,
call_times,
call_prices,
yield_effective_per_period = NULL,
yield_rate = NULL,
yield_rate_type = "effective",
yield_m = 1L,
redemption = NULL,
tol = 1e-10,
check = TRUE,
output = c("value", "table")
)
Arguments
face |
Numeric scalar. Face or par value of the bond. |
coupon_rate |
Numeric scalar. Annual coupon rate as a proportion. |
years_to_maturity |
Numeric scalar. Final maturity in years. Must be strictly positive. |
coupons_per_year |
Positive integer. Number of coupon payments per year. |
call_times |
Numeric vector of callable times in years.
Each value must be strictly between |
call_prices |
Numeric vector of call prices corresponding to
|
yield_effective_per_period |
Optional numeric scalar. Effective yield per coupon period. If supplied, it is used directly. |
yield_rate |
Optional numeric scalar. Annual yield rate value. |
yield_rate_type |
Character string indicating the annual yield type:
|
yield_m |
Positive integer. Compounding frequency for nominal annual yields. |
redemption |
Numeric scalar. Redemption value at final maturity.
If |
tol |
Numeric scalar. Tolerance used in alignment checks. |
check |
Logical scalar. If |
output |
Character string. Use |
Details
The bond is evaluated under each possible redemption scenario:
each callable date with its associated call price, and
final maturity with its final redemption value.
For each scenario, the bond price is computed using the target yield. The callable-bond price returned by this function is the smallest of those scenario prices, that is, the maximum price consistent with the target yield under the least favorable redemption scenario for the investor.
This follows the standard actuarial/financial interpretation used in introductory fixed-income mathematics: when a bond is callable at the issuer's option, the investor must protect against the redemption scenario that is least favorable to the investor at the required yield.
Assumptions:
Coupons are paid in arrears at regular intervals.
-
years_to_maturity * coupons_per_yearmust be an integer. Each
call_times * coupons_per_yearmust be an integer.Stub periods are not supported.
Pricing is performed at a coupon date; no accrued interest is included.
Yield input conventions:
If
yield_effective_per_periodis supplied, it takes precedence overyield_rate,yield_rate_type, andyield_m, and is interpreted as the effective yield per coupon period.Otherwise,
yield_rate,yield_rate_type, andyield_mdefine an annual yield specification, which is converted first to annual effective yield and then to effective yield per coupon period.
Let the callable bond have possible redemption scenarios indexed by
j = 1, \dots, J, where each scenario corresponds either to a call date
or to final maturity. For scenario j, let P_j(y) denote the bond
price computed at the target yield y assuming redemption occurs at that
scenario time and value.
Then this function returns
\min_j P_j(y)
when output = "value".
This is the maximum price an investor can pay while still guaranteeing at least the target yield under the least favorable redemption scenario.
Value
If output = "value", a numeric scalar: the worst-case callable-bond
price consistent with the target yield.
If output = "table", a tibble with one row per redemption scenario,
including scenario prices and the worst-case indicator.
See Also
bond_price, bond_cash_flows,
bond_book_value, bond_ytm
Other bonds:
bond_book_value(),
bond_convexity(),
bond_duration(),
bond_price(),
bond_ytm(),
portfolio_convexity(),
portfolio_duration()
Examples
# Callable bond with two possible call dates
bond_callable_price(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 10,
coupons_per_year = 2,
call_times = c(5, 7),
call_prices = c(105, 102),
yield_rate = 0.06,
yield_rate_type = "effective"
)
# Table output with all redemption scenarios
bond_callable_price(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 10,
coupons_per_year = 2,
call_times = c(5, 7),
call_prices = c(105, 102),
yield_rate = 0.06,
yield_rate_type = "effective",
output = "table"
)
# Target yield given directly per coupon period
bond_callable_price(
face = 1000,
coupon_rate = 0.05,
years_to_maturity = 12,
coupons_per_year = 2,
call_times = c(4, 8),
call_prices = c(1030, 1015),
yield_effective_per_period = 0.028
)
Cash flow structure of a level coupon bond
Description
Builds the cash flow schedule of a level coupon bond with constant coupon rate and a single redemption payment at maturity.
Usage
bond_cash_flows(
face,
coupon_rate,
years_to_maturity,
coupons_per_year = 1L,
redemption = NULL,
tol = 1e-10,
check = TRUE
)
Arguments
face |
Numeric scalar. Face value. |
coupon_rate |
Numeric scalar. Annual coupon rate. |
years_to_maturity |
Numeric scalar. Years to maturity. |
coupons_per_year |
Positive integer. Payments per year. |
redemption |
Numeric scalar. Redemption value. |
tol |
Numeric scalar. Tolerance. |
check |
Logical scalar. Input validation. |
Value
A tibble with the bond schedule.
Discrete convexity of a level coupon bond under a flat yield
Description
Computes discrete convexity measures for a level coupon bond valued under a flat yield-to-maturity assumption.
Usage
bond_convexity(
face,
coupon_rate,
years_to_maturity,
coupons_per_year = 1L,
y_effective_per_period = NULL,
y_rate = NULL,
y_type = "effective",
y_m = 1L,
redemption = NULL,
tol = 1e-10,
check = TRUE
)
Arguments
face |
Numeric scalar. Face (par) value of the bond. |
coupon_rate |
Numeric scalar. Annual coupon rate as a proportion. |
years_to_maturity |
Numeric scalar. Time to maturity in years. |
coupons_per_year |
Positive integer. Number of coupon payments per year. |
y_effective_per_period |
Optional numeric scalar. Effective yield per coupon period. |
y_rate |
Optional numeric scalar. Annual yield rate value. |
y_type |
Character string indicating the annual yield type:
|
y_m |
Positive integer. Compounding frequency for nominal annual yields. |
redemption |
Numeric scalar. Redemption value at maturity.
If |
tol |
Numeric scalar. Tolerance used to check maturity alignment. |
check |
Logical scalar. If |
Details
Assumptions:
Coupons are paid in arrears at regular intervals.
-
years_to_maturity * coupons_per_yearmust be an integer. Stub periods are not supported.
Valuation is at a coupon date (no accrued interest).
A single flat yield is used to discount all cash flows.
Yield input conventions:
If
y_effective_per_periodis supplied, it is interpreted as the effective yield per coupon period.Otherwise,
y_rate,y_type, andy_mdefine an annual yield specification, which is converted first to annual effective yield and then to effective yield per coupon period.
Let j be the effective yield per coupon period, m the number of
coupon payments per year, and let cash flows C_k occur at coupon
periods k = 1, \dots, N. With v = 1/(1+j) and
P = \sum_{k} C_k v^k, the discrete convexity in coupon periods is
C_p = \frac{1}{P} \cdot \frac{\sum_{k=1}^{N} C_k k(k+1) v^k}{(1+j)^2}
Discrete convexity in years is C_p / m^2.
This is the second-order sensitivity of the bond price to changes in
the yield per period. Together with bond_duration, it is used in
the second-order Taylor approximation of price changes.
Value
A one-row tibble with:
- price
Dirty price at the given yield.
- discrete_convexity_periods
Discrete convexity in coupon periods.
- discrete_convexity_years
Discrete convexity in years.
- yield_per_period
Effective yield per coupon period.
- yield_effective_annual
Annual effective yield.
- coupons_per_year
Coupon frequency.
- n_periods
Total number of coupon periods.
See Also
bond_duration, bond_price, bond_cash_flows,
bond_book_value, bond_ytm
Other bonds:
bond_book_value(),
bond_callable_price(),
bond_duration(),
bond_price(),
bond_ytm(),
portfolio_convexity(),
portfolio_duration()
Examples
bond_convexity(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
coupons_per_year = 2,
y_rate = 0.06,
y_type = "effective"
)
bond_convexity(
face = 1000,
coupon_rate = 0.05,
years_to_maturity = 10,
coupons_per_year = 2,
y_effective_per_period = 0.03
)
Macaulay and modified duration of a level coupon bond under a flat yield
Description
Computes Macaulay duration and modified-duration measures for a level coupon bond valued under a flat yield-to-maturity assumption.
Usage
bond_duration(
face,
coupon_rate,
years_to_maturity,
coupons_per_year = 1L,
y_effective_per_period = NULL,
y_rate = NULL,
y_type = "effective",
y_m = 1L,
redemption = NULL,
tol = 1e-10,
check = TRUE
)
Arguments
face |
Numeric scalar. Face (par) value of the bond. |
coupon_rate |
Numeric scalar. Annual coupon rate as a proportion. |
years_to_maturity |
Numeric scalar. Time to maturity in years. |
coupons_per_year |
Positive integer. Number of coupon payments per year. |
y_effective_per_period |
Optional numeric scalar. Effective yield per coupon period. |
y_rate |
Optional numeric scalar. Annual yield rate value. |
y_type |
Character string indicating the annual yield type:
|
y_m |
Positive integer. Compounding frequency for nominal annual yields. |
redemption |
Numeric scalar. Redemption value at maturity.
If |
tol |
Numeric scalar. Tolerance used to check maturity alignment. |
check |
Logical scalar. If |
Details
Assumptions:
Coupons are paid in arrears at regular intervals.
-
years_to_maturity * coupons_per_yearmust be an integer. Stub periods are not supported.
Valuation is at a coupon date (no accrued interest).
A single flat yield is used to discount all cash flows.
Yield input conventions:
If
y_effective_per_periodis supplied, it is interpreted as the effective yield per coupon period.Otherwise,
y_rate,y_type, andy_mdefine an annual yield specification, which is converted first to annual effective yield and then to effective yield per coupon period.
Let j be the effective yield per coupon period, m the number of
coupon payments per year, and let cash flows C_k occur at coupon
periods k = 1, \dots, N. With v = 1/(1+j) and
P = \sum_{k} C_k v^k:
Macaulay duration in coupon periods:
D_p = \frac{\sum_{k=1}^{N} k \, C_k \, v^k}{P}.
Macaulay duration in years: D = D_p / m.
Modified duration with respect to j (in coupon periods):
D^*_j = D_p / (1 + j).
Modified duration with respect to the annual effective rate i
(in years): D^*_i = D / (1 + i), where i = (1+j)^m - 1.
Modified duration measures the first-order sensitivity of the bond price
to yield changes. Together with bond_convexity, it forms the
second-order Taylor approximation of price changes.
Value
A one-row tibble with:
- price
Dirty price at the given yield.
- macaulay_duration_periods
Macaulay duration in coupon periods.
- macaulay_duration_years
Macaulay duration in years.
- modified_duration_periods_j
Modified duration with respect to the effective yield per coupon period, expressed in coupon periods.
- modified_duration_years_i
Modified duration with respect to the annual effective yield, expressed in years.
- yield_per_period
Effective yield per coupon period.
- yield_effective_annual
Annual effective yield.
- coupons_per_year
Coupon frequency.
- n_periods
Total number of coupon periods.
See Also
bond_convexity, bond_price, bond_cash_flows,
bond_book_value, bond_ytm
Other bonds:
bond_book_value(),
bond_callable_price(),
bond_convexity(),
bond_price(),
bond_ytm(),
portfolio_convexity(),
portfolio_duration()
Examples
bond_duration(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
coupons_per_year = 2,
y_rate = 0.06,
y_type = "effective"
)
bond_duration(
face = 1000,
coupon_rate = 0.05,
years_to_maturity = 10,
coupons_per_year = 2,
y_effective_per_period = 0.03
)
Price of a level coupon bond from its yield
Description
Computes the dirty price of a level coupon bond at time 0 from its yield.
Usage
bond_price(
face,
coupon_rate,
years_to_maturity,
coupons_per_year = 1L,
y_effective_per_period = NULL,
y_rate = NULL,
y_type = "effective",
y_m = 1L,
redemption = NULL,
tol = 1e-10,
check = TRUE
)
Arguments
face |
Numeric scalar. Face (par) value of the bond. |
coupon_rate |
Numeric scalar. Annual coupon rate as a proportion. |
years_to_maturity |
Numeric scalar. Time to maturity in years. |
coupons_per_year |
Positive integer. Number of coupon payments per year. |
y_effective_per_period |
Optional numeric scalar. Effective yield per coupon period. If supplied, it is used directly. |
y_rate |
Optional numeric scalar. Annual yield rate value. |
y_type |
Character string indicating the annual yield type:
|
y_m |
Positive integer. Compounding frequency for nominal annual yields. |
redemption |
Numeric scalar. Redemption value at maturity.
If |
tol |
Numeric scalar. Tolerance used when checking alignment of maturity with coupon periods. |
check |
Logical scalar. If |
Details
Assumptions:
Coupons are paid in arrears at regular intervals.
-
years_to_maturity * coupons_per_yearmust be an integer. Stub periods are not supported.
No accrued interest is considered; the price is evaluated at a coupon date.
The yield may be supplied in either of two ways:
directly as an effective yield per coupon period through
y_effective_per_period, oras an annual rate specification through
y_rate,y_type, andy_m.
If an annual rate specification is supplied, it is first converted to the equivalent annual effective yield and then to the effective yield per coupon period.
Let j be the effective yield per coupon period, m the number of
coupon payments per year, and N = T \times m the total number of
periods. With coupon per period C = F r / m and discount factor
v = 1/(1+j), the price is:
P = C \cdot a_{\overline{N|}|j} + R \cdot v^N = \sum_{k=1}^{N} C_k \, v^k
where the sum runs over all cash flows (coupons and redemption) indexed by
coupon period k.
Value
Numeric scalar: dirty price of the bond at time 0.
See Also
bond_ytm, bond_cash_flows, bond_duration,
bond_convexity, bond_book_value, bond_callable_price
Other bonds:
bond_book_value(),
bond_callable_price(),
bond_convexity(),
bond_duration(),
bond_ytm(),
portfolio_convexity(),
portfolio_duration()
Examples
# 5-year annual coupon bond, yield given as annual effective
bond_price(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
coupons_per_year = 1,
y_rate = 0.06,
y_type = "effective"
)
# 10-year semiannual bond, yield given directly per coupon period
bond_price(
face = 1000,
coupon_rate = 0.05,
years_to_maturity = 10,
coupons_per_year = 2,
y_effective_per_period = 0.03
)
# Semiannual coupons, nominal annual yield convertible quarterly
bond_price(
face = 100,
coupon_rate = 0.08,
years_to_maturity = 5,
coupons_per_year = 2,
y_rate = 0.06,
y_type = "nominal_interest",
y_m = 4
)
Yield to maturity of a level coupon bond
Description
Computes the yield to maturity (YTM) of a level coupon bond given its observed dirty price at time 0.
Usage
bond_ytm(
price,
face,
coupon_rate,
years_to_maturity,
coupons_per_year = 1L,
redemption = NULL,
interval = NULL,
tol = 1e-12,
maxiter = 1000,
check = TRUE
)
Arguments
price |
Numeric scalar. Observed dirty price of the bond at time 0. |
face |
Numeric scalar. Face (par) value of the bond. |
coupon_rate |
Numeric scalar. Annual coupon rate as a proportion. |
years_to_maturity |
Numeric scalar. Time to maturity in years. Must be strictly positive. |
coupons_per_year |
Positive integer. Number of coupon payments per year. |
redemption |
Numeric scalar. Redemption value at maturity.
If |
interval |
Optional numeric vector of length 2 giving a bracket for the effective yield per coupon period. |
tol |
Numeric scalar. Tolerance passed to |
maxiter |
Positive integer. Maximum number of iterations passed to
|
check |
Logical scalar. If |
Details
The YTM is solved first as the effective yield per coupon period and then reported together with common annual equivalents.
Assumptions:
Coupons are paid in arrears at regular intervals.
Price is observed at a coupon date (no accrued interest).
-
years_to_maturity * coupons_per_yearmust be an integer. Stub periods are not supported.
The effective yield per coupon period j is the solution to
P = \sum_{k=1}^{N} C_k \, (1+j)^{-k}
where P is the observed price and C_k are the bond's cash
flows (coupons and redemption) at coupon periods k = 1, \dots, N.
The root is found numerically using uniroot. If no interval
is supplied, the function automatically brackets the root starting from
(-0.999999, \, 0.10) and progressively widens the upper bound until
a sign change is detected.
From the per-period yield, the annual equivalents are:
j^{(m)} = m \cdot j, \qquad i = (1 + j)^m - 1.
Value
A one-row tibble with columns:
- price
Input dirty price.
- i_period
Effective yield per coupon period.
- j_nominal
Nominal annual yield convertible
coupons_per_yeartimes per year (=coupons_per_year * i_period). Whencoupons_per_year = 2, this is the bond-equivalent yield.- i_effective_annual
Annual effective yield.
See Also
bond_price, bond_cash_flows, bond_duration,
bond_convexity, bond_callable_price
Other bonds:
bond_book_value(),
bond_callable_price(),
bond_convexity(),
bond_duration(),
bond_price(),
portfolio_convexity(),
portfolio_duration()
Examples
bond_ytm(
price = 100,
face = 100,
coupon_rate = 0.06,
years_to_maturity = 5,
coupons_per_year = 1
)
bond_ytm(
price = 950,
face = 1000,
coupon_rate = 0.05,
years_to_maturity = 10,
coupons_per_year = 2
)
Sample bond contracts for fixed-income examples
Description
A small pedagogical dataset containing bond contracts for pricing, yield-to-maturity, duration, and convexity examples.
A small pedagogical dataset containing bond contracts for pricing, yield-to-maturity, duration, and convexity examples.
Usage
bonds_sample
bonds_sample
Format
A tibble with 5 rows and 8 variables:
- bond_id
Bond identifier.
- face_value
Face value of the bond.
- coupon_rate
Annual coupon rate.
- coupon_frequency
Number of coupon payments per year.
- maturity_years
Maturity in years.
- yield_rate
Annual nominal yield rate consistent with coupon frequency.
- bond_type
Short bond type label.
- price
Theoretical bond price computed from the listed yield rate.
A tibble with 5 rows and 8 variables:
- bond_id
Bond identifier.
- face_value
Face value of the bond.
- coupon_rate
Annual coupon rate.
- coupon_frequency
Number of coupon payments per year.
- maturity_years
Maturity in years.
- yield_rate
Annual nominal yield rate consistent with coupon frequency.
- bond_type
Short bond type label.
- price
Theoretical bond price computed from the listed yield rate.
Source
Synthetic pedagogical data created for tidyactuarial examples.
Synthetic pedagogical data created for tidyactuarial examples.
Examples
data(bonds_sample)
bonds_sample |>
dplyr::select(bond_id, face_value, coupon_rate, maturity_years, price)
data(bonds_sample)
bonds_sample |>
dplyr::select(bond_id, face_value, coupon_rate, maturity_years, price)
Sample cash flows for interest theory examples
Description
A small pedagogical dataset containing cash-flow scenarios for present value, future value, net present value, internal rate of return, equations of value, and cash-flow diagrams.
A small pedagogical dataset containing cash-flow scenarios for present value, future value, net present value, internal rate of return, equations of value, and cash-flow diagrams.
Usage
cash_flows_sample
cash_flows_sample
Format
A tibble with 19 rows and 5 variables:
- scenario_id
Scenario identifier.
- time
Payment time.
- amount
Cash-flow amount. Negative values represent outflows and positive values represent inflows.
- cashflow_type
Type of cash flow.
- description
Short description of the cash flow.
A tibble with 19 rows and 5 variables:
- scenario_id
Scenario identifier.
- time
Payment time.
- amount
Cash-flow amount. Negative values represent outflows and positive values represent inflows.
- cashflow_type
Type of cash flow.
- description
Short description of the cash flow.
Source
Synthetic pedagogical data created for tidyactuarial examples.
Synthetic pedagogical data created for tidyactuarial examples.
Examples
data(cash_flows_sample)
cash_flows_sample |>
dplyr::filter(scenario_id == "investment_project")
data(cash_flows_sample)
cash_flows_sample |>
dplyr::filter(scenario_id == "investment_project")
Build an annual commutation table (discrete ages)
Description
Constructs classical annual commutation functions Dx, Nx, Sx, Cx, Mx, Rx from a lifetable defined at integer ages and an annual effective interest rate i.
Usage
commutation_table(lt, i, check = TRUE)
Arguments
lt |
A lifetable object as produced by |
i |
Numeric. Annual effective interest rate (must satisfy |
check |
Logical. If |
Value
A tibble with columns x, lx, dx, v,
Dx, Nx, Sx, Cx, Mx, Rx.
Spot discount factor
Description
Computes the discount factor implied by a spot rate for a given time.
Usage
discount_factor_spot(
time,
rate,
rate_type = "effective",
m = 1L,
output = c("value", "table")
)
Arguments
time |
Numeric vector of times in years. Each value must be greater than or equal to 0. |
rate |
Numeric vector of spot-rate values. |
rate_type |
Character vector indicating the spot-rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency for nominal spot-rate inputs. |
output |
Character string. Use |
Details
The spot rate may be supplied in FM-style notation:
annual effective rate,
nominal annual interest rate,
nominal annual discount rate,
force of interest.
Internally, the supplied spot rate is first converted to the equivalent
annual effective rate using standardize_interest. The discount
factor is then computed as
v(t) = (1+i)^{-t}
If t = 0, the discount factor is 1.
Input vectors must have length 1 or a common length. Missing values are propagated.
Value
If output = "value", a numeric vector of discount factors.
If output = "table", a tibble with input values, standardized rates,
and discount factors.
See Also
standardize_interest, present_value
Other interest:
forward_rate(),
interest_equivalents(),
standardize_interest(),
yield_curve()
Examples
# Numeric discount factor
discount_factor_spot(
time = 3,
rate = 0.05
)
# Vectorized example
discount_factor_spot(
time = c(1, 2, 3),
rate = c(0.05, 0.055, 0.06)
)
# FM-style input with nominal annual interest
discount_factor_spot(
time = 2,
rate = 0.08,
rate_type = "nominal_interest",
m = 2
)
# Tibble output for teaching or auditing
discount_factor_spot(
time = c(1, 2, 3),
rate = c(0.05, 0.055, 0.06),
output = "table"
)
Expected future lifetime from an annual life table
Description
Computes the curtate or complete expected future lifetime at integer age
x, optionally restricted to a temporary horizon of t years.
Usage
e_x(
lt,
x,
t = NULL,
type = c("curtate", "complete"),
frac,
tidy = FALSE,
check = TRUE,
tol = 1e-10
)
Arguments
lt |
A life table object as produced by |
x |
Integer age(s). |
t |
Optional nonnegative numeric duration(s). If |
type |
Character: |
frac |
Fractional-age assumption for |
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks. |
Details
Curtate life expectancy (Finan, Section 23.7):
e_x = \sum_{k=1}^{\omega - x} {}_k p_x = \frac{1}{\ell_x}
\sum_{k=1}^{\omega - x} \ell_{x+k}.
The t-year temporary curtate expectancy is (Finan, Sec. 23.7):
e_{x:\overline{t}|} = \sum_{k=1}^{t} {}_k p_x.
Complete life expectancy (Finan, Section 23.3):
\breve{e}_x = \int_0^{\omega - x} {}_t p_x \, dt =
\frac{T_x}{\ell_x}.
The integral is decomposed year-by-year. Within each year, the
within-year survival integral \int_0^s {}_u p_y \, du is evaluated
in closed form under the selected fractional-age assumption
(Finan, Section 24):
UDD (Sec. 24.1):
\int_0^s {}_u p_y \, du = s - \frac{1}{2} s^2 q_yCF (Sec. 24.2):
\int_0^s {}_u p_y \, du = (1 - p_y^s) / (-\ln p_y)Balducci (Sec. 24.3):
\int_0^s {}_u p_y \, du = \frac{p_y}{q_y} \ln \left( \frac{p_y + q_y s}{p_y} \right)
Under UDD, the complete expectancy satisfies the well-known approximation (Finan, Example 20.24):
\breve{e}_x \approx e_x + \frac{1}{2}.
Value
A numeric vector of expected future lifetimes, or a tibble if
tidy = TRUE with columns x, t, type,
frac, ex.
Expected future lifetime for two independent lives
Description
Computes the expected future lifetime for two independent lives aged
x and y, for either joint-life (first death) or
last-survivor (second death).
Usage
e_xy(
lt,
x,
y,
t = NULL,
type = c("curtate", "complete"),
frac,
cohort = c("first", "last"),
tidy = FALSE,
check = TRUE,
tol = 1e-10
)
Arguments
lt |
A life table data frame with columns |
x |
Integer actuarial age for life 1. |
y |
Integer actuarial age for life 2. |
t |
Optional nonnegative numeric duration(s). If |
type |
Character: |
frac |
Fractional-age assumption for |
cohort |
Two-life cohort: |
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks. |
Details
Curtate expectation (Finan, Section 56.4 / Section 57):
e_{xy} = \sum_{k=1}^{\infty} {}_kp_{xy}, \quad
e_{\overline{xy}} = \sum_{k=1}^{\infty} {}_kp_{\overline{xy}}.
Complete expectation (Finan, Section 56.4):
\mathring{e}_{xy} = \int_0^{\infty} {}_tp_{xy} \, dt.
The integral is decomposed year-by-year. Within each year, the survival integral for the two-life status is computed numerically via composite trapezoid (80-point grid), since closed-form expressions for joint/last survivor under fractional-age assumptions are complex.
Key identity (Finan, Example 57.4):
\mathring{e}_{\overline{xy}} = \mathring{e}_x + \mathring{e}_y
- \mathring{e}_{xy}.
This can be used to cross-validate results.
Value
Numeric vector, or tibble if tidy = TRUE.
See Also
e_x for single-life expectancy,
t_pxy for two-life survival probabilities,
annuity_xy for two-life annuity APVs.
Examples
lt <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)
# Curtate joint-life expectancy (Finan, Sec. 56.4)
e_xy(lt, x = 60, y = 62, type = "curtate", cohort = "first")
# Curtate last-survivor expectancy (Finan, Sec. 57)
e_xy(lt, x = 60, y = 62, type = "curtate", cohort = "last")
# Verify identity (Finan, Example 57.4):
# e_{xy-bar} = e_x + e_y - e_xy
e_joint <- e_xy(lt, x = 60, y = 62, type = "curtate", cohort = "first")
e_last <- e_xy(lt, x = 60, y = 62, type = "curtate", cohort = "last")
e_x_val <- e_x(lt, x = 60, type = "curtate")
e_y_val <- e_x(lt, x = 62, type = "curtate")
c(last_surv = e_last, sum_minus_joint = e_x_val + e_y_val - e_joint)
# Complete joint-life expectancy under UDD
e_xy(lt, x = 60, y = 62, type = "complete", frac = "UDD", cohort = "first")
# Temporary: 3-year curtate joint-life
e_xy(lt, x = 60, y = 62, t = 3, type = "curtate", cohort = "first")
# Tidy output
e_xy(lt, x = 60, y = 62, type = "curtate", cohort = "first", tidy = TRUE)
Compute an implied forward rate from a discrete spot curve
Description
Returns the annual effective forward rate implied between two maturities from a discrete yield curve stored in tibble-first format.
Usage
forward_rate(
.data = NULL,
term = NULL,
spot = NULL,
t_start = NULL,
t_end = NULL,
col_term = "term",
col_spot = "spot",
col_t_start = "t_start",
col_t_end = "t_end",
method = c("exact", "linear"),
plot = FALSE,
.out = "forward_rate",
.out_plot = "forward_rate_plot",
.keep = c("all", "used", "none"),
.na = c("propagate", "error", "drop")
)
Arguments
.data |
A data.frame or tibble. If |
term |
Numeric vector of maturities when |
spot |
Numeric vector of annual effective spot rates when
|
t_start |
Numeric scalar giving the start maturity when
|
t_end |
Numeric scalar giving the end maturity when
|
col_term |
Name of the list-column containing maturities. |
col_spot |
Name of the list-column containing spot rates. |
col_t_start |
Name of the numeric column containing the start maturity. |
col_t_end |
Name of the numeric column containing the end maturity. |
method |
Spot extraction method: |
plot |
Logical; if |
.out |
Name of the output column containing the forward rate. |
.out_plot |
Name of the output list-column containing |
.keep |
One of |
.na |
NA handling policy: |
Details
Each row is treated as one curve (one case). For tibble input,
col_term and col_spot must be list-columns of equal-length
numeric vectors, and col_t_start and col_t_end must be
numeric columns giving the forward interval for each row.
The implied forward rate is computed from the spot curve through:
(1+i_1)^{t_1}(1+f)^{t_2-t_1}=(1+i_2)^{t_2}
so that
f_{t_1,t_2} = \left(\frac{(1+i_2)^{t_2}}{(1+i_1)^{t_1}}\right)^{1/(t_2-t_1)} - 1
Two extraction methods are supported for the spot rates:
-
"exact": requires thatt_startandt_endmatch curve nodes. -
"linear": uses linear interpolation between adjacent nodes.
No extrapolation is performed outside the observed maturity range.
Value
A tibble. By default it returns the original columns plus a new
numeric column named by .out. If plot = TRUE, it also adds
a list-column named by .out_plot containing ggplot2 objects.
References
Marcel B. Finan, A Basic Course in the Theory of Interest and Derivatives Markets: A Preparation for the Actuarial Exam FM/2, Section 53: The Term Structure of Interest Rates and Yield Curves.
Kellison, S. G. The Theory of Interest, Chapter 10: The Term Structure of Interest Rates.
See Also
yield_curve, discount_factor_spot,
standardize_interest
Other interest:
discount_factor_spot(),
interest_equivalents(),
standardize_interest(),
yield_curve()
Examples
# Simple example: exact forward rate
forward_rate(
term = c(1, 2, 3, 4, 5),
spot = c(0.040, 0.045, 0.048, 0.050, 0.051),
t_start = 2,
t_end = 5
)
# Medium example: interpolated forward rates for multiple curves
curves <- tibble::tibble(
curve_id = c("A", "B"),
term = list(c(1, 2, 3, 5), c(1, 3, 5, 7)),
spot = list(c(0.04, 0.05, 0.055, 0.06),
c(0.03, 0.035, 0.04, 0.045)),
t_start = c(2, 2),
t_end = c(4, 6)
)
forward_rate(
curves,
method = "linear",
plot = TRUE
)
Future value of a single payment
Description
Computes the future value of a payment invested at time 0 and accumulated to a given time, using the annual effective interest rate implied by the supplied rate specification.
Usage
future_value(
amount,
rate,
rate_type = "effective",
m = 1,
time,
output = c("value", "table")
)
Arguments
amount |
Numeric vector of initial payment amounts. |
rate |
Numeric vector of rate values. |
rate_type |
Character vector indicating the rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency
for nominal rates. Ignored for |
time |
Numeric vector of times in years from valuation to accumulation. |
output |
Character string. Use |
Details
The future value is computed as
FV = C(1+i)^t
where i is the annual effective interest rate.
The input interest rate may be supplied as:
annual effective interest rate,
nominal annual interest rate,
nominal annual discount rate,
force of interest.
Internally, all rate specifications are first converted to the equivalent
annual effective interest rate using standardize_interest.
Input vectors must have length 1 or a common length. Missing values are propagated. This function does not accept dates.
Value
If output = "value", a numeric vector of future values.
If output = "table", a tibble with input values, equivalent rates,
accumulation factors, and future values.
See Also
standardize_interest, present_value
Other time-value:
fv_flow(),
irr_flow(),
irr_flow_multi(),
plot_cash_flow(),
present_value(),
pv_flow()
Examples
# Numeric future value
future_value(amount = 1000, rate = 0.08, time = 3)
# Nominal interest converted monthly
future_value(
amount = 1000,
rate = 0.12,
rate_type = "nominal_interest",
m = 12,
time = 5
)
# Tibble output for teaching or auditing
future_value(
amount = 1000,
rate = 0.08,
time = 3,
output = "table"
)
# Vectorized example
future_value(
amount = c(1000, 2500, 4000),
rate = c(0.08, 0.10, 0.12),
rate_type = c("effective", "nominal_interest", "force"),
m = c(1, 12, 1),
time = c(3, 5, 2)
)
Future value of a general cash flow
Description
Computes the future value of a cash-flow vector under either:
a constant interest-rate specification, or
a term structure of spot rates, one rate per cash flow.
Usage
fv_flow(
payment,
rate,
type = "effective",
m = 1L,
time = NULL,
date = NULL,
day_count = c("act/365", "act/360"),
tol = 1e-10
)
Arguments
payment |
Numeric vector of cash flows. |
rate |
Numeric scalar or numeric vector of rate values. |
type |
Character vector indicating the rate type:
|
m |
Positive integer vector giving the compounding frequency for
nominal rates. May have length 1 or the same length as |
time |
Optional numeric vector of payment times in years. |
date |
Optional vector of payment dates. If supplied, the earliest date is treated as time 0. |
day_count |
Day-count convention used to convert dates to year fractions.
One of |
tol |
Numeric tolerance used in internal checks. |
Details
The cash flow is supplied explicitly through payment. Its timing is
supplied either through time (in years) or date (calendar
dates). If date is supplied, the earliest date is taken as time 0.
The future value is accumulated to the latest payment time (or latest date).
Interest-rate input:
If
ratehas length 1, the same rate is used for all payments.If
ratehas the same length aspayment, each rate is interpreted as the annual effective spot rate associated with the corresponding payment time.
Rate types may be supplied in FM-style notation:
annual effective rate
i,nominal annual interest rate
j^{(m)},nominal annual discount rate
d^{(m)},force of interest
\delta.
Internally, all supplied rates are converted to annual effective rates using
standardize_interest. When rate is a vector, the
accumulation uses the implied discount-factor ratio under the spot-rate
interpretation.
Let T be the latest payment time (the accumulation horizon). For
each payment C_k at time t_k with annual effective spot rate
i_k, the future value contribution is
FV_k = C_k \cdot \frac{(1+i_T)^T}{(1+i_k)^{t_k}}
and the total future value is FV = \sum_k FV_k.
When a single constant rate is supplied, i_k = i_T = i for all
k and the formula simplifies to
FV = \sum_k C_k (1+i)^{T - t_k}.
Value
Numeric scalar: the future value of the cash flow accumulated to the latest payment time.
See Also
pv_flow, future_value,
standardize_interest
Other time-value:
future_value(),
irr_flow(),
irr_flow_multi(),
plot_cash_flow(),
present_value(),
pv_flow()
Examples
# Constant annual effective rate
fv_flow(
payment = c(100, 150, 200),
rate = 0.08,
type = "effective",
time = c(0, 1, 2)
)
# Spot rates, one per payment
fv_flow(
payment = c(100, 150, 200),
rate = c(0.05, 0.055, 0.06),
type = "effective",
time = c(1, 2, 3)
)
# Using dates; earliest date is taken as t = 0
fv_flow(
payment = c(100, 150, 200),
rate = c(0.05, 0.055, 0.06),
type = "effective",
date = as.Date(c("2026-01-10", "2027-01-10", "2028-01-10"))
)
# Nominal rates by payment
fv_flow(
payment = c(100, 100, 100),
rate = c(0.12, 0.12, 0.12),
type = "nominal_interest",
m = c(12, 12, 12),
time = c(1, 2, 3)
)
Duration-based immunization with multiple assets
Description
Computes asset weights that duration-immunize a stream of liabilities using two or more assets. The method enforces:
Present value of assets = PV of liabilities;
Macaulay duration of assets = Macaulay duration of liabilities.
Usage
immunize_duration(liabilities, t_liabilities, pv_assets, duration_assets, i)
Arguments
liabilities |
Numeric vector with liability payments. |
t_liabilities |
Numeric vector of the same length as |
pv_assets |
Numeric vector with present values (prices) of each asset
evaluated at the same yield rate |
duration_assets |
Numeric vector with the Macaulay duration of each
asset, expressed in the same time units as |
i |
Yield rate used to discount the liabilities (effective per period). |
Details
For exactly two assets, a closed-form solution is used. For three or more assets, a minimum-norm solution is computed via linear algebra.
Let PV_L and D_L be the present value and Macaulay
duration of the liability stream at yield i. Let PV_j and
D_j be the present value and duration of asset j. The
weights w_j are chosen so that:
\sum_j w_j PV_j = PV_L
and
\frac{\sum_j w_j PV_j D_j}{\sum_j w_j PV_j} = D_L
For two assets, the closed-form solution is:
w_1 = \frac{PV_L (D_L - D_2)}{PV_1 (D_1 - D_2)}, \quad w_2 = \frac{PV_L - w_1 PV_1}{PV_2}
For k \geq 3 assets, the minimum-norm solution of the linear
system A w = b is computed, where A is a 2 \times k
matrix with rows (PV_1, \ldots, PV_k) and
(PV_1 D_1, \ldots, PV_k D_k), and
b = (PV_L,\; PV_L D_L)^T.
Value
A tibble with:
- weight_asset
Numeric vector of asset weights (amounts of each asset).
- PV_liabilities
Present value of the liabilities.
- Duration_liabilities
Macaulay duration of the liabilities.
- PV_assets
Present value of the immunized asset portfolio.
- Duration_assets
Macaulay duration of the asset portfolio.
- n_assets
Number of assets used.
See Also
immunize_duration_convexity,
bond_duration, bond_convexity
Other immunization:
immunize_duration_convexity(),
plot_immunization_gap()
Examples
# Two-asset immunization
immunize_duration(
liabilities = c(5000, 8000),
t_liabilities = c(3, 7),
pv_assets = c(100, 200),
duration_assets = c(3, 7),
i = 0.05
)
# Three-asset immunization (minimum-norm)
immunize_duration(
liabilities = c(5000, 8000),
t_liabilities = c(3, 7),
pv_assets = c(100, 150, 200),
duration_assets = c(2, 5, 8),
i = 0.05
)
Duration and convexity immunization with multiple assets
Description
Computes asset weights that immunize a stream of liabilities using three or more assets, enforcing:
Present value of assets = PV of liabilities;
Macaulay duration of assets = Macaulay duration of liabilities;
Convexity of assets = convexity of liabilities.
Usage
immunize_duration_convexity(
liabilities,
t_liabilities,
pv_assets,
duration_assets,
convexity_assets,
i
)
Arguments
liabilities |
Numeric vector with liability payments. |
t_liabilities |
Numeric vector with the times (in periods) at which
each liability payment occurs. Must have the same length as
|
pv_assets |
Numeric vector with present values (prices) of each asset
evaluated at the same yield rate |
duration_assets |
Numeric vector with the Macaulay duration of each
asset, in the same time units as |
convexity_assets |
Numeric vector with the discrete convexity of each
asset, in the same time units (periods) as |
i |
Yield rate used to discount the liabilities (effective per period). |
Details
For exactly three assets, the system is solved directly. For four or more assets, a minimum-norm solution is computed via linear algebra.
Let PV_L, D_L, and C_L be the present
value, Macaulay duration, and discrete convexity of the liability stream at
yield i. The discrete convexity of the liabilities is computed as:
C_L = \frac{\sum_t L_t \, t(t+1) \, v^{t+2}}{PV_L}
where v = 1/(1+i).
The weights w_j satisfy the 3 \times k system
A w = b, where the rows of A are
(PV_j), (PV_j D_j), (PV_j C_j), and
b = (PV_L,\; PV_L D_L,\; PV_L C_L)^T.
For k = 3 assets, the system is square and solved directly.
For k \geq 4 assets, the minimum-norm solution
w = A^T (A A^T)^{-1} b is computed.
Value
A tibble with:
- weight_asset
Numeric vector of asset weights (amounts of each asset).
- PV_liabilities
Present value of the liabilities.
- Duration_liabilities
Macaulay duration of the liabilities.
- Convexity_liabilities
Discrete convexity of the liabilities.
- PV_assets
Present value of the asset portfolio.
- Duration_assets
Macaulay duration of the asset portfolio.
- Convexity_assets
Discrete convexity of the asset portfolio.
- n_assets
Number of assets used.
See Also
immunize_duration, bond_duration,
bond_convexity
Other immunization:
immunize_duration(),
plot_immunization_gap()
Examples
# Three-asset immunization (exact solution)
immunize_duration_convexity(
liabilities = c(5000, 8000, 10000),
t_liabilities = c(3, 5, 7),
pv_assets = c(100, 150, 200),
duration_assets = c(2, 5, 8),
convexity_assets = c(6, 30, 72),
i = 0.05
)
# Four-asset immunization (minimum-norm)
immunize_duration_convexity(
liabilities = c(5000, 8000, 10000),
t_liabilities = c(3, 5, 7),
pv_assets = c(100, 120, 150, 200),
duration_assets = c(2, 4, 6, 8),
convexity_assets = c(6, 20, 42, 72),
i = 0.05
)
Actuarial present value of a life insurance with variable k-thly benefits
Description
Computes the actuarial present value of a life insurance where the death
benefit may vary by subperiod (k payments per year) and is payable
at the end of the subperiod of death.
Usage
insurance_variable_k(
lt,
x,
i,
benefit,
n = NULL,
m = 0,
k = 12,
frac,
tidy = FALSE,
check = TRUE
)
Arguments
lt |
A life table object or data frame containing at least |
x |
Integer actuarial age at issue. |
i |
Effective annual interest rate (must be |
benefit |
Numeric vector of benefits by subperiod, or a function of
time returning the benefit at time |
n |
Optional term in years. If |
m |
Nonnegative integer deferral period in years (default |
k |
Number of subperiods per year (default |
frac |
Fractional-age assumption used in survival probabilities:
|
tidy |
Logical. If |
check |
Logical. If |
Details
Let k be the number of subperiods per year and N = nk the
total number of subperiods. With deferral m, the actuarial present
value at age x is (generalizing Finan, Section 29):
\text{APV} = \sum_{j=1}^{N} v^{m + t_j} \cdot b_j \cdot
\left({{}_{m + t_{j-1}}}p_x - {{}_{m + t_j}}p_x\right)
where t_j = j/k and b_j is the benefit payable if death
occurs in the interval (m + t_{j-1},\, m + t_j].
This is the general form that encompasses:
-
Level insurance: constant
b_j = 1reduces toA^{(k)1}_{x:\overline{n}|}(Finan, Section 31). -
Increasing insurance:
b_j = \lceil j/k \rceilgives(IA)^{(k)}_{x:\overline{n}|}(Finan, Section 29.3). -
Decreasing insurance:
b_j = n - \lfloor(j-1)/k\rfloorgives(DA)^{(k)}_{x:\overline{n}|}(Finan, Section 29.3). -
Credit insurance:
b_j = B(t_j)whereB(t)is the outstanding loan balance.
The benefit may be supplied either as a numeric vector indexed by subperiod or as a function of time.
Fractional survival probabilities are computed via t_px
under the selected assumption (Finan, Section 24).
Value
A numeric actuarial present value, or a one-row tibble if
tidy = TRUE.
See Also
insurance_x for level-benefit life insurance,
annuity_x for life annuity APVs,
t_px for survival probabilities,
Monte Carlo simulation tools may be used for empirical variance estimation.
Examples
lt <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)
# Monthly insurance with increasing benefits (Finan, Sec. 29.3 style)
insurance_variable_k(
lt, x = 60, i = 0.05,
benefit = seq(100, 1200, length.out = 12),
n = 1, k = 12
)
# Credit-style insurance with declining outstanding balance
balance <- function(t) 2000 * exp(-0.3 * t)
insurance_variable_k(
lt, x = 60, i = 0.05,
benefit = balance,
n = 1, k = 12
)
# Level benefit = 1 should match a term insurance
# (approximately, since insurance_x uses annual and this uses k-thly)
insurance_variable_k(lt, x = 60, i = 0.05, benefit = 1, n = 5, k = 1)
insurance_x(mortality_table = lt, age = 60, rate = 0.05, term_years = 5, insurance_type = "term")
# 2-year deferred, 3-year term with monthly varying benefits
insurance_variable_k(
lt, x = 60, i = 0.05,
benefit = rep(1000, 36),
n = 3, m = 2, k = 12
)
# Tidy output
insurance_variable_k(
lt, x = 60, i = 0.05,
benefit = rep(1000, 12), n = 1, k = 12, tidy = TRUE
)
Actuarial present value of a life insurance
Description
Computes the actuarial present value of a discrete life insurance using a life table.
Usage
insurance_x(
mortality_table,
age,
rate,
rate_type = "effective",
m = 1L,
term_years = Inf,
deferral_years = 0L,
insurance_type = c("whole", "term", "endowment"),
benefit = 1,
output = c("value", "table")
)
Arguments
mortality_table |
A life table as produced by |
age |
Integer actuarial age. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. Ignored
for |
term_years |
Integer term in years. Required for
|
deferral_years |
Integer deferral period in years. |
insurance_type |
Character string. One of |
benefit |
Numeric scalar. Benefit amount. |
output |
Character string. Use |
Details
The benefit is paid at the end of the year of death for whole-life and term insurance. For endowment insurance, the same benefit is paid either at death within the term or at the end of the term if the life survives.
Supported contracts:
-
"whole": whole-life insurance. -
"term": n-year term insurance. -
"endowment": n-year endowment insurance.
This function uses standard annual discrete identities.
For whole-life insurance,
A_x = 1 - d\,\ddot{a}_x,
where d = i/(1+i).
For n-year term insurance,
A^1_{x:\overline{n}|} =
1 - d\,\ddot{a}_{x:\overline{n}|} - v^n\,{}_np_x.
For n-year endowment insurance,
A_{x:\overline{n}|} =
1 - d\,\ddot{a}_{x:\overline{n}|}.
Deferral is handled by multiplying the value at age
age + deferral_years by v^h {}_hp_x, where
h = deferral_years.
Value
If output = "value", a numeric scalar containing the actuarial present
value.
If output = "table", a one-row tibble with the main input values,
equivalent interest rate, deferral factor, pure endowment factor, annuity-due
value used in the identity, and APV.
See Also
annuity_x, premium_x,
reserve_x, t_Ex, insurance_xy
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:65,
lx = c(100000, 99000, 97500, 95500, 93000, 90000)
)
# Whole-life insurance
insurance_x(
mortality_table = lt,
age = 60,
rate = 0.06,
insurance_type = "whole"
)
# 5-year term insurance
insurance_x(
mortality_table = lt,
age = 60,
rate = 0.06,
term_years = 5,
insurance_type = "term"
)
# 5-year endowment insurance
insurance_x(
mortality_table = lt,
age = 60,
rate = 0.06,
term_years = 5,
insurance_type = "endowment"
)
# Deferred whole-life insurance
insurance_x(
mortality_table = lt,
age = 60,
rate = 0.06,
deferral_years = 2,
insurance_type = "whole"
)
# Table output
insurance_x(
mortality_table = lt,
age = 60,
rate = 0.06,
term_years = 5,
insurance_type = "term",
output = "table"
)
Cause-specific term/whole-life insurance APV under multiple decrements: insurance_xj
Description
Computes the actuarial present value (APV) of an annual (discrete) insurance
that pays benefit at the end of the year of decrement by a specified
cause j, using a multiple decrement table produced by
md_table.
Usage
insurance_xj(
md,
x,
i,
cause,
product = c("whole", "term"),
benefit = 1,
n = NULL,
m = 0L,
tidy = FALSE,
check = TRUE,
tol = 1e-10
)
A_xj(
md,
x,
i,
cause,
product = c("whole", "term"),
benefit = 1,
n = NULL,
m = 0L,
tidy = FALSE,
check = TRUE,
tol = 1e-10
)
Arguments
md |
A multiple decrement table produced by |
x |
Integer age(s) at issue. |
i |
Annual effective interest rate(s). Must satisfy |
cause |
Character. Name of the cause column in |
product |
Character. Insurance type: |
benefit |
Numeric benefit amount payable at the end of the year of
decrement by the specified cause (default |
n |
Integer term length in years (required when |
m |
Integer deferment in years (default |
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks. |
Details
Let q_{x+k}^{(j)} be the one-year decrement probability for cause j
at age x+k, and p_{x+r}^{(\tau)} be the one-year total survival
probability (any cause) at age x+r. For a product with deferment m
and term n, with benefit payable at the end of the year of decrement by
cause j, the APV is:
\sum_{k=m}^{m+n-1} v^{k+1}\left(\prod_{r=0}^{k-1} p_{x+r}^{(\tau)}\right) q_{x+k}^{(j)},
where v = (1+i)^{-1} and i is the annual effective interest rate.
If product = "whole", the function sets n to the remaining length
of the table after deferment (i.e., whole life over the available ages).
Value
Numeric vector of APVs (or a tibble if tidy = TRUE).
See Also
t_qxj for cause-specific decrement probabilities,
lt_tau to build a single-decrement lifetable for the total decrement.
Examples
qx_df <- tibble::tibble(
x = 30:35,
q_death = c(0.001, 0.0012, 0.0014, 0.0017, 0.0020, 1.0000),
q_disability = c(0.002, 0.0021, 0.0022, 0.0023, 0.0024, 0.0000)
)
md <- md_table(qx_df, radix = 1e5, close = TRUE)
# 5-year term cause-specific insurance for death, i = 5%
insurance_xj(md, x = 30, i = 0.05, cause = "q_death", product = "term", n = 5)
# Whole-life (over available ages), 2-year deferred
insurance_xj(md, x = 30, i = 0.05, cause = "q_death", product = "whole", m = 2, tidy = TRUE)
Actuarial present value of a two-life insurance
Description
Computes the actuarial present value of a discrete two-life insurance with benefit payable at the end of the year of the triggering death, assuming independent future lifetimes.
Usage
insurance_xy(
mortality_table,
age_x = NULL,
age_y = NULL,
rate = NULL,
rate_type = NULL,
m = NULL,
insurance_type = c("whole", "term", "endowment"),
cohort = c("first", "last"),
term_years = Inf,
deferment_years = 0L,
benefit = 1,
output = c("value", "table")
)
Arguments
mortality_table |
Either a single life table used for both lives, or a
list of two life tables |
age_x |
Integer actuarial age for the first life. |
age_y |
Integer actuarial age for the second life. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. |
insurance_type |
Character string. One of |
cohort |
Character string. Use |
term_years |
Term in years. Required for term and endowment insurance.
Use |
deferment_years |
Integer deferment period in years. |
benefit |
Numeric scalar. Insurance benefit. |
output |
Character string. Use |
Details
Supported contracts:
whole life insurance,
term insurance,
endowment insurance.
The cohort determines the two-life status:
-
cohort = "first": joint-life status; insurance is triggered by the first death. -
cohort = "last": last-survivor status; insurance is triggered by the second death.
This function uses the standard identities that express insurance values through two-life annuity-due values.
For a whole life contract:
A = 1 - d \ddot{a}
For an n-year term insurance:
A^1_{:\overline{n}|} =
1 - d\ddot{a}_{:\overline{n}|} - v^n\,{}_np
For an endowment insurance:
A_{:\overline{n}|} =
1 - d\ddot{a}_{:\overline{n}|}
The function assumes independent future lifetimes.
Value
If output = "value", a numeric scalar.
If output = "table", a one-row tibble.
See Also
annuity_xy(), premium_xy(), insurance_x(), t_pxy()
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:110,
lx = seq(100000, 0, length.out = 51)
)
insurance_xy(
mortality_table = lt,
age_x = 60,
age_y = 62,
rate = 0.06,
insurance_type = "whole",
cohort = "first"
)
lt |>
life_contract(lives = "joint", age_x = 60, age_y = 62, rate = 0.06) |>
insurance_xy(
insurance_type = "term",
term_years = 4,
cohort = "first"
)
Equivalent interest rates in FM actuarial notation
Description
Converts a single interest-rate specification into equivalent
actuarial rates for the same compounding frequency m.
Usage
interest_equivalents(
type = c("effective", "nominal_interest", "nominal_discount", "force"),
rate,
m = 1L
)
Arguments
type |
Character string indicating the input rate type.
Must be one of |
rate |
Numeric scalar giving the rate value. |
m |
Positive integer scalar giving the compounding frequency for nominal rates. |
Details
Internally, the supplied rate is first converted to the annual
effective interest rate i using standardize_interest.
Given the annual effective interest rate i, the equivalents are:
- Effective discount rate:
d = i/(1+i)- Discount factor:
v = 1/(1+i)- Force of interest:
\delta = \ln(1+i)- Nominal interest:
j^{(m)} = m[(1+i)^{1/m} - 1]- Nominal discount:
d^{(m)} = m[1 - (1+i)^{-1/m}]
Value
A tibble with columns:
- family
Rate family:
"effective","discount","force","nominal_interest", or"nominal_discount".- notation
Actuarial notation for the equivalent rate.
- m
Compounding frequency used for nominal rates.
- description
Human-readable description.
- value
Equivalent rate value.
See Also
standardize_interest,
discount_factor_spot
Other interest:
discount_factor_spot(),
forward_rate(),
standardize_interest(),
yield_curve()
Examples
interest_equivalents("nominal_interest", rate = 0.18, m = 4)
interest_equivalents("nominal_discount", rate = 0.10, m = 12)
interest_equivalents("force", rate = 0.12)
interest_equivalents("effective", rate = 0.08)
# Batch use with purrr
if (requireNamespace("purrr", quietly = TRUE) &&
requireNamespace("tibble", quietly = TRUE)) {
cases <- tibble::tibble(
type = c("effective", "force"),
rate = c(0.08, 0.12),
m = c(1, 1)
)
purrr::pmap(cases, interest_equivalents)
}
Internal rate of return for a cash flow
Description
Computes the internal rate of return (IRR) of a cash flow by finding the annual effective rate that makes its present value equal to zero.
Usage
irr_flow(
payment,
time = NULL,
date = NULL,
nominal_m = 1L,
interval = c(-0.99, 10),
tol = 1e-10,
maxiter = 1000,
day_count = c("act/365", "act/360")
)
Arguments
payment |
Numeric vector of cash flows. |
time |
Optional numeric vector of cash-flow times in years. |
date |
Optional vector of cash-flow dates. If supplied, the earliest date is treated as time 0. |
nominal_m |
Positive integer used only to report an equivalent nominal
annual interest rate convertible |
interval |
Numeric vector of length 2 giving the search interval for the
annual effective IRR. Default is |
tol |
Numeric tolerance passed to |
maxiter |
Maximum number of iterations passed to
|
day_count |
Day-count convention used when |
Details
The cash flow is supplied explicitly through payment. Its timing is
given either through time (in years) or date (calendar dates).
If date is supplied, the earliest date is treated as time 0.
The IRR returned is therefore interpreted as an annual effective rate.
The IRR is defined as the rate r satisfying
\sum_{k} C_k (1+r)^{-t_k} = 0
where C_k are the cash flows and t_k the corresponding times
in years.
The root is found using uniroot over the specified
interval. If the NPV does not change sign over the interval, no root
can be bracketed and the function returns gracefully with
converged = FALSE.
The number of sign changes in the nonzero cash-flow sequence is reported as a diagnostic. By Descartes' rule of signs, if there is exactly one sign change, the IRR is unique.
Value
A one-row tibble with:
- irr
Estimated IRR as an annual effective rate.
- i_effective_annual
Same as
irr, reported explicitly.- j_nominal_interest
Equivalent nominal annual interest rate convertible
nominal_mtimes.- delta
Equivalent force of interest.
- npv
Present value at the estimated IRR, close to zero.
- interval_left
Left endpoint of the search interval.
- interval_right
Right endpoint of the search interval.
- converged
Logical flag indicating whether a root was found.
- n_iter
Number of iterations used by
uniroot.- n_cashflows
Length of
payment.- has_both_signs
Whether the cash flow has at least one positive and one negative value.
- n_sign_changes
Number of sign changes in the nonzero cash-flow sequence.
If no sign change is present in the cash flow, or if the NPV does not change
sign over interval, the function returns converged = FALSE and
irr = NA_real_.
See Also
pv_flow, irr_flow_multi,
bond_ytm
Other time-value:
future_value(),
fv_flow(),
irr_flow_multi(),
plot_cash_flow(),
present_value(),
pv_flow()
Examples
irr_flow(
payment = c(-1000, 300, 400, 500),
time = c(0, 1, 2, 3)
)
irr_flow(
payment = c(-1000, 300, 400, 500),
date = as.Date(c("2026-01-01", "2027-01-01", "2028-01-01", "2029-01-01"))
)
Multiple internal rates of return for a cash flow
Description
Searches for multiple internal rates of return (IRRs) of a cash flow by scanning a search interval and solving for all detectable roots of the net present value (NPV) function.
Usage
irr_flow_multi(
payment,
time = NULL,
date = NULL,
search_interval = c(-0.99, 10),
grid_points = 2000L,
nominal_m = 1L,
tol = 1e-10,
maxiter = 1000L,
day_count = c("act/365", "act/360")
)
Arguments
payment |
Numeric vector of cash flows. |
time |
Optional numeric vector of cash-flow times in years. |
date |
Optional vector of cash-flow dates. If supplied, the earliest date is treated as time 0. |
search_interval |
Numeric vector of length 2 giving the search interval
for annual effective IRRs. Default is |
grid_points |
Positive integer giving the number of grid points used to scan the interval. Larger values improve detection at the cost of speed. |
nominal_m |
Positive integer used only to report an equivalent nominal
annual interest rate convertible |
tol |
Numeric tolerance passed to |
maxiter |
Positive integer passed to |
day_count |
Day-count convention used when |
Details
This function is intended for cash flows with multiple sign changes, where
more than one IRR may exist. It evaluates the NPV on a fine grid over the
search interval, identifies subintervals with sign changes (and grid points
where the NPV is approximately zero), and applies
uniroot to each candidate interval.
The IRRs returned are interpreted as annual effective rates.
Timing can be supplied either through time (in years) or date
(calendar dates). If date is supplied, the earliest date is treated
as time 0.
This function detects roots numerically over a finite search interval. It may miss roots if:
the grid is too coarse,
two roots are extremely close,
the NPV touches zero without changing sign,
or the root lies outside the search interval.
For a single-IRR workflow, use irr_flow.
Value
A tibble with one row per detected IRR and columns:
- root_id
Root index.
- irr
Detected IRR as an annual effective rate.
- i_effective_annual
Same as
irr, reported explicitly.- j_nominal_interest
Equivalent nominal annual interest rate convertible
nominal_mtimes.- delta
Equivalent force of interest.
- npv
NPV evaluated at the detected root (approximately zero).
- interval_left
Left endpoint of the local search bracket.
- interval_right
Right endpoint of the local search bracket.
- n_cashflows
Length of
payment.- has_both_signs
Whether the cash flow has at least one positive and one negative value.
- n_sign_changes_cashflow
Number of sign changes in the nonzero cash-flow sequence.
If no roots are detected, the function returns a tibble with zero rows.
See Also
Other time-value:
future_value(),
fv_flow(),
irr_flow(),
plot_cash_flow(),
present_value(),
pv_flow()
Examples
# A standard single-IRR cash flow
irr_flow_multi(
payment = c(-1000, 300, 400, 500),
time = c(0, 1, 2, 3)
)
# A cash flow with multiple sign changes
irr_flow_multi(
payment = c(-1000, 5000, -4500, 200),
time = c(0, 1, 2, 3),
search_interval = c(-0.99, 5),
grid_points = 5000
)
# Date-based version
irr_flow_multi(
payment = c(-1000, 300, 400, 500),
date = as.Date(c("2026-01-01", "2027-01-01", "2028-01-01", "2029-01-01"))
)
Kaplan–Meier survival curve and a lifetable-style life table
Description
Fits the nonparametric Kaplan–Meier estimator \hat S(t) for
right-censored time-to-event data, computes Greenwood's variance estimator
for \hat S(t), and constructs a discrete life table by evaluating
\hat S(t) at user-provided cut points (breaks).
Usage
km_lifetable(
time,
status,
entry = NULL,
breaks = NULL,
radix = 1e+05,
conf_level = 0.95,
assumption = c("UDD", "CF", "Balducci")
)
Arguments
time |
Numeric vector. Observed times (event or censoring times). |
status |
Integer/numeric vector of the same length as |
entry |
Optional numeric vector of entry times (left truncation /
delayed entry). If provided, must have the same length as |
breaks |
Optional numeric vector of increasing cut points used to build
the discrete life table (e.g., |
radix |
Numeric. Life table radix used to scale |
conf_level |
Numeric in |
assumption |
Character. Fractional-age assumption used to compute
|
Details
The resulting life table is intended for experience-based (empirical) life tables in actuarial/demographic contexts (e.g., cohort studies, population indicators). It is not a replacement for graduated/regulatory tables when smoothing, extrapolation, or product-specific selection effects are required.
Kaplan–Meier estimator. At each observed event time t_j:
\hat S(t) = \prod_{t_j \le t} \left(1 - \frac{d_j}{n_j}\right)
where n_j is the risk set size and d_j is the number of events.
Greenwood's variance:
\widehat{\mathrm{Var}}(\hat S(t)) = \hat S(t)^2 \sum_{t_j \le t}
\frac{d_j}{n_j(n_j - d_j)}.
Pointwise confidence intervals use the log(-log) transformation.
Life table mapping. For each interval [x, x + \Delta):
\ell_x = \text{radix} \cdot \hat S(x), \quad d_x = \ell_x -
\ell_{x+\Delta}, \quad q_x = d_x / \ell_x.
Exposure L_x = \int_x^{x+\Delta} \ell(t)\,dt is computed using the
selected fractional-age assumption (Finan, Section 24):
UDD (Finan, Sec. 24.1):
L_x \approx \tfrac{\ell_x + \ell_{x+\Delta}}{2} \DeltaCF (constant force, Finan, Sec. 24.2):
L_x = \Delta \cdot (\ell_x - \ell_{x+\Delta}) / \ln(\ell_x / \ell_{x+\Delta})Balducci (Finan, Sec. 24.3):
L_x = \Delta \cdot \ell_x \ell_{x+\Delta} / (\ell_x - \ell_{x+\Delta}) \cdot \ln(\ell_x / \ell_{x+\Delta})
Additional columns follow Finan, Sections 23.3, 23.8–23.9:
-
T_x = \sum_{k \ge x} L_k: total expected years lived after agex(Finan, Sec. 23.3). -
\mathring{e}_x = T_x / \ell_x: complete life expectancy (Finan, Sec. 23.3). -
m_x = d_x / L_x: central death rate (Finan, Sec. 23.9). -
a_x = (\ell_x \Delta - L_x) / d_x: average fraction of the interval lived by those who die.
Value
A list with two tibbles:
-
km: tibble with columnstime,n_risk,d,censored,S,varS,seS,ci_low,ci_high. -
lifetable: tibble with columnsx,x_next,width,lx,dx,qx,px,mx,ax,Lx,Tx,ex. Carries class"lifetable"and standard attributes for compatibility with downstream functions.
See Also
lifetable for building tables from known mortality
inputs, plot_km for plotting the KM curve.
Examples
set.seed(1)
n <- 200
trueT <- rexp(n, rate = 0.08)
censT <- rexp(n, rate = 0.04)
time <- pmin(trueT, censT)
status <- as.integer(trueT <= censT)
out <- km_lifetable(time, status, breaks = 0:25, radix = 100000)
head(out$km)
head(out$lifetable)
# Tidy pipeline: filter high-mortality intervals
out$lifetable |> dplyr::filter(qx > 0.05)
# Compare UDD vs CF assumptions
udd <- km_lifetable(time, status, breaks = 0:20, assumption = "UDD")
cfm <- km_lifetable(time, status, breaks = 0:20, assumption = "CF")
c(ex_udd = udd$lifetable$ex[1], ex_cf = cfm$lifetable$ex[1])
# Plot the KM curve with plot_km
plot_km(out$km, time_col = "time", surv_col = "S",
lower_col = "ci_low", upper_col = "ci_high")
Create a life-contingency contract specification
Description
Creates a lightweight contract object that stores common life-contingency inputs for use in pipe workflows.
Usage
life_contract(
mortality_table,
lives = c("single", "joint", "last_survivor"),
age = NULL,
age_x = NULL,
age_y = NULL,
rate,
rate_type = "effective",
m = 1L,
...
)
Arguments
mortality_table |
A life table or a list of two life tables. For
single-life contracts, provide one data.frame or tibble. For two-life
contracts, provide either one data.frame used for both lives, or
|
lives |
Character string. Use |
age |
Numeric scalar. Age for a single-life contract. |
age_x |
Numeric scalar. First age for two-life contracts. |
age_y |
Numeric scalar. Second age for two-life contracts. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. Ignored
for |
... |
Reserved for future extensions. |
Details
This function does not compute actuarial values. It validates and stores
common inputs such as the mortality table, life status, ages, and interest
rate specification. Calculation functions such as annuity_x(),
insurance_x(), premium_x(), reserve_x(), annuity_xy(),
insurance_xy(), premium_xy(), and simulation functions can then consume
this object.
Value
An object of class "tidyact_life_contract".
See Also
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Build an annual life table (tidy tibble) from lx, qx, px, or mx
Description
Creates an annual life table with integer, consecutive ages and returns a
tibble (tidyverse-friendly) with class "lifetable".
Usage
lifetable(
x,
lx = NULL,
qx = NULL,
px = NULL,
mx = NULL,
radix = NULL,
omega = NULL,
close = TRUE,
ax = 0.5,
type = c("ultimate", "select"),
frac = c("UDD", "CF", "Balducci"),
check = TRUE,
tol = 1e-10
)
Arguments
x |
Numeric vector of ages. Must be integer and consecutive
(annual table), e.g. |
lx |
Optional numeric vector of survivors |
qx |
Optional numeric vector of one-year death probabilities |
px |
Optional numeric vector of one-year survival probabilities |
mx |
Optional numeric vector of central death rates |
radix |
Optional positive scalar. Required if building |
omega |
Optional integer limiting age. If |
close |
Logical. If |
ax |
Scalar in |
type |
Character. |
frac |
Character. |
check |
Logical. If |
tol |
Numeric tolerance for integer checks and consistency checks. |
Details
The table can be built from exactly one of:
-
lx(survivors), or -
qx(one-year death probabilities), or -
px(one-year survival probabilities), or -
mx(central death rates),
and the function will compute the remaining columns consistently: dx,
qx, px, and mx.
When multiple inputs are provided, priority is:
lx > qx > px > mx.
If lx is provided together with qx, cross-consistency is
validated (both must agree via q_x = (\ell_x - \ell_{x+1}) / \ell_x).
By default, the table is actuarially closed at omega:
\ell_{\omega+1} = 0 \Rightarrow d_{\omega} = \ell_{\omega}
\Rightarrow q_{\omega} = 1 \Rightarrow p_{\omega} = 0.
The life table follows the standard actuarial construction described in Finan, Sections 22–24 (Exam MLC preparation).
The basic identities are (Finan, Section 22):
\ell_x = \ell_0 \cdot s(x), \quad d_x = \ell_x - \ell_{x+1},
\quad q_x = d_x / \ell_x, \quad p_x = \ell_{x+1} / \ell_x.
The central death rate mx is computed via the discrete approximation
(Finan, Section 23.9):
m_x = \frac{q_x}{1 - a_x \cdot q_x}
which under UDD (ax = 0.5) reduces to the classical formula
m_x = q_x / (1 - 0.5 \, q_x) (Finan, Section 24.1). This arises
because under UDD, L_x = \ell_x - \tfrac{1}{2} d_x, and therefore
m_x = d_x / L_x.
At the terminal age \omega with close = TRUE, closure forces
q_\omega = 1, p_\omega = 0, and d_\omega = \ell_\omega.
The corresponding m_\omega equals 1/(1 - a_x), which is 2
under UDD (ax = 0.5). If ax = 1, m_\omega = \infty.
Value
A tibble with class
c("lifetable","tbl_df","tbl","data.frame") and columns:
-
x: integer ages -
lx: survivors at exact age x -
dx: deaths in[x, x+1) -
qx: probability of death in[x, x+1) -
px: probability of survival tox+1 -
mx: central death rate (derived usingax). At the terminal age withclose = TRUE,mxequals1/(1 - a_x)and may beInfifax = 1.
Attributes include: radix, omega, type, frac,
closed, ax.
See Also
km_lifetable for Kaplan–Meier construction,
t_px and t_qx for survival and death
probabilities (including fractional ages),
e_x for curtate and complete life expectancy,
annuity_x and insurance_x for life
contingency valuations that consume a life table.
Examples
# Example 1: build from lx (Finan, Section 22 style)
x <- 0:5
lx <- c(100000, 99500, 99000, 98200, 97000, 95000)
lt1 <- lifetable(x = x, lx = lx, omega = 5, close = TRUE)
lt1
# Example 2: build from qx (radix required)
qx <- c(0.005, 0.005, 0.008, 0.012, 0.020, 1)
lt2 <- lifetable(x = x, qx = qx, radix = 100000, omega = 5, close = TRUE)
lt2
# Example 3: build from px
px <- 1 - c(0.005, 0.005, 0.008, 0.012, 0.020, 1)
lt3 <- lifetable(x = x, px = px, radix = 100000, omega = 5, close = TRUE)
lt3
# Example 4: build from mx
mx <- c(0.005, 0.006, 0.008, 0.012, 0.020, 0.030)
lt4 <- lifetable(x = x, mx = mx, radix = 100000, omega = 5, close = TRUE, ax = 0.5)
lt4
# Example 5: truncate to a smaller omega
lt5 <- lifetable(x = 0:10, lx = 100000 * exp(-0.01 * (0:10)), omega = 7, close = TRUE)
lt5
# Example 6: Finan Example 22.1 - exponential survival s(x) = exp(-0.005x)
lt_exp <- lifetable(
x = 0:7,
lx = 1000 * exp(-0.005 * (0:7)),
close = TRUE
)
lt_exp
# Example 7: verify survival identity (Finan, Section 22)
# 2_p_2 = l_4 / l_2 = 97000 / 99000
lt1$lx[lt1$x == 4] / lt1$lx[lt1$x == 2]
# Example 8: without closure - qx at omega is not forced to 1
lt_open <- lifetable(x = 0:3, lx = c(1000, 900, 750, 500), close = FALSE)
lt_open$qx # last element is NA
# Example 9: access table metadata
attr(lt1, "omega") # 5
attr(lt1, "closed") # TRUE
attr(lt1, "frac") # "UDD"
attr(lt1, "ax") # 0.5
Sample loan contracts for amortization examples
Description
A small pedagogical dataset containing level-payment loan contracts for amortization schedule and outstanding balance examples.
A small pedagogical dataset containing level-payment loan contracts for amortization schedule and outstanding balance examples.
Usage
loans_sample
loans_sample
Format
A tibble with 4 rows and 7 variables:
- loan_id
Loan identifier.
- principal
Initial loan principal.
- annual_effective_rate
Annual effective interest rate.
- term_months
Loan term in months.
- payments_per_year
Number of payments per year.
- loan_type
Short loan type label.
- payment
Level payment amount.
A tibble with 4 rows and 7 variables:
- loan_id
Loan identifier.
- principal
Initial loan principal.
- annual_effective_rate
Annual effective interest rate.
- term_months
Loan term in months.
- payments_per_year
Number of payments per year.
- loan_type
Short loan type label.
- payment
Level payment amount.
Source
Synthetic pedagogical data created for tidyactuarial examples.
Synthetic pedagogical data created for tidyactuarial examples.
Examples
data(loans_sample)
loans_sample |>
dplyr::select(loan_id, principal, annual_effective_rate, term_months, payment)
data(loans_sample)
loans_sample |>
dplyr::select(loan_id, principal, annual_effective_rate, term_months, payment)
Total-decrement lifetable from a multiple decrement table: lt_tau
Description
Builds a single-decrement lifetable for the total decrement (any cause),
using q_x^{(\tau)} from a multiple decrement table produced by
md_table. This enables direct re-use of single-life functions
(e.g., t_px, t_qx, t_Ex, annuities, insurances) under the
total decrement model.
Usage
lt_tau(md, ...)
Arguments
md |
A multiple decrement table (typically the output of |
... |
Additional arguments passed to |
Details
Given cause-specific decrement probabilities q_x^{(j)}, the total decrement
is q_x^{(\tau)} = \sum_j q_x^{(j)}. This function simply passes
x = md$x and qx = md$q_total to lifetable.
Value
A lifetable object as produced by lifetable.
Examples
qx_df <- tibble::tibble(
x = 30:35,
q_death = c(0.001, 0.0012, 0.0014, 0.0017, 0.0020, 1.0000),
q_disability = c(0.002, 0.0021, 0.0022, 0.0023, 0.0024, 0.0000)
)
md <- md_table(qx_df, radix = 1e5, close = TRUE)
lt <- lt_tau(md, radix = 1e5, close = TRUE, frac = "UDD")
t_px(lt, x = 30, t = 5)
Compute simulated present values for life annuities
Description
Computes Monte Carlo simulated present values of life annuity payments from simulated future lifetimes.
Usage
mc_annuity(
data,
rate,
payment = 1,
payments_per_year = 1,
annuity = c("whole_life", "temporary", "deferred", "deferred_temporary", "certain",
"guaranteed"),
term = NULL,
deferral_years = 0,
guarantee_years = NULL,
timing = c("immediate", "due"),
interest_type = c("effective", "nominal", "force"),
m = 1,
k_col = "Kx",
tx_col = "Tx",
annuity_col = "pv_annuity"
)
Arguments
data |
A data frame or tibble containing simulated future lifetimes,
typically returned by |
rate |
Numeric scalar. Interest rate used for discounting. |
payment |
Numeric scalar. Amount of each annuity payment. Default is
|
payments_per_year |
Positive integer-like scalar. Number of annuity
payments per year. Default is |
annuity |
Character string specifying the annuity type. Available
options are |
term |
Numeric scalar. Term of the annuity in years. Required for
|
deferral_years |
Numeric scalar. Deferral period in years. Default is
|
guarantee_years |
Numeric scalar. Guaranteed payment period in years.
Required for |
timing |
Character string specifying the annuity payment timing.
Available options are |
interest_type |
Character string specifying the interest rate convention.
Available options are |
m |
Numeric scalar. Number of interest conversion periods per year when
|
k_col |
Character string. Name of the column containing simulated
curtate future lifetimes. Default is |
tx_col |
Character string. Name of the column containing simulated
complete future lifetimes. Default is |
annuity_col |
Character string. Name of the output column containing
simulated present values of annuity payments. Default is |
Details
This function is designed to be used after simulate_lifetime(). It takes
simulated values of the curtate future lifetime K_x, and when needed
the complete future lifetime T_x, and evaluates the present value
random variable associated with several classical annuity benefits.
Let K_x denote the curtate future lifetime of a life aged x,
let T_x denote the complete future lifetime, and let v denote
the annual discount factor.
The arguments m and payments_per_year have different meanings:
-
mis used only wheninterest_type = "nominal"and controls the frequency of interest conversion. -
payments_per_yearcontrols how frequently annuity payments are made.
The argument payment represents the amount of each annuity payment. Thus,
for a monthly annuity with total annual payment equal to 1, use
payment = 1 / 12 and payments_per_year = 12.
For annual payments, payments_per_year = 1, the function works directly
with K_x. For fractional payments, such as monthly, quarterly, or
semiannual payments, the function uses T_x to determine whether the
life is alive at each fractional payment time.
For annual whole life annuity-immediate, the simulated present value is
Y = \sum_{j=1}^{K_x} c v^j,
where c is the amount of each payment.
For annual whole life annuity-due, the simulated present value is
\ddot{Y} = \sum_{j=0}^{K_x} c v^j.
For fractional payments with payment frequency m_p, annuity-immediate
payments are made at times 1/m_p, 2/m_p, \ldots while the life is
alive. Annuity-due payments are made at times
0, 1/m_p, 2/m_p, \ldots while the life is alive.
The following annuity types are supported:
-
"whole_life": payments continue while the life is alive. -
"temporary": payments continue while the life is alive, but for at mosttermyears. -
"deferred": payments begin afterdeferral_yearsyears and continue while the life is alive. -
"deferred_temporary": payments begin afterdeferral_yearsyears and continue while the life is alive, but for at mosttermyears after the deferral period. -
"certain": payments are made fortermyears regardless of survival. -
"guaranteed": payments continue while the life is alive, with at leastguarantee_yearsyears of payments guaranteed.
The function returns simulated present values, not only their expected value.
Therefore the resulting column can be summarized with summary_mc(),
plotted with ggplot2, or used to construct premiums, losses, and reserves.
Value
A tibble with the original simulation columns and additional columns:
-
rate: original rate supplied. -
interest_type: interest rate convention. -
m: interest conversion frequency. -
effective_rate: equivalent annual effective interest rate. -
discount_factor: annual discount factor. -
annuity: annuity type. -
payment: amount of each annuity payment. -
payments_per_year: annuity payment frequency. -
term: annuity term, if applicable. -
deferral_years: deferral period. -
guarantee_years: guaranteed period, if applicable. -
timing: annuity payment timing. -
n_payments: number of payments made in the simulated scenario. -
first_payment_time: first payment time in the simulated scenario. -
last_payment_time: last payment time in the simulated scenario. -
pv_annuity: simulated present value of annuity payments, or another name supplied throughannuity_col.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetime(), simulate_lifetimes(), mc_multilife_status(),
mc_insurance(), mc_premium(), mc_loss(), mc_reserve(),
summary_mc()
Examples
life_table <- tibble::tibble(
age = 40:100,
qx = seq(0.002, 1, length.out = 61)
)
# Annual whole life annuity-due
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_annuity(
rate = 0.05,
annuity = "whole_life",
payment = 1,
payments_per_year = 1,
timing = "due"
)
# Monthly whole life annuity-due with total annual payment equal to 1
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_annuity(
rate = 0.05,
annuity = "whole_life",
payment = 1 / 12,
payments_per_year = 12,
timing = "due"
)
# Quarterly temporary life annuity-immediate
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_annuity(
rate = 0.05,
annuity = "temporary",
term = 20,
payment = 1 / 4,
payments_per_year = 4,
timing = "immediate"
)
# Monthly joint-life annuity using a multiple-life status
life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_multilife_status(status = "joint_life") |>
mc_annuity(
rate = 0.04,
annuity = "whole_life",
payment = 1 / 12,
payments_per_year = 12,
timing = "due",
k_col = "K_status",
tx_col = "T_status"
)
# Nominal rate convertible monthly, with quarterly payments
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_annuity(
rate = 0.06,
interest_type = "nominal",
m = 12,
annuity = "whole_life",
payment = 1 / 4,
payments_per_year = 4,
timing = "due"
)
Compute simulated present values for life insurance benefits
Description
Computes Monte Carlo simulated present values of life insurance benefits from simulated future lifetimes.
Usage
mc_insurance(
data,
rate,
benefit = 1,
insurance = c("whole_life", "term", "deferred", "deferred_term", "pure_endowment",
"endowment"),
term = NULL,
deferral_years = 0,
payment_timing = c("end_of_year", "moment_of_death"),
interest_type = c("effective", "nominal", "force"),
m = 1,
k_col = "Kx",
tx_col = "Tx",
benefit_col = "pv_benefit"
)
Arguments
data |
A data frame or tibble containing simulated future lifetimes,
typically returned by |
rate |
Numeric scalar. Interest rate used for discounting. |
benefit |
Numeric scalar. Benefit amount payable under the insurance.
Default is |
insurance |
Character string specifying the type of insurance.
Available options are |
term |
Numeric scalar. Term of the insurance in years. Required for
|
deferral_years |
Numeric scalar. Deferral period in years. Default is
|
payment_timing |
Character string specifying when death benefits are
paid. Available options are |
interest_type |
Character string specifying the interest rate convention.
Available options are |
m |
Numeric scalar. Number of interest conversion periods per year when
|
k_col |
Character string. Name of the column containing simulated
curtate future lifetimes. Default is |
tx_col |
Character string. Name of the column containing simulated
complete future lifetimes. Required when
|
benefit_col |
Character string. Name of the output column containing
simulated present values of benefits. Default is |
Details
This function is designed to be used after simulate_lifetime(). It takes
simulated values of the curtate future lifetime K_x, and optionally
the complete future lifetime T_x, and evaluates the present value
random variable associated with classical life insurance benefits.
Let K_x denote the curtate future lifetime of a life aged x,
let T_x denote the complete future lifetime, and let v denote
the annual discount factor.
If payment_timing = "end_of_year", death benefits are discounted using
K_x + 1. If payment_timing = "moment_of_death", death benefits are
discounted using T_x.
The arguments interest_type and m determine how the supplied rate is
converted into an annual effective rate before discounting. When
interest_type = "effective", rate is interpreted as an annual effective
interest rate. When interest_type = "nominal", rate is interpreted as a
nominal annual interest rate convertible m times per year. When
interest_type = "force", rate is interpreted as a constant force of
interest.
The following insurance types are supported:
-
"whole_life": benefit is paid whenever death occurs. -
"term": benefit is paid if death occurs withintermyears. -
"deferred": benefit is paid if death occurs after the deferral period. -
"deferred_term": benefit is paid if death occurs after the deferral period and within the followingtermyears. -
"pure_endowment": benefit is paid at timetermif the life survives to that time. -
"endowment": death benefit is paid if death occurs withintermyears; otherwise, a survival benefit is paid at timeterm.
The function returns simulated present values, not only their expected
values. Therefore the resulting column can be summarized with
summary_mc(), plotted with ggplot2, or used to construct premiums,
losses, and reserves.
Value
A tibble with the original simulation columns and additional columns:
-
rate: original rate supplied. -
interest_type: interest rate convention. -
m: interest conversion frequency. -
effective_rate: equivalent annual effective interest rate. -
discount_factor: annual discount factor. -
insurance: insurance type. -
benefit: benefit amount. -
term: insurance term, if applicable. -
deferral_years: deferral period. -
payment_timing: timing used for death benefits. -
benefit_time: simulated payment time of the benefit. -
benefit_indicator: indicator that the benefit is paid. -
pv_benefit: simulated present value of the benefit, or another name supplied throughbenefit_col.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetime(), simulate_lifetimes(), mc_multilife_status(),
mc_annuity(), mc_premium(), mc_loss(), mc_reserve(),
summary_mc()
Examples
life_table <- tibble::tibble(
age = 40:100,
qx = seq(0.002, 1, length.out = 61)
)
# Whole life insurance payable at the end of the year of death
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
benefit = 1
)
# 20-year term insurance
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "term",
term = 20,
benefit = 100000
)
# 10-year deferred whole life insurance
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "deferred",
deferral_years = 10,
benefit = 1
)
# 10-year deferred, 20-year term insurance
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "deferred_term",
deferral_years = 10,
term = 20,
benefit = 1
)
# Pure endowment
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "pure_endowment",
term = 20,
benefit = 1
)
# Endowment insurance payable at the moment of death if death occurs
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, fractional = "udd", seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "endowment",
term = 20,
payment_timing = "moment_of_death",
benefit = 1
)
# Nominal rate convertible monthly
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.06,
interest_type = "nominal",
m = 12,
insurance = "whole_life",
benefit = 1
)
# First-death insurance using a multiple-life status
life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_multilife_status(status = "first_death") |>
mc_insurance(
rate = 0.04,
insurance = "whole_life",
benefit = 100000,
k_col = "K_status",
tx_col = "T_status"
)
Compute Monte Carlo loss random variables for life contingencies
Description
Computes simulated actuarial loss random variables from Monte Carlo present values of benefits, premium annuities, and premiums.
Usage
mc_loss(
data,
benefit_col = "pv_benefit",
annuity_col = "pv_annuity",
premium_col = "premium",
loss_col = "loss",
premium = NULL
)
Arguments
data |
A data frame or tibble containing simulated present values of benefits and premium annuities. |
benefit_col |
Character string. Name of the column containing the
simulated present value of benefits. Default is |
annuity_col |
Character string. Name of the column containing the
simulated present value of premium annuities. Default is |
premium_col |
Character string. Name of the column containing the
premium. Default is |
loss_col |
Character string. Name of the output column containing the
simulated loss random variable. Default is |
premium |
Optional numeric scalar. If supplied, this value is used as
the premium instead of reading the premium from |
Details
This function constructs the simulated loss random variable
L = Z - P Y,
where Z is the present value random variable of the insurance benefit,
Y is the present value random variable of the premium annuity, and
P is the premium.
In actuarial notation, the loss at issue is commonly written as
L_0 = Z - P Y.
The random variable Z represents the present value of future benefits,
while Y represents the present value of future premium payments.
This function does not estimate the premium. It only constructs the simulated
loss random variable. The premium may come from a column previously created
by mc_premium(), or it may be supplied directly through the premium
argument.
The interpretation of premium depends on how the premium annuity present
value was constructed. If pv_annuity was generated with annual payments,
then premium corresponds to that annual premium structure. If pv_annuity
was generated using fractional payments in mc_annuity(), such as
payment = 1 / 12 and payments_per_year = 12, then the premium is applied
to that same fractional payment pattern.
Thus, mc_loss() can be used without modification for annual premiums,
monthly premiums, quarterly premiums, semiannual premiums, and multiple-life
premium structures, provided that annuity_col contains the appropriate
simulated premium annuity present value.
The resulting loss column can be used to estimate quantities such as:
expected loss;
variance and standard deviation of loss;
probability of positive loss;
loss quantiles;
empirical value-at-risk type measures;
sensitivity of loss distributions across ages, benefits, products, payment frequencies, interest rates, or multiple-life statuses.
Value
A tibble with the original columns and one additional column
containing the simulated loss random variable. The name of this column is
controlled by loss_col.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetime(), simulate_lifetimes(), mc_multilife_status(),
mc_insurance(), mc_annuity(), mc_premium(), mc_reserve(),
summary_mc()
Examples
# Example 1: direct use with simulated present values
sim_values <- tibble::tibble(
sim_id = 1:5,
pv_benefit = c(0.80, 0.75, 0.95, 0.60, 0.70),
pv_annuity = c(8.0, 7.5, 9.0, 6.0, 7.0),
premium = 0.10
)
sim_values |>
mc_loss()
# Example 2: using a premium supplied directly
sim_values |>
mc_loss(premium = 0.12)
# Example 3: annual whole life loss
life_table <- tibble::tibble(
age = 40:100,
qx = seq(0.002, 1, length.out = 61)
)
life_table |>
simulate_lifetime(age = 40, n_sim = 500, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
benefit = 1
) |>
mc_annuity(
rate = 0.05,
annuity = "whole_life",
payment = 1,
payments_per_year = 1,
timing = "due"
) |>
mc_premium() |>
mc_loss()
# Example 4: monthly whole life loss
life_table |>
simulate_lifetime(
age = 40,
n_sim = 500,
fractional = "udd",
seed = 123
) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
benefit = 1
) |>
mc_annuity(
rate = 0.05,
annuity = "whole_life",
payment = 1 / 12,
payments_per_year = 12,
timing = "due"
) |>
mc_premium() |>
mc_loss()
# Example 5: summarising simulated losses
life_table |>
simulate_lifetime(
age = 45,
n_sim = 500,
fractional = "udd",
seed = 123
) |>
mc_insurance(
rate = 0.04,
insurance = "term",
term = 20,
benefit = 100000
) |>
mc_annuity(
rate = 0.04,
annuity = "temporary",
term = 20,
payment = 1 / 12,
payments_per_year = 12,
timing = "due"
) |>
mc_premium() |>
mc_loss() |>
summary_mc(value_col = "loss")
# Example 6: joint-life loss with monthly premium annuity
life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 500,
fractional = "udd",
seed = 123
) |>
mc_multilife_status(status = "joint_life") |>
mc_insurance(
rate = 0.04,
insurance = "whole_life",
benefit = 100000,
k_col = "K_status",
tx_col = "T_status"
) |>
mc_annuity(
rate = 0.04,
annuity = "whole_life",
payment = 1 / 12,
payments_per_year = 12,
timing = "due",
k_col = "K_status",
tx_col = "T_status"
) |>
mc_premium() |>
mc_loss()
Construct multiple-life status lifetimes from simulated lives
Description
Constructs simulated multiple-life status lifetimes from simulated individual lifetimes in long format.
Usage
mc_multilife_status(
data,
status = c("joint_life", "last_survivor", "first_death", "last_death", "kth_death",
"at_least_k_alive"),
k = NULL,
sim_col = "sim_id",
life_col = "life_id",
k_col = "Kx",
tx_col = "Tx",
k_status_col = "K_status",
t_status_col = "T_status",
keep_lifetimes = FALSE
)
Arguments
data |
A data frame or tibble containing simulated multiple-life
lifetimes, typically returned by |
status |
Character string specifying the multiple-life status.
Available options are |
k |
Optional positive integer. Required when |
sim_col |
Character string. Name of the simulation identifier column.
Default is |
life_col |
Character string. Name of the life identifier column.
Default is |
k_col |
Character string. Name of the column containing simulated
curtate future lifetimes. Default is |
tx_col |
Character string. Name of the column containing simulated
complete future lifetimes. Default is |
k_status_col |
Character string. Name of the output column containing
the curtate lifetime of the multiple-life status. Default is |
t_status_col |
Character string. Name of the output column containing
the complete lifetime of the multiple-life status. Default is |
keep_lifetimes |
Logical. If |
Details
This function is designed to be used after simulate_lifetimes(). It takes
one row per simulation and per life, and returns one row per simulation with
simulated curtate and complete lifetimes for the selected multiple-life
status.
The key output columns are K_status and T_status. They are intentionally
named so they can be used directly in downstream functions such as
mc_insurance() and mc_annuity() through their k_col and tx_col
arguments.
Suppose that, for a given simulation, the complete future lifetimes of
r lives are
T_1, T_2, \ldots, T_r.
Let
T_{(1)} \le T_{(2)} \le \cdots \le T_{(r)}
denote the ordered lifetimes. The selected multiple-life status is converted into an order statistic:
-
"joint_life": fails at the first death,T_{(1)}. -
"first_death": time until the first death,T_{(1)}. -
"last_survivor": fails at the last death,T_{(r)}. -
"last_death": time until the last death,T_{(r)}. -
"kth_death": time until thek-th death,T_{(k)}. -
"at_least_k_alive": status remains active while at leastklives are alive, and fails at the(r-k+1)-th death.
The same order-statistic logic is applied to the curtate future lifetimes
K_1, K_2, \ldots, K_r, producing K_status.
This construction allows multiple-life quantities to be evaluated using the
single-life Monte Carlo functions. For example, a joint-life annuity can be
computed by passing k_col = "K_status" to mc_annuity(), and a first-death
insurance can be computed by passing k_col = "K_status" and
tx_col = "T_status" to mc_insurance().
The function assumes the individual lifetimes have already been simulated.
If the input comes from simulate_lifetimes(), the default model is
independence across lives.
Value
A tibble with one row per simulation and the following columns:
-
sim_id: simulation identifier, or the column named bysim_col. -
status: multiple-life status. -
n_lives: number of lives in the simulation. -
k: value ofk, when applicable. -
status_rank: order statistic rank used to define the status. -
K_status: simulated curtate lifetime of the status, or another name supplied throughk_status_col. -
T_status: simulated complete lifetime of the status, or another name supplied throught_status_col. If complete lifetimes are unavailable, this column containsNA. -
event_life_id: identifier of the life or lives associated with the status event time. -
n_event_lives: number of lives tied at the status event time.
If keep_lifetimes = TRUE, a list-column named lifetimes is also returned.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetimes(), simulate_lifetime(), mc_insurance(),
mc_annuity(), summary_mc()
Examples
life_table <- tibble::tibble(
age = 60:90,
qx = seq(0.01, 1, length.out = 31)
)
# Simulated lifetimes for two independent lives
sim_two <- life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 25,
seed = 123
)
# Joint-life status: time until the first death
mc_multilife_status(
sim_two,
status = "joint_life"
)
# Last-survivor status: time until the last death
mc_multilife_status(
sim_two,
status = "last_survivor"
)
# Simulated lifetimes for three independent lives
sim_three <- life_table |>
simulate_lifetimes(
ages = c(60, 58, 55),
life_id = c("x", "y", "z"),
n_sim = 25,
seed = 123
)
# Time until the second death among three lives
mc_multilife_status(
sim_three,
status = "kth_death",
k = 2
)
# Status active while at least two lives are alive
mc_multilife_status(
sim_three,
status = "at_least_k_alive",
k = 2
)
Compute Monte Carlo net premiums for life contingencies
Description
Computes simulated net premiums from Monte Carlo present values of insurance benefits and premium annuities.
Usage
mc_premium(
data,
benefit_col = "pv_benefit",
annuity_col = "pv_annuity",
premium_col = "premium",
by = NULL,
na_rm = TRUE
)
Arguments
data |
A data frame or tibble containing simulated present values.
Usually this object is obtained after applying |
benefit_col |
Character string. Name of the column containing the
simulated present value of the insurance benefit. Default is
|
annuity_col |
Character string. Name of the column containing the
simulated present value of the premium annuity. Default is |
premium_col |
Character string. Name of the output column containing
the simulated net premium. Default is |
by |
Optional character vector with grouping columns. If supplied, the
premium is computed separately within each group. If |
na_rm |
Logical. Should missing values be removed when computing
simulated means? Default is |
Details
This function applies the actuarial equivalence principle to simulated
present value random variables. If Z denotes the present value random
variable of the benefit and Y denotes the present value random variable
of the premium annuity, the net premium is estimated as
\hat{P} = \frac{\bar{Z}}{\bar{Y}}.
Equivalently, this is the Monte Carlo estimator of
P = \frac{E[Z]}{E[Y]}.
The function does not simulate lifetimes and does not calculate present values directly. It only computes the Monte Carlo net premium from columns that already contain simulated present values.
In a typical workflow, simulate_lifetime() generates simulated values of
K_x and possibly T_x; mc_insurance() creates the simulated
benefit present value Z; mc_annuity() creates the simulated premium
annuity present value Y; and mc_premium() estimates the net level
premium.
The estimated premium is attached to every row of the input data. This is
intentional: it makes it easy to construct the simulated loss random variable
with mc_loss(), for example
L = Z - \hat{P}Y.
The function is also valid for premiums payable more than once per year.
In that case, the payment frequency is not specified in mc_premium();
it is already embedded in the simulated premium annuity present value
supplied through annuity_col.
For example, if mc_annuity() was called with
payment = 1 / 12 and payments_per_year = 12, then pv_annuity
represents the present value of a monthly premium stream whose total annual
amount is 1. The premium estimated by mc_premium() is then consistent with
that monthly payment structure and corresponds to a Monte Carlo estimate of
a premium such as P_x^{(12)}.
If mc_annuity() was called with payment = 1 and
payments_per_year = 12, then pv_annuity represents a stream of payments
of 1 each month, and the resulting premium should be interpreted relative to
that payment pattern.
This function computes net premiums only. It does not include expenses, safety loadings, profit margins, taxes, surrender charges, commissions, or other practical pricing adjustments.
Value
A tibble with the original columns and one additional column
containing the simulated net premium. The name of this column is controlled
by premium_col.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetime(), simulate_lifetimes(), mc_insurance(),
mc_annuity(), mc_loss(), mc_reserve(), summary_mc()
Examples
# Example 1: direct use with simulated present values
sim_values <- tibble::tibble(
sim_id = 1:6,
pv_benefit = c(0.82, 0.74, 0.61, 0.95, 0.70, 0.88),
pv_annuity = c(8.2, 7.5, 6.1, 9.0, 7.2, 8.8)
)
sim_values |>
mc_premium()
# Example 2: grouped premiums by age
sim_by_age <- tibble::tibble(
sim_id = rep(1:6, times = 2),
age = rep(c(40, 50), each = 6),
pv_benefit = c(
0.82, 0.74, 0.61, 0.95, 0.70, 0.88,
0.91, 0.86, 0.79, 0.98, 0.83, 0.94
),
pv_annuity = c(
8.2, 7.5, 6.1, 9.0, 7.2, 8.8,
6.8, 6.4, 5.9, 7.1, 6.2, 6.7
)
)
sim_by_age |>
mc_premium(by = "age")
# Example 3: using dplyr grouping
sim_by_age |>
dplyr::group_by(age) |>
mc_premium()
# Example 4: annual whole life net premium
life_table <- tibble::tibble(
age = 40:100,
qx = seq(0.002, 1, length.out = 61)
)
life_table |>
simulate_lifetime(age = 40, n_sim = 500, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
benefit = 1
) |>
mc_annuity(
rate = 0.05,
annuity = "whole_life",
payment = 1,
payments_per_year = 1,
timing = "due"
) |>
mc_premium()
# Example 5: monthly whole life net premium
life_table |>
simulate_lifetime(
age = 40,
n_sim = 500,
fractional = "udd",
seed = 123
) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
benefit = 1
) |>
mc_annuity(
rate = 0.05,
annuity = "whole_life",
payment = 1 / 12,
payments_per_year = 12,
timing = "due"
) |>
mc_premium()
# Example 6: term insurance with monthly premium annuity
life_table |>
simulate_lifetime(
age = 45,
n_sim = 500,
fractional = "udd",
seed = 123
) |>
mc_insurance(
rate = 0.04,
insurance = "term",
term = 20,
benefit = 100000
) |>
mc_annuity(
rate = 0.04,
annuity = "temporary",
term = 20,
payment = 1 / 12,
payments_per_year = 12,
timing = "due"
) |>
mc_premium()
# Example 7: joint-life monthly net premium
life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 500,
fractional = "udd",
seed = 123
) |>
mc_multilife_status(status = "joint_life") |>
mc_insurance(
rate = 0.04,
insurance = "whole_life",
benefit = 100000,
k_col = "K_status",
tx_col = "T_status"
) |>
mc_annuity(
rate = 0.04,
annuity = "whole_life",
payment = 1 / 12,
payments_per_year = 12,
timing = "due",
k_col = "K_status",
tx_col = "T_status"
) |>
mc_premium()
Compute Monte Carlo prospective reserves for life contingencies
Description
Computes simulated prospective reserve losses at one or more policy durations from simulated future lifetimes.
Usage
mc_reserve(
data,
duration = 0,
rate,
premium = NULL,
premium_col = "premium",
benefit = 1,
payment = 1,
payments_per_year = 1,
insurance = c("whole_life", "term", "deferred", "deferred_term", "pure_endowment",
"endowment"),
annuity = c("whole_life", "temporary", "deferred", "deferred_temporary", "certain",
"guaranteed"),
term = NULL,
deferral_years = 0,
guarantee_years = NULL,
payment_timing = c("end_of_year", "moment_of_death"),
premium_timing = c("due", "immediate"),
reserve_timing = c("before_payment", "after_payment"),
interest_type = c("effective", "nominal", "force"),
m = 1,
k_col = "Kx",
tx_col = "Tx",
in_force_basis = c("auto", "complete", "curtate"),
not_in_force = c("na", "zero"),
reserve_col = "reserve_loss"
)
Arguments
data |
A data frame or tibble containing simulated future lifetimes,
typically returned by |
duration |
Numeric vector. Policy duration or durations at which the
reserve is evaluated. Default is |
rate |
Numeric scalar. Interest rate used for discounting. |
premium |
Optional numeric scalar. Premium used in the reserve loss.
If |
premium_col |
Character string. Name of the premium column in |
benefit |
Numeric scalar. Benefit amount payable under the insurance.
Default is |
payment |
Numeric scalar. Amount of each premium annuity payment.
Default is |
payments_per_year |
Positive integer-like scalar. Number of premium
payments per year. Default is |
insurance |
Character string specifying the insurance type. Available
options are |
annuity |
Character string specifying the premium annuity type.
Available options are |
term |
Numeric scalar. Contract term in years. Required for insurance
types |
deferral_years |
Numeric scalar. Deferral period in years. Default is
|
guarantee_years |
Numeric scalar. Guaranteed payment period in years.
Required when |
payment_timing |
Character string specifying when death benefits are
paid. Available options are |
premium_timing |
Character string specifying the premium payment timing.
Available options are |
reserve_timing |
Character string specifying whether payments due
exactly at the valuation duration are included. Available options are
|
interest_type |
Character string specifying the interest rate convention.
Available options are |
m |
Numeric scalar. Number of interest conversion periods per year when
|
k_col |
Character string. Name of the column containing simulated
curtate future lifetimes. Default is |
tx_col |
Character string. Name of the column containing simulated
complete future lifetimes. Default is |
in_force_basis |
Character string specifying how the in-force indicator
is evaluated. Available options are |
not_in_force |
Character string specifying what to return for scenarios
that are not in force at the valuation duration. Available options are
|
reserve_col |
Character string. Name of the output reserve loss column.
Default is |
Details
This function recalculates future benefit and future premium present values
from each valuation duration. It is not a wrapper around mc_loss(), because
reserves require valuing only the cash flows that remain after the valuation
time.
For a policy in force at duration t, the simulated prospective loss is
L_t = Z_t - P Y_t,
where Z_t is the present value at duration t of future benefits,
Y_t is the present value at duration t of future premium
payments, and P is the premium.
The arguments m and payments_per_year have different meanings:
-
mis used only wheninterest_type = "nominal"and controls the frequency of interest conversion. -
payments_per_yearcontrols how frequently future premium payments are made.
The argument payment represents the amount of each premium payment. Thus,
for monthly premiums with total annual premium equal to 1, use
payment = 1 / 12 and payments_per_year = 12.
If payments_per_year = 1, future premiums are annual. If
payments_per_year > 1, future premiums are made at fractional times, and a
valid complete future lifetime column supplied through tx_col is required.
Durations may be integer or fractional. Fractional reserve durations require
complete future lifetimes. For example, monthly reserve calculations may use
duration = seq(0, 20, by = 1 / 12).
If reserve_timing = "before_payment", cash flows occurring exactly at the
valuation duration are included. If reserve_timing = "after_payment", cash
flows occurring exactly at the valuation duration are excluded.
If not_in_force = "na", scenarios that are not in force at a given duration
receive NA values for future present values and reserve losses. This is
useful for estimating reserves conditional on the policy still being in
force. If not_in_force = "zero", those scenarios receive zero values,
which may be useful for portfolio run-off summaries.
This function computes prospective reserves under the simulated model. It does not include expenses, surrender values, taxes, profit loadings, or statutory reserving adjustments.
Value
A tibble with one row per original simulation and per requested duration. It contains the original simulation columns and additional columns:
-
duration: valuation duration. -
in_force: logical indicator for survival or in-force status. -
reserve_timing: timing convention used at valuation. -
not_in_force: convention used for scenarios not in force. -
rate: original rate supplied. -
interest_type: interest rate convention. -
m: interest conversion frequency. -
effective_rate: equivalent annual effective interest rate. -
discount_factor: annual discount factor. -
insurance: insurance type. -
annuity: premium annuity type. -
benefit: benefit amount. -
payment: amount of each premium payment. -
payments_per_year: premium payment frequency. -
future_pv_benefit: present value at duration of future benefits. -
future_pv_premiums: present value at duration of future premium payments. -
premium: premium used in the simulated reserve loss. -
reserve_loss: simulated reserve loss, or another name supplied throughreserve_col.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetime(), simulate_lifetimes(), mc_multilife_status(),
mc_insurance(), mc_annuity(), mc_premium(), mc_loss(),
summary_mc()
Examples
life_table <- tibble::tibble(
age = 40:100,
qx = seq(0.002, 1, length.out = 61)
)
# Annual prospective reserves for whole life insurance
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_reserve(
duration = c(0, 5, 10, 20),
rate = 0.05,
insurance = "whole_life",
annuity = "whole_life",
benefit = 1,
payment = 1,
payments_per_year = 1,
premium_timing = "due"
)
# Monthly premium reserve curve
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_reserve(
duration = seq(0, 10, by = 1),
rate = 0.05,
insurance = "whole_life",
annuity = "whole_life",
benefit = 1,
payment = 1 / 12,
payments_per_year = 12,
premium_timing = "due"
)
# Monthly reserves by fractional policy duration
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_reserve(
duration = seq(0, 5, by = 1 / 12),
rate = 0.05,
insurance = "whole_life",
annuity = "whole_life",
benefit = 1,
payment = 1 / 12,
payments_per_year = 12,
premium_timing = "due"
) |>
summary_mc(value_col = "reserve_loss", by = "duration")
# Joint-life reserve using a multiple-life status
life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_multilife_status(status = "joint_life") |>
mc_reserve(
duration = c(0, 5, 10),
rate = 0.04,
insurance = "whole_life",
annuity = "whole_life",
benefit = 1,
payment = 1,
payments_per_year = 1,
k_col = "K_status",
tx_col = "T_status"
)
Multiple decrement table (annual, discrete ages)
Description
Builds a multiple decrement table from cause-specific annual decrement
probabilities q_x^{(j)}. This function is annual/discrete: ages must be
integer-valued and the input probabilities are interpreted as one-year
decrement probabilities for each cause.
Usage
md_table(
qx_df,
age_col = "x",
cause_cols = NULL,
radix = 1e+05,
close = TRUE,
check = TRUE,
tol = 1e-10
)
Arguments
qx_df |
A data.frame/tibble with an age column (default |
age_col |
Character. Name of the age column (default |
cause_cols |
Character vector. Names of the cause columns. If |
radix |
Numeric. Starting cohort size at the first age (default |
close |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance used in checks (default |
Details
Let the cause columns be q_x^{(1)}, \dots, q_x^{(J)}. The total decrement
probability is q_x^{(\tau)} = \sum_j q_x^{(j)} and the total survival
probability is p_x^{(\tau)} = 1 - q_x^{(\tau)}. The cohort is generated
recursively by \ell_{x+1} = \ell_x \, p_x^{(\tau)} with starting radix
\ell_{x_0} = \text{radix}.
If close = TRUE, the last age (omega) must satisfy
q_{\omega}^{(\tau)} = 1 (within tolerance), so that the table closes
naturally.
Value
A tibble with columns:
-
x: integer ages. -
lx: cohort\ell_x. -
q_total: total decrement probabilityq_x^{(\tau)}. -
p_total: total survival probabilityp_x^{(\tau)}. -
d_total: total decrementsd_x^{(\tau)} = \ell_x q_x^{(\tau)}. cause columns (as provided).
cause-specific decrements
d_*withd_x^{(j)} = \ell_x q_x^{(j)}.
Examples
qx_df <- tibble::tibble(
x = 30:35,
q_death = c(0.001, 0.0012, 0.0014, 0.0017, 0.0020, 1.0000),
q_disability = c(0.002, 0.0021, 0.0022, 0.0023, 0.0024, 0.0000)
)
md <- md_table(qx_df, radix = 1e5, close = TRUE)
md
Colombian mortality tables
Description
A tidy collection of Colombian mortality tables for life-contingency examples. The dataset includes regulatory and pedagogical mortality tables used in Colombian actuarial applications.
Usage
mortality_colombia_tables
Format
A tibble with 12 variables:
- table
Mortality table identifier.
- sex
Sex category, typically
"male"or"female".- x
Integer age.
- lx
Number of survivors at exact age
x.- dx
Expected number of deaths between ages
xandx + 1.- qx
One-year death probability between ages
xandx + 1.- px
One-year survival probability between ages
xandx + 1.- mu
Force of mortality, if available.
- ex
Complete life expectancy at age
x, if available.- source
Source identifier or reference.
- qx_calculated
Death probability recalculated from
lxanddx, when available.- qx_difference
Difference between reported
qxand recalculatedqx, when available.
Details
The dataset is intended for actuarial examples involving Colombian mortality tables, survival probabilities, life annuities, life insurance present values, and validation of life-table calculations.
The variables qx_calculated and qx_difference are included as
validation aids. They allow users to compare reported death probabilities
against probabilities reconstructed from lx and dx.
Some tables may start at different initial ages, use different terminal ages, or use different radix values. Users should filter the desired table and sex before passing the data to life-contingency functions.
Source
Colombian mortality tables cleaned for tidyactuarial examples. Source
identifiers are provided in the source column.
Examples
data(mortality_colombia_tables)
head(mortality_colombia_tables)
mortality_colombia_tables |>
dplyr::count(table, sex)
rv08_male <- mortality_colombia_tables |>
dplyr::filter(table == "RV08_Rentistas_2005_2008", sex == "male") |>
dplyr::select(x, lx, dx, qx, px, ex)
head(rv08_male)
Generate a tidy life table from a theoretical mortality law
Description
Creates a tidy life table with one row per integer age from a parametric
mortality law. The output follows tidyactuarial conventions and includes
columns such as x, qx, px, lx, dx, and
mx.
Usage
mortality_law_table(
law = c("Exponential", "Gompertz", "Makeham", "Weibull", "Logistic", "DeMoivre",
"Beta", "HeligmanPollard"),
x_min,
x_max,
...,
params = NULL,
frac = c("CF", "UDD", "Balducci"),
radix = 1e+05,
close = TRUE,
ax = 0.5,
check = TRUE,
tol = 1e-10
)
Arguments
law |
Character. Mortality law. One of |
x_min |
Integer. Minimum age, inclusive. |
x_max |
Integer. Maximum age, inclusive. Must satisfy |
... |
Named law parameters. These values override |
params |
Named list of law parameters, or |
frac |
Character. Within-year assumption used to convert |
radix |
Numeric. Starting cohort size at age |
close |
Logical. If |
ax |
Numeric. Average fraction of the year lived by those dying. |
check |
Logical. If |
tol |
Numeric. Tolerance used in checks. |
Value
A tibble with columns x, law, frac, mu_x,
qx, px, lx, dx, Lx, Tx,
ex, and mx.
Supported laws
-
Exponential:
\mu_x = \lambda -
Gompertz:
\mu_x = B c^x -
Makeham:
\mu_x = A + B c^x -
Weibull:
\mu_x = (k/\lambda)(x/\lambda)^{k-1} -
Logistic:
\mu_x = (A + B c^x)/(1 + C c^x) -
DeMoivre: finite lifetime law with
q_x = 1/(\omega - x)forx < \omega -
Beta: scaled lifetime model
X/\omega \sim Beta(\alpha,\beta) -
HeligmanPollard: odds model returning
q_xdirectly
Converting mu(x) to qx
For laws defined by a force of mortality \mu_x, the one-year death
probability q_x is obtained using frac:
-
"CF":q_x = 1 - \exp(-\mu_x) -
"UDD":q_x = \mu_x -
"Balducci":q_x = \mu_x/(1+\mu_x)
Direct qx laws
"DeMoivre", "Beta", and "HeligmanPollard" define
qx directly. For these laws, frac is not used to derive
qx.
Closure
If close = TRUE, the last age is forced to close the table by setting
qx[x_max] = 1 and px[x_max] = 0.
Examples
mortality_law_table("Exponential", 0, 110, lambda = 0.01)
mortality_law_table("Gompertz", 0, 110, B = 1e-5, c = 1.08)
mortality_law_table("Makeham", 0, 110, A = 5e-4, B = 1e-6, c = 1.10)
mortality_law_table("Weibull", 1, 110, k = 2.5, lambda = 90)
mortality_law_table("Logistic", 0, 110, A = 1e-4, B = 1e-6, c = 1.10, C = 1e-3)
mortality_law_table("DeMoivre", 0, 100, omega = 100)
mortality_law_table("Beta", 0, 100, alpha = 2, beta = 5, omega = 101)
mortality_law_table(
"HeligmanPollard", 1, 110,
A = 0.0002, B = 0.1, C = 0.03, D = 10, E = 20, F = 0.00005, G = 1.08
)
World mortality sample panel, 2015–2023
Description
A compact international panel of period life tables for selected countries from 2015 to 2023. The dataset is intended for comparative mortality examples, especially before, during, and after the COVID-19 pandemic period.
Usage
mortality_world_sample_2015_2023
Format
A tibble with 35,451 rows and 14 variables:
- country
Country name.
- country_code
Numeric ISO country code.
- continent
Continent.
- region
Geographic region.
- year
Calendar year, from 2015 to 2023.
- pandemic_period
Period label:
"pre_pandemic","pre_pandemic_reference","pandemic","transition", or"post_pandemic".- sex
Sex category:
"male","female", or"both".- age
Integer age, from 0 to 100.
- mx
Central death rate at age
x.- qx
One-year death probability between ages
xandx + 1.- px
One-year survival probability between ages
xandx + 1.- lx
Number of survivors at exact age
x, based on radix 100,000.- dx
Expected number of deaths between ages
xandx + 1.- source
Data source.
Details
The dataset is derived from central death rates mx. Death probabilities
qx were computed using the annual approximation
q_x = \frac{m_x}{1 + (1 - a_x)m_x},
with a_x = 0.5. The last available age is closed by setting
qx = 1. Survivors lx and expected deaths dx are then
reconstructed recursively from a radix of 100,000.
This dataset is a selected-country panel, not a complete world mortality database.
Source
United Nations, World Population Prospects 2024.
Examples
data(mortality_world_sample_2015_2023)
mortality_world_sample_2015_2023 |>
dplyr::filter(country == "Colombia", sex == "both", age == 70) |>
dplyr::select(year, pandemic_period, qx, lx)
World mortality sample, 2023
Description
A compact international sample of period life tables for selected countries in 2023. The dataset is intended for recent and simple examples involving central death rates, one-year death probabilities, survival probabilities, and life-table calculations.
Usage
mortality_world_sample_2023
Format
A tibble with 3,939 rows and 13 variables:
- country
Country name.
- country_code
Numeric ISO country code.
- continent
Continent.
- region
Geographic region.
- year
Calendar year.
- sex
Sex category:
"male","female", or"both".- age
Integer age, from 0 to 100.
- mx
Central death rate at age
x.- qx
One-year death probability between ages
xandx + 1.- px
One-year survival probability between ages
xandx + 1.- lx
Number of survivors at exact age
x, based on radix 100,000.- dx
Expected number of deaths between ages
xandx + 1.- source
Data source.
Details
The dataset is derived from central death rates mx. Death probabilities
qx were computed using the annual approximation
q_x = \frac{m_x}{1 + (1 - a_x)m_x},
with a_x = 0.5. The last available age is closed by setting
qx = 1. Survivors lx and expected deaths dx are then
reconstructed recursively from a radix of 100,000.
This dataset is a selected-country sample, not a complete world mortality database.
Source
United Nations, World Population Prospects 2024.
Examples
data(mortality_world_sample_2023)
mortality_world_sample_2023 |>
dplyr::filter(country == "Colombia", sex == "both") |>
dplyr::select(age, mx, qx, px, lx, dx)
Sample multiple decrement probabilities
Description
A small pedagogical annual multiple decrement dataset with three causes: death, disability, and withdrawal. It is intended for examples involving multiple decrement tables, total-decrement life tables, cause-specific decrement probabilities, and cause-specific insurance benefits.
A small pedagogical annual multiple decrement dataset with three causes: death, disability, and withdrawal. It is intended for examples involving multiple decrement tables, total-decrement life tables, cause-specific decrement probabilities, and cause-specific insurance benefits.
Usage
multiple_decrement_sample
multiple_decrement_sample
Format
A tibble with 7 rows and 6 variables:
- x
Integer age.
- q_death
One-year death decrement probability.
- q_disability
One-year disability decrement probability.
- q_withdrawal
One-year withdrawal decrement probability.
- q_total
Total one-year decrement probability.
- p_total
Total one-year survival probability.
A tibble with 7 rows and 6 variables:
- x
Integer age.
- q_death
One-year death decrement probability.
- q_disability
One-year disability decrement probability.
- q_withdrawal
One-year withdrawal decrement probability.
- q_total
Total one-year decrement probability.
- p_total
Total one-year survival probability.
Source
Synthetic pedagogical data created for tidyactuarial examples.
Synthetic pedagogical data created for tidyactuarial examples.
Examples
data(multiple_decrement_sample)
md <- md_table(
qx_df = multiple_decrement_sample |>
dplyr::select(x, q_death, q_disability, q_withdrawal),
radix = 100000,
close = FALSE
)
md
data(multiple_decrement_sample)
md <- md_table(
qx_df = multiple_decrement_sample |>
dplyr::select(x, q_death, q_disability, q_withdrawal),
radix = 100000,
close = FALSE
)
md
Plot a cash-flow diagram
Description
Creates a professional cash-flow diagram with arrows representing inflows and outflows over time.
Usage
plot_cash_flow(
.data = NULL,
payment,
time = NULL,
date = NULL,
rate = NULL,
i = NULL,
pv = NULL,
title = NULL,
subtitle = NULL,
x_label = NULL,
amount_label = "Cash flow",
financial = TRUE,
normalize = FALSE,
aggregate = TRUE,
show_labels = TRUE,
label_size = 3.5,
arrow_size = 0.8,
timeline_size = 0.7,
label_digits = 2L,
currency = "",
col_inflow = "#1B9E77",
col_outflow = "#D95F02",
date_labels = "%Y-%m-%d",
day_count = c("act/365", "act/360")
)
Arguments
.data |
Optional data.frame or tibble containing cash-flow columns.
If supplied, |
payment |
Numeric vector of cash flows, or a column name when |
time |
Optional numeric vector of times, or a column name when |
date |
Optional date vector, or a column name when |
rate |
Optional annual effective interest rate used to compute present
value if |
i |
Deprecated alias for |
pv |
Optional numeric present value to display. |
title |
Optional plot title. |
subtitle |
Optional plot subtitle. If |
x_label |
Optional x-axis label. |
amount_label |
Optional y-axis label when |
financial |
Logical. If |
normalize |
Logical. If |
aggregate |
Logical. If |
show_labels |
Logical. If |
label_size |
Numeric text size for labels. |
arrow_size |
Numeric line width for arrows. |
timeline_size |
Numeric line width for the time axis. |
label_digits |
Integer number of decimal digits for payment labels. |
currency |
Optional currency or unit prefix, such as |
col_inflow |
Character color for inflow arrows. |
col_outflow |
Character color for outflow arrows. |
date_labels |
Character date-label format passed to
|
day_count |
Day-count convention used when |
Details
The function supports both classical vector input and tidyverse-style data input through the pipe. It also supports either numeric times or calendar dates.
By default, the plot uses real cash-flow magnitudes on the vertical axis.
This is preferable when the diagram is intended to communicate financial
scale. Use normalize = TRUE only when an intentionally schematic diagram
is desired.
If an interest rate is supplied and pv is NULL, the present value is
computed as
PV = \sum_k C_k (1+i)^{-t_k}
and displayed in the subtitle.
Value
A ggplot2 object.
See Also
pv_flow(), fv_flow(), irr_flow()
Other time-value:
future_value(),
fv_flow(),
irr_flow(),
irr_flow_multi(),
present_value(),
pv_flow()
Examples
plot_cash_flow(
payment = c(-1000, 300, 400, 500),
time = c(0, 1, 2, 3),
rate = 0.08,
currency = "$"
)
cashflows <- tibble::tibble(
time = c(0, 1, 2, 3),
payment = c(-1000, 300, 400, 500)
)
cashflows |>
plot_cash_flow(payment = payment, time = time, rate = 0.08)
dated_flows <- tibble::tibble(
date = as.Date(c("2026-01-01", "2026-07-01", "2027-01-01")),
payment = c(-1000, 450, 700)
)
dated_flows |>
plot_cash_flow(payment = payment, date = date, rate = 0.08)
Plot immunization performance under interest rate shifts
Description
Computes and plots the difference between the present value of liabilities and the present value of an immunized asset portfolio under small interest rate changes. This allows visual evaluation of duration or duration-convexity immunization quality.
Usage
plot_immunization_gap(
liabilities,
t_liabilities,
asset_cashflows,
weights,
i0,
delta = 0.01,
n_grid = 200L
)
Arguments
liabilities |
Numeric vector of liability payments. |
t_liabilities |
Numeric vector of times (periods) of each liability payment. |
asset_cashflows |
A list where each element is a list with components
|
weights |
Numeric vector of portfolio weights (amount invested in each
asset). Must have same length as |
i0 |
Base effective interest rate per period. |
delta |
A numeric value defining the range of rates: from
|
n_grid |
Number of rate values to evaluate. |
Details
Let v(i) = 1/(1+i). For a liability stream
L_k at time t_k:
PV_L(i) = \sum_k L_k \, v(i)^{t_k}
For a portfolio of assets with weights w_j:
PV_A(i) = \sum_j w_j \, PV_j(i)
The curve \Delta(i) = PV_A(i) - PV_L(i)
illustrates immunization robustness. Under perfect duration immunization,
this curve is tangent to zero at i = i_0 and non-negative
nearby if the convexity condition is also met.
Value
A ggplot2 object showing the PV difference curve
PV_A(i) - PV_L(i) and a zero reference line.
See Also
immunize_duration,
immunize_duration_convexity,
bond_duration, bond_convexity
Other immunization:
immunize_duration(),
immunize_duration_convexity()
Examples
# Two-asset duration immunization gap
plot_immunization_gap(
liabilities = c(5000, 8000),
t_liabilities = c(3, 7),
asset_cashflows = list(
list(payment = c(0, 0, 100), time = c(1, 2, 3)),
list(payment = c(0, 0, 0, 0, 0, 0, 200), time = 1:7)
),
weights = c(5, 2.5),
i0 = 0.05,
delta = 0.02
)
Plot a Kaplan–Meier survival curve
Description
Creates a step-function plot of the Kaplan–Meier survival estimate
\hat S(t) with optional pointwise confidence bands. Designed to
work directly with the output of km_lifetable.
Usage
plot_km(
km,
time_col = "time",
surv_col = "S",
lower_col = "ci_low",
upper_col = "ci_high",
conf_int = TRUE,
title = NULL
)
Arguments
km |
A data frame or tibble with at least columns for time and
survival. Can also be the full list returned by |
time_col |
Character. Name of the time column. Default |
surv_col |
Character. Name of the survival column. Default |
lower_col |
Character. Name of the lower CI column. Default
|
upper_col |
Character. Name of the upper CI column. Default
|
conf_int |
Logical. If |
title |
Optional character string for the plot title. |
Details
Both the survival curve and the confidence band are rendered as step
functions (using geom_step), which is the correct
representation for the KM estimator - a right-continuous step function
that drops at each observed event time.
The confidence band uses geom_stepribbon logic: the data is
internally expanded so that a ribbon-fill follows the step pattern rather
than interpolating linearly between event times.
Value
A ggplot object that can be further customised with
additional ggplot2 layers.
See Also
km_lifetable for fitting the KM estimator and
building the empirical life table.
Examples
set.seed(42)
n <- 150
time <- rexp(n, rate = 0.05)
status <- rbinom(n, 1, prob = 0.7)
# Fit KM and plot directly
out <- km_lifetable(time, status, breaks = 0:30)
# Pass the full list - $km is extracted automatically
plot_km(out)
# Or pass just the km tibble
plot_km(out$km)
# Without confidence band
plot_km(out, conf_int = FALSE, title = "KM Survival Curve")
# Customise with ggplot2 layers
if (requireNamespace("ggplot2", quietly = TRUE)) {
plot_km(out) +
ggplot2::geom_hline(yintercept = 0.5, linetype = "dashed") +
ggplot2::labs(subtitle = "Dashed line = median survival")
}
Compute portfolio convexity as a market-value-weighted average
Description
Computes portfolio convexity from individual position convexities using market values as weights.
Usage
portfolio_convexity(
.data = NULL,
portfolio_id = NULL,
market_value = NULL,
convexity = NULL,
col_portfolio = "portfolio_id",
col_market_value = "market_value",
col_convexity = "convexity",
.out = "portfolio_convexity",
.out_value = "portfolio_market_value",
.out_n = "n_positions",
.na = c("propagate", "error", "drop")
)
Arguments
.data |
A data.frame or tibble. If |
portfolio_id |
Optional vector of portfolio identifiers when
|
market_value |
Numeric vector of market values when |
convexity |
Numeric vector of individual convexities when
|
col_portfolio |
Name of the portfolio identifier column. If |
col_market_value |
Name of the numeric column containing market values. |
col_convexity |
Name of the numeric column containing individual convexities. |
.out |
Name of the output column containing portfolio convexity. |
.out_value |
Name of the output column containing total portfolio market value. |
.out_n |
Name of the output column containing the number of positions used in the calculation. |
.na |
NA handling policy: |
Details
This is a summarise-style tibble-first function. Each input row represents one position, and each output row represents one portfolio.
The function does not compute individual convexities from bond terms or yields. Instead, it assumes that the input convexity column already contains valid convexity measures on a common basis within each portfolio.
The portfolio convexity is computed as:
C_P = \frac{\sum_{k=1}^n P_k C_k}{\sum_{k=1}^n P_k}
where P_k is the market value of position k and
C_k is its convexity.
Value
A tibble with one row per portfolio and columns for portfolio convexity, total market value, and number of positions used.
References
Marcel B. Finan, A Basic Course in the Theory of Interest and Derivatives Markets: A Preparation for the Actuarial Exam FM/2, Section 55: Redington Immunization and Convexity.
Kellison, S. G. The Theory of Interest, Chapter 11: Duration, Convexity and Immunization.
See Also
portfolio_duration,
bond_convexity, bond_duration
Other bonds:
bond_book_value(),
bond_callable_price(),
bond_convexity(),
bond_duration(),
bond_price(),
bond_ytm(),
portfolio_duration()
Examples
# Simple example: one portfolio
portfolio_convexity(
market_value = c(1000, 2000, 500),
convexity = c(20, 12, 35)
)
# Medium example: two portfolios
positions <- tibble::tibble(
portfolio_id = c("A", "A", "B", "B"),
market_value = c(1000, 2000, 1000 / 1.08^2, 1000 / 1.08^4),
convexity = c(20, 12, 6, 18)
)
portfolio_convexity(
positions,
col_portfolio = "portfolio_id",
col_market_value = "market_value",
col_convexity = "convexity"
)
Compute portfolio duration as a market-value-weighted average
Description
Computes portfolio duration from individual position durations using market values as weights.
Usage
portfolio_duration(
.data = NULL,
portfolio_id = NULL,
market_value = NULL,
duration = NULL,
col_portfolio = "portfolio_id",
col_market_value = "market_value",
col_duration = "duration",
.out = "portfolio_duration",
.out_value = "portfolio_market_value",
.out_n = "n_positions",
.na = c("propagate", "error", "drop")
)
Arguments
.data |
A data.frame or tibble. If |
portfolio_id |
Optional vector of portfolio identifiers when
|
market_value |
Numeric vector of market values when |
duration |
Numeric vector of individual durations when
|
col_portfolio |
Name of the portfolio identifier column. If |
col_market_value |
Name of the numeric column containing market values. |
col_duration |
Name of the numeric column containing individual durations. |
.out |
Name of the output column containing portfolio duration. |
.out_value |
Name of the output column containing total portfolio market value. |
.out_n |
Name of the output column containing the number of positions used in the calculation. |
.na |
NA handling policy: |
Details
This is a summarise-style tibble-first function. Each input row represents one position, and each output row represents one portfolio.
The function does not compute individual durations from bond terms or yields. Instead, it assumes that the input duration column already contains valid duration measures on a common basis within each portfolio.
The portfolio duration is computed as:
D_P = \frac{\sum_{k=1}^n P_k D_k}{\sum_{k=1}^n P_k}
where P_k is the market value of position k and
D_k is its duration.
Value
A tibble with one row per portfolio and columns for portfolio duration, total market value, and number of positions used.
References
Marcel B. Finan, A Basic Course in the Theory of Interest and Derivatives Markets: A Preparation for the Actuarial Exam FM/2, Section 54: Macaulay and Modified Durations.
Kellison, S. G. The Theory of Interest, Chapter 11: Duration, Convexity and Immunization.
See Also
portfolio_convexity,
bond_duration, bond_convexity
Other bonds:
bond_book_value(),
bond_callable_price(),
bond_convexity(),
bond_duration(),
bond_price(),
bond_ytm(),
portfolio_convexity()
Examples
# Simple example: one portfolio
portfolio_duration(
market_value = c(1000, 2000, 500),
duration = c(7, 5, 10)
)
# Medium example: two portfolios
positions <- tibble::tibble(
portfolio_id = c("A", "A", "B", "B"),
market_value = c(1000, 2000, 1000 / 1.08^2, 1000 / 1.08^4),
duration = c(7, 5, 2, 4)
)
portfolio_duration(
positions,
col_portfolio = "portfolio_id",
col_market_value = "market_value",
col_duration = "duration"
)
Gross (expense-loaded) premium from net premium
Description
Adjusts a net premium using a simple expense structure
(\alpha, \beta, \gamma) to obtain the gross or commercial premium
through the extended equivalence principle.
Usage
premium_gross(
prem,
alpha = 0,
beta = 0,
gamma = 0,
output = c("value", "table")
)
Arguments
prem |
A one-row data frame or tibble containing at least:
|
alpha |
Numeric scalar greater than or equal to 0. Initial acquisition
expense as a multiple of one gross premium payment. The initial expense is
|
beta |
Numeric scalar in |
gamma |
Numeric scalar greater than or equal to 0. Fixed maintenance expense per premium payment period, in monetary units. |
output |
Character string. Use |
Details
The function is designed to work with the detailed output of
premium_x using output = "table". It can also be used
with any one-row tibble containing the columns premium and
apv_premiums.
The extended equivalence principle equates the APV of gross premiums with the APV of benefits plus expenses:
G \ddot{a} =
P_{\text{net}}\ddot{a}
+ \alpha G
+ \beta G \ddot{a}
+ \gamma \ddot{a}.
Solving for the gross premium gives:
G =
\frac{P_{\text{net}} + \gamma}
{(1-\beta) - \alpha / \ddot{a}}.
In this function, \ddot{a} is supplied through the
apv_premiums column of prem.
Value
If output = "value", a numeric gross premium per payment.
If output = "table", a one-row tibble with columns
gross_premium, net_premium, alpha, beta,
gamma, loading_pct, and apv_premiums.
See Also
premium_x for single-life net premiums,
premium_xy for two-life net premiums,
annuity_x for building custom expense APVs.
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)
# Full workflow: net premium -> gross premium
net <- premium_x(
mortality_table = lt,
age = 60,
rate = 0.05,
insurance_type = "whole",
benefit = 100000,
output = "table"
)
premium_gross(net, alpha = 0.5, beta = 0.05, gamma = 50)
# Finan-style expense structure:
# 10% of each premium plus fixed expenses of 275 per payment period
premium_gross(net, alpha = 0, beta = 0.10, gamma = 275)
# Detailed output with expense breakdown
premium_gross(
net,
alpha = 0.5,
beta = 0.05,
gamma = 50,
output = "table"
)
# No expenses: gross = net
premium_gross(net)
Net premium for life insurance by the equivalence principle
Description
Computes the net benefit premium of a life insurance contract using the equivalence principle:
P = \frac{\text{APV of benefits}}{\text{APV of premium annuity}}.
Usage
premium_x(
mortality_table,
age,
rate,
rate_type = "effective",
m = 1L,
insurance_type = c("whole", "term", "endowment", "variable_k"),
benefit = 1,
term_years = Inf,
deferral_years = 0L,
payments_per_year = 1L,
frac = c("UDD", "CF", "CML", "Balducci"),
premium_timing = c("due", "immediate"),
premium_start = c("issue", "deferred"),
premium_term_years = NULL,
woolhouse = c("none", "first", "second"),
output = c("value", "table"),
check = TRUE
)
Arguments
mortality_table |
A life table data frame containing at least columns
|
age |
Integer actuarial age at issue. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. Ignored
for |
insurance_type |
Type of insurance contract. One of |
benefit |
Benefit amount. For standard products, a single nonnegative
numeric value. For |
term_years |
Term of the insurance contract in years. Use |
deferral_years |
Integer deferral period in years. |
payments_per_year |
Positive integer. Number of premium payments per year. |
frac |
Fractional-age assumption used only for
|
premium_timing |
Timing of premium payments. Use |
premium_start |
Start of premium payments. Use |
premium_term_years |
Optional premium-paying term in years, counted from
|
woolhouse |
Woolhouse order for the premium annuity when
|
output |
Character string. Use |
check |
Logical. If |
Details
The premium returned corresponds to one premium payment. For example, when
payments_per_year = 12, the returned value is the monthly premium.
The benefit premium is the level payment satisfying the equivalence principle: the APV of premiums equals the APV of benefits at issue.
For standard products, the APV of benefits is computed with
insurance_x. The APV of the premium annuity is computed with
annuity_x, supporting k-thly payments and Woolhouse
approximations.
Value
If output = "value", a numeric scalar with the net premium per
payment.
If output = "table", a one-row tibble with the main inputs, APV of
benefits, APV of premiums, premium per payment, and annualized premium.
See Also
insurance_x, annuity_x,
premium_xy, premium_gross
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)
# Whole life insurance, annual premium
premium_x(
mortality_table = lt,
age = 60,
rate = 0.05,
insurance_type = "whole",
benefit = 100000
)
# Verify manually: P = A / a-double-dot
A <- insurance_x(
mortality_table = lt,
age = 60,
rate = 0.05,
insurance_type = "whole",
benefit = 100000
)
ad <- annuity_x(
mortality_table = lt,
age = 60,
rate = 0.05,
timing = "due"
)
A / ad
# Five-year term insurance
premium_x(
mortality_table = lt,
age = 60,
rate = 0.05,
insurance_type = "term",
term_years = 5,
benefit = 100000
)
# Table output
premium_x(
mortality_table = lt,
age = 60,
rate = 0.05,
insurance_type = "term",
term_years = 5,
benefit = 100000,
output = "table"
)
# Monthly premiums paid for a shorter period than the coverage term
premium_x(
mortality_table = lt,
age = 60,
rate = 0.05,
insurance_type = "term",
term_years = 5,
benefit = 100000,
payments_per_year = 12,
premium_term_years = 3
)
Net premium for two-life insurance by the equivalence principle
Description
Computes the net benefit premium for a two-life insurance contract using the equivalence principle.
Usage
premium_xy(
mortality_table,
age_x = NULL,
age_y = NULL,
rate = NULL,
rate_type = NULL,
m = NULL,
insurance_type = c("whole", "term", "endowment", "pure_endowment"),
benefit = 1,
term_years = Inf,
deferment_years = 0L,
payments_per_year = 1L,
frac = c("UDD", "CF", "CML", "Balducci"),
premium_timing = c("due", "immediate"),
premium_start = c("issue", "deferred"),
premium_term_years = NULL,
cohort = c("first", "last"),
output = c("value", "table"),
check = TRUE,
tol = 1e-10
)
Arguments
mortality_table |
Either a single life table used for both lives, or a
list of two life tables |
age_x |
Integer actuarial age for the first life. |
age_y |
Integer actuarial age for the second life. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. |
insurance_type |
Type of insurance: |
benefit |
Benefit amount. Must be a single nonnegative number. |
term_years |
Optional insurance term in years after deferment. Required
for |
deferment_years |
Nonnegative integer deferral period in years. |
payments_per_year |
Number of premium payments per year. |
frac |
Fractional-age assumption used for fractional premium payment
times: |
premium_timing |
Timing of premium payments: |
premium_start |
Start of premium payments: |
premium_term_years |
Optional premium-paying term in years, counted from
|
cohort |
Status definition: |
output |
Character string. Use |
check |
Logical. If |
tol |
Numeric tolerance for integer checks. |
Details
The premium returned corresponds to one premium payment. For example, if
payments_per_year = 1, it is an annual premium; if
payments_per_year = 12, it is a monthly premium.
This function separates:
the insurance coverage period, controlled by
insurance_type,term_years, anddeferment_years;the premium-paying period, controlled by
premium_term_years,premium_start,premium_timing, andpayments_per_year.
The function assumes independent future lifetimes.
The supported two-life statuses are:
-
cohort = "first": joint-life status. The status survives while both lives survive. -
cohort = "last": last-survivor status. The status survives while at least one life survives.
Let P(t) denote the probability that the selected two-life status survives
t years from issue. For integer death benefits paid at the end of the year
of status failure, the benefit APV for a term insurance deferred h years
and lasting n years is
B \sum_{r=h}^{h+n-1} v^{r+1}\{P(r)-P(r+1)\}.
For an endowment insurance, the pure endowment benefit
B v^(h+n) P(h+n) is added.
Premiums are contingent on the selected two-life status being in force at the premium payment time.
Value
If output = "value", a numeric net premium per payment.
If output = "table", a one-row tibble with premium details.
See Also
premium_x(), insurance_xy(), annuity_xy(), reserve_xy()
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
reserve_x(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:110,
lx = seq(100000, 0, length.out = 51)
)
premium_xy(
mortality_table = lt,
age_x = 60,
age_y = 62,
rate = 0.05,
insurance_type = "term",
term_years = 5,
premium_term_years = 3,
payments_per_year = 12,
cohort = "last",
benefit = 100000,
output = "table"
)
lt |>
life_contract(lives = "joint", age_x = 60, age_y = 62, rate = 0.05) |>
premium_xy(
insurance_type = "term",
term_years = 5,
premium_term_years = 3,
payments_per_year = 12,
cohort = "first",
benefit = 100000
)
Present value of a single payment
Description
Computes the present value of a future payment due at a given time, using the annual effective interest rate implied by the supplied rate specification.
Usage
present_value(
amount,
rate,
rate_type = "effective",
m = 1,
time,
output = c("value", "table")
)
Arguments
amount |
Numeric vector of future payment amounts. |
rate |
Numeric vector of rate values. |
rate_type |
Character vector indicating the rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency
for nominal rates. Ignored for |
time |
Numeric vector of times in years until payment. |
output |
Character string. Use |
Details
The present value is computed as
PV = C v^t = \frac{C}{(1+i)^t}
where i is the annual effective interest rate and
v = (1+i)^{-1} is the annual discount factor.
The input interest rate may be supplied as:
annual effective interest rate,
nominal annual interest rate,
nominal annual discount rate,
force of interest.
Internally, all rate specifications are first converted to the equivalent
annual effective interest rate using standardize_interest.
Input vectors must have length 1 or a common length. Missing values are propagated. This function does not accept dates.
Value
If output = "value", a numeric vector of present values.
If output = "table", a tibble with input values, equivalent rates,
discount factors, and present values.
See Also
standardize_interest, future_value
Other time-value:
future_value(),
fv_flow(),
irr_flow(),
irr_flow_multi(),
plot_cash_flow(),
pv_flow()
Examples
# Numeric present value
present_value(amount = 1000, rate = 0.08, time = 3)
# Nominal interest converted monthly
present_value(
amount = 1000,
rate = 0.12,
rate_type = "nominal_interest",
m = 12,
time = 5
)
# Tibble output for teaching or auditing
present_value(
amount = 1000,
rate = 0.08,
time = 3,
output = "table"
)
# Vectorized example
present_value(
amount = c(1000, 2500, 4000),
rate = c(0.08, 0.10, 0.12),
rate_type = c("effective", "nominal_interest", "force"),
m = c(1, 12, 1),
time = c(3, 5, 2)
)
Present value of a general cash flow
Description
Computes the present value of a cash-flow vector under either:
a constant interest-rate specification, or
a term structure of spot rates, one rate per cash flow.
Usage
pv_flow(
payment,
rate,
type = "effective",
m = 1L,
time = NULL,
date = NULL,
day_count = c("act/365", "act/360")
)
Arguments
payment |
Numeric vector of cash flows. |
rate |
Numeric scalar or numeric vector of rate values. |
type |
Character vector indicating the rate type:
|
m |
Positive integer vector giving the compounding frequency for
nominal rates. May have length 1 or the same length as |
time |
Optional numeric vector of payment times in years. |
date |
Optional vector of payment dates. If supplied, the earliest date is treated as time 0. |
day_count |
Day-count convention used to convert dates to year fractions.
One of |
Details
The cash flow is supplied explicitly through payment. Its timing is
supplied either through time (in years) or date (calendar
dates). If date is supplied, the earliest date is taken as time 0.
Interest-rate input:
If
ratehas length 1, the same rate is used for all payments.If
ratehas the same length aspayment, each rate is interpreted as the spot rate associated with the corresponding payment time.
Rate types may be supplied in FM-style notation:
annual effective rate
i,nominal annual interest rate
j^{(m)},nominal annual discount rate
d^{(m)},force of interest
\delta.
Internally, all supplied rates are converted to annual effective rates using
standardize_interest.
When rate is a vector of spot rates, the discounting formula is
PV = \sum_{k=1}^n \frac{C_k}{(1+i_k)^{t_k}}
where i_k is the annual effective spot rate corresponding to
payment k. When a single constant rate is supplied,
i_k = i for all k.
Value
Numeric scalar: the present value of the cash flow.
See Also
fv_flow, present_value,
irr_flow, standardize_interest
Other time-value:
future_value(),
fv_flow(),
irr_flow(),
irr_flow_multi(),
plot_cash_flow(),
present_value()
Examples
# Constant annual effective rate
pv_flow(
payment = c(100, 150, 200),
rate = 0.08,
type = "effective",
time = c(0, 1, 2)
)
# Spot rates, one per payment
pv_flow(
payment = c(100, 150, 200),
rate = c(0.05, 0.055, 0.06),
type = "effective",
time = c(1, 2, 3)
)
# Using dates; earliest date is taken as t = 0
pv_flow(
payment = c(100, 150, 200),
rate = c(0.05, 0.055, 0.06),
type = "effective",
date = as.Date(c("2026-01-10", "2027-01-10", "2028-01-10"))
)
# Nominal rates by payment
pv_flow(
payment = c(100, 100, 100),
rate = c(0.12, 0.12, 0.12),
type = "nominal_interest",
m = c(12, 12, 12),
time = c(1, 2, 3)
)
Benefit reserve schedule for single-life insurance
Description
Computes terminal benefit reserves at selected policy durations for a fully discrete single-life insurance contract.
Usage
reserve_x(
mortality_table,
age,
rate,
rate_type = "effective",
m = 1L,
insurance_type = c("whole", "term", "endowment"),
term_years = Inf,
benefit = 1,
premium = NULL,
premium_term_years = NULL,
durations = NULL,
method = c("prospective", "recursive"),
output = c("table", "value")
)
Arguments
mortality_table |
A life table data frame with columns |
age |
Integer actuarial age at issue. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. Ignored
for |
insurance_type |
Character string. One of |
term_years |
Insurance term in years. Use |
benefit |
Numeric scalar. Insurance benefit amount. |
premium |
Optional numeric scalar. Premium per annual payment. If
|
premium_term_years |
Optional premium-paying term in years. If
|
durations |
Optional integer vector of policy durations at which to
compute reserves. If |
method |
Character string. Either |
output |
Character string. Use |
Details
The function supports whole-life, term, and endowment insurance. Reserves
may be computed prospectively or recursively. Premiums are assumed payable
annually in advance. Limited-payment policies are supported through
premium_term_years.
The prospective reserve is computed as
{}_kV = APV_k(\text{future benefits}) - P\,APV_k(\text{future premiums}).
The recursive method uses the annual fully discrete recursion
{}_{k+1}V = \frac{({}_kV + P_k)(1+i) - b_{k+1}q_{x+k}}{p_{x+k}}.
Value
If output = "table", a tibble with one row per selected duration.
If output = "value", a named numeric vector of reserves.
See Also
premium_x, insurance_x,
annuity_x, t_px
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_xy(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:70,
lx = c(100000, 99000, 97500, 95500, 93000, 90000,
86000, 81000, 75000, 68000, 60000)
)
reserve_x(
mortality_table = lt,
age = 60,
rate = 0.06,
insurance_type = "whole"
)
reserve_x(
mortality_table = lt,
age = 60,
rate = 0.06,
insurance_type = "endowment",
term_years = 5,
benefit = 100000
)
Benefit reserve schedule for two-life insurance
Description
Computes terminal benefit reserves at selected policy durations for a fully discrete two-life insurance contract, assuming independent future lifetimes.
Usage
reserve_xy(
mortality_table,
age_x = NULL,
age_y = NULL,
rate = NULL,
rate_type = NULL,
m = NULL,
insurance_type = c("whole", "term", "endowment"),
cohort = c("first", "last"),
term_years = Inf,
benefit = 1,
premium = NULL,
premium_term_years = NULL,
payments_per_year = 1L,
premium_timing = c("due", "immediate"),
at = NULL,
method = c("prospective", "recursive"),
output = c("table", "value")
)
Arguments
mortality_table |
Either a single life table used for both lives, or a
list of two life tables |
age_x |
Integer actuarial age for the first life at issue. |
age_y |
Integer actuarial age for the second life at issue. |
rate |
Numeric scalar. Annual interest-rate input. |
rate_type |
Character string indicating the rate type. Allowed values
are |
m |
Positive integer. Compounding frequency for nominal rates. |
insurance_type |
Insurance type. One of |
cohort |
Status definition. Use |
term_years |
Insurance term in years. Required for term and endowment
insurance. For whole life insurance, use |
benefit |
Numeric benefit amount. |
premium |
Net premium per payment. If |
premium_term_years |
Premium-paying term in years. If |
payments_per_year |
Number of premium payments per year. Default is 1. |
premium_timing |
Timing of premium payments. Use |
at |
Integer vector of policy durations at which to compute reserves.
If |
method |
Computation method. Use |
output |
Character string. Use |
Details
The prospective reserve at duration k is computed as:
{}_kV = APV(\text{future benefits}) -
P \cdot APV(\text{future premiums}).
Future benefit values are computed with insurance_xy() at shifted ages
age_x + k and age_y + k. Future premium values are computed with
annuity_xy() on the same two-life status.
The recursive method is implemented for annual due premiums and follows the usual one-year recursion for the selected two-life status.
This function assumes independent future lifetimes.
Value
If output = "table", a tibble with reserve schedule details.
If output = "value", a named numeric vector.
See Also
reserve_x(), premium_xy(), insurance_xy(), annuity_xy(),
t_pxy()
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
simulate_annuity_x(),
simulate_insurance_x()
Examples
lt <- data.frame(
x = 60:70,
lx = c(100000, 99000, 97500, 95500, 93000, 90000,
86000, 81000, 75000, 68000, 60000)
)
reserve_xy(
mortality_table = lt,
age_x = 60,
age_y = 62,
rate = 0.06,
insurance_type = "term",
cohort = "first",
term_years = 4
)
reserve_xy(
mortality_table = lt,
age_x = 60,
age_y = 62,
rate = 0.06,
insurance_type = "endowment",
cohort = "last",
term_years = 5,
benefit = 100000
)
Level annuity accumulation factor s-angle-n
Description
Computes the actuarial accumulation factor for a level annuity.
Usage
s_angle(
n_years,
payments_per_year = 1L,
rate,
rate_type = "effective",
m = 1L,
deferral_years = 0,
timing = "immediate",
payment = 1,
output = c("value", "table")
)
Arguments
n_years |
Numeric vector of payment durations in years. Each value must be positive and finite. |
payments_per_year |
Positive integer vector giving the number of discrete payments per year. Ignored for continuous annuities. |
rate |
Numeric vector of rate values. |
rate_type |
Character vector indicating the rate type.
Allowed values are |
m |
Positive integer vector giving the compounding frequency
for nominal rates. Ignored for |
deferral_years |
Numeric vector of deferral times in years. Must be greater than or equal to 0. Under the adopted horizon convention, this is metadata only for accumulation factors. |
timing |
Character vector. One of |
payment |
Numeric vector of level payment amounts. Used only when
|
output |
Character string. Use |
Details
Supported timing conventions:
-
"immediate": annuity-immediate with discrete payments. -
"due": annuity-due with discrete payments. -
"continuous": continuous annuity.
For discrete annuities, payments_per_year = k means payments are made
every 1/k year. The function returns the accumulation factor, assuming
a unit payment at each payment time.
Horizon convention:
the future value is measured at the time of the last payment. Under this
convention, a pure deferral that shifts the entire payment block forward in
time does not change the accumulation factor when the payment pattern is
otherwise unchanged. Therefore, deferral_years is recorded and
validated, but it does not modify the factor.
The future value of a perpetuity diverges, so perpetuities are not supported
in s_angle().
The function first converts the supplied rate to the equivalent annual
effective interest rate using standardize_interest.
For finite discrete annuities:
s_{\overline{n|}} = \frac{(1+i)^n - 1}{i}
For due annuities:
\ddot{s}_{\overline{n|}} = (1+i)s_{\overline{n|}}
For continuous annuities:
\bar{s}_{\overline{n|}} = \frac{e^{\delta n} - 1}{\delta}
Input vectors must have length 1 or a common length. Missing values are propagated.
Value
If output = "value", a numeric vector of accumulation factors.
If output = "table", a tibble with input values, equivalent rates,
accumulation factors, payment amounts, and future values.
See Also
a_angle, standardize_interest,
future_value
Other annuities:
a_angle(),
annuity_arith(),
annuity_geom()
Examples
# Numeric accumulation factor
s_angle(n_years = 10, rate = 0.05)
# Nominal interest converted monthly, with monthly payments
s_angle(
n_years = 10,
rate = 0.06,
rate_type = "nominal_interest",
m = 12,
payments_per_year = 12
)
# Continuous annuity
s_angle(
n_years = 15,
rate = 0.04,
rate_type = "force",
timing = "continuous"
)
# Tibble output for teaching or auditing
s_angle(
n_years = 10,
rate = 0.05,
payment = 1000,
output = "table"
)
# Vectorized example
s_angle(
n_years = c(5, 10, 20),
payments_per_year = c(1, 12, 1),
rate = c(0.05, 0.06, 0.04),
rate_type = c("effective", "nominal_interest", "force"),
m = c(1, 12, 1),
deferral_years = c(0, 2, 3),
timing = c("immediate", "due", "continuous")
)
Monte Carlo simulation of a life annuity
Description
Simulates the present value of a discrete life annuity using a mortality table and annual curtate future lifetimes.
Usage
simulate_annuity_x(
mortality_table,
age = NULL,
rate = NULL,
rate_type = NULL,
m = NULL,
term_years = Inf,
payments_per_year = 1L,
timing = c("due", "immediate"),
payment = 1,
method = c("inverse"),
n_sim = 10000L,
seed = NULL,
output = c("simulations", "summary")
)
Arguments
mortality_table |
A life table or a |
age |
Integer actuarial age. Optional when |
rate |
Numeric scalar. Annual interest-rate input. Optional when
|
rate_type |
Character string indicating the rate type. |
m |
Positive integer. Compounding frequency for nominal rates. |
term_years |
Term in years. Use |
payments_per_year |
Positive integer. Number of annuity payments per year. |
timing |
|
payment |
Numeric scalar. Level payment per payment period. |
method |
Simulation method. Currently |
n_sim |
Positive integer. Number of simulations. |
seed |
Optional seed for reproducibility. |
output |
|
Details
The function supports direct use with mortality_table, age, and rate,
and pipe workflows with life_contract().
Value
A tibble.
See Also
Other simulation:
simulate_insurance_x(),
summary_mc()
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_insurance_x()
Monte Carlo simulation of a life insurance
Description
Simulates the present value of a discrete life insurance using a mortality table and annual curtate future lifetimes.
Usage
simulate_insurance_x(
mortality_table,
age = NULL,
rate = NULL,
rate_type = NULL,
m = NULL,
insurance_type = c("whole", "term", "endowment"),
term_years = Inf,
benefit = 1,
method = c("inverse"),
n_sim = 10000L,
seed = NULL,
output = c("simulations", "summary")
)
Arguments
mortality_table |
A life table or a |
age |
Integer actuarial age. Optional when |
rate |
Numeric scalar. Annual interest-rate input. Optional when
|
rate_type |
Character string indicating the rate type. |
m |
Positive integer. Compounding frequency for nominal rates. |
insurance_type |
|
term_years |
Term in years. Required for term and endowment insurance.
Use |
benefit |
Numeric scalar. Insurance benefit. |
method |
Simulation method. Currently |
n_sim |
Positive integer. Number of simulations. |
seed |
Optional seed for reproducibility. |
output |
|
Details
The function supports direct use with mortality_table, age, and rate,
and pipe workflows with life_contract().
Value
A tibble.
See Also
Other simulation:
simulate_annuity_x(),
summary_mc()
Other life-contingencies:
annuity_x(),
annuity_xy(),
insurance_x(),
insurance_xy(),
life_contract(),
premium_gross(),
premium_x(),
premium_xy(),
reserve_x(),
reserve_xy(),
simulate_annuity_x()
Simulate future lifetimes from a life table
Description
Simulates curtate and, optionally, complete future lifetimes from a life table containing one-year death probabilities.
Usage
simulate_lifetime(
data,
age,
n_sim = 10000,
age_col = "age",
qx_col = "qx",
method = c("inverse", "multinomial", "antithetic"),
fractional = c("udd", "constant_force", "none"),
seed = NULL,
include_distribution = FALSE
)
Arguments
data |
A data frame or tibble containing the life table. |
age |
Numeric scalar. Initial age |
n_sim |
Positive integer. Number of Monte Carlo simulations.
Default is |
age_col |
Character string. Name of the age column in |
qx_col |
Character string. Name of the one-year death probability
column in |
method |
Character string specifying the simulation method for
|
fractional |
Character string specifying how the fractional part of
the complete future lifetime is generated within the year of death.
Available options are |
seed |
Optional integer seed for reproducibility. Default is |
include_distribution |
Logical. If |
Details
This function is designed as the simulation engine for Monte Carlo life
contingency calculations in tidyactuarial. It generates simulated values
of the curtate future lifetime K_x and a simulated complete future
lifetime T_x. The output is a tidy tibble that can be used naturally
with pipes and downstream functions such as mc_insurance(),
mc_annuity(), mc_premium(), and mc_loss().
For a life aged x, the curtate future lifetime K_x follows
P(K_x = k) = {}_k p_x q_{x+k},
where {}_k p_x is the probability of surviving k complete years
from age x, and q_{x+k} is the one-year probability of death
at attained age x+k.
The function first constructs the conditional distribution of K_x
from the selected age onward. Then it generates simulated values according
to the selected method.
The available simulation methods are:
-
"inverse": inverse transform simulation using the cumulative distribution ofK_x. -
"multinomial": direct sampling from the probability mass function ofK_x. -
"antithetic": inverse transform simulation using antithetic uniformsUand1-U, a basic variance reduction technique.
The fractional argument controls the simulated complete future lifetime
T_x. If fractional = "udd", a uniform fractional lifetime is added
to K_x. If fractional = "constant_force", the fractional lifetime
within the year of death is generated under a constant force of mortality
assumption conditional on death during that year. If fractional = "none",
the complete lifetime is returned as NA.
The probabilities are normalized internally to handle finite life tables. If the life table is truncated, the simulated distribution is conditional on death occurring within the available range of ages. In practical work, it is recommended that the last available death probability be equal to 1.
Value
A tibble with one row per simulation and the following columns:
-
sim_id: simulation identifier. -
age: initial age. -
method: simulation method used. -
fractional: fractional age assumption used forT_x. -
Kx: simulated curtate future lifetime. -
Tx: simulated complete future lifetime. Iffractional = "none", this column containsNA.
If include_distribution = TRUE, the output also includes a list-column
named distribution containing the probability mass function used for the
simulation.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
mc_insurance(), mc_annuity(), mc_premium(), mc_loss()
Examples
life_table <- tibble::tibble(
age = 40:100,
qx = seq(0.002, 1, length.out = 61)
)
# Basic simulation using inverse transform sampling
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
method = "inverse",
seed = 123
)
# Antithetic simulation
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
method = "antithetic",
seed = 123
)
# Returning the distribution used for simulation
life_table |>
simulate_lifetime(
age = 40,
n_sim = 100,
include_distribution = TRUE,
seed = 123
)
# Full pipeline with a life insurance present value
life_table |>
simulate_lifetime(age = 40, n_sim = 1000, seed = 123) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
benefit = 1
)
# Complete future lifetime for benefits payable at the moment of death
life_table |>
simulate_lifetime(
age = 40,
n_sim = 1000,
fractional = "udd",
seed = 123
) |>
mc_insurance(
rate = 0.05,
insurance = "whole_life",
payment_timing = "moment_of_death",
tx_col = "Tx"
)
Simulate multiple independent future lifetimes
Description
Simulates curtate and complete future lifetimes for several lives under an independence assumption.
Usage
simulate_lifetimes(
data,
ages,
n_sim = 10000,
life_id = NULL,
age_col = "age",
qx_col = "qx",
method = c("inverse", "multinomial", "antithetic"),
fractional = c("udd", "constant_force", "none"),
seed = NULL
)
Arguments
data |
A life table data frame or a list of life table data frames.
If a single data frame is supplied, the same life table is used for all
lives. If a list is supplied, it must have length 1 or the same length as
|
ages |
Numeric vector. Initial ages of the lives to simulate. |
n_sim |
Positive integer. Number of Monte Carlo simulations per life.
Default is |
life_id |
Optional character vector identifying each life. If |
age_col |
Character string. Name of the age column in the life table or
life tables. Default is |
qx_col |
Character string. Name of the one-year death probability
column in the life table or life tables. Default is |
method |
Character string specifying the simulation method for each
curtate future lifetime. Available options are |
fractional |
Character string specifying how the fractional part of the
complete future lifetime is generated within the year of death. Available
options are |
seed |
Optional integer seed for reproducibility. Default is |
Details
This function extends simulate_lifetime() to multiple lives. It is designed
as the simulation engine for Monte Carlo multiple-life actuarial calculations.
Each life is simulated independently, and the output is returned in tidy long
format with one row per simulation and per life.
For lives aged x_1, x_2, \ldots, x_r, the function simulates
K_{x_1}, K_{x_2}, \ldots, K_{x_r}
independently. The same applies to the complete future lifetimes
T_{x_1}, T_{x_2}, \ldots, T_{x_r} when fractional is not "none".
The independence assumption means that no common shock, copula, frailty, or other dependence structure is imposed. This is a natural first model for multiple-life Monte Carlo calculations and allows direct construction of joint-life, last-survivor, first-death, last-death, and k-th death quantities through downstream functions.
The output is intentionally long:
sim_id | life_id | life_index | age | Kx | Tx
This format works naturally with dplyr::group_by(),
dplyr::summarise(), tidyr::pivot_wider(), and downstream functions such
as mc_multilife_status().
If different mortality tables are needed for different lives, pass data as
a list of life tables. For example, one table may be used for a male life and
another for a female life.
Value
A tibble with one row per simulation and per life. It contains:
-
sim_id: simulation identifier. -
life_id: life identifier. -
life_index: position of the life inages. -
age: initial age of the life. -
method: simulation method used. -
fractional: fractional age assumption. -
Kx: simulated curtate future lifetime. -
Tx: simulated complete future lifetime. Iffractional = "none", this column containsNA.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second Edition. Society of Actuaries.
See Also
simulate_lifetime(), summary_mc()
Examples
life_table <- tibble::tibble(
age = 40:110,
qx = seq(0.002, 1, length.out = 71)
)
# Two independent lives using the same life table
life_table |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 1000,
seed = 123
)
# Three independent lives
life_table |>
simulate_lifetimes(
ages = c(60, 58, 55),
life_id = c("x", "y", "z"),
n_sim = 1000,
seed = 123
)
# Different life tables for different lives
male_table <- tibble::tibble(
age = 40:110,
qx = seq(0.003, 1, length.out = 71)
)
female_table <- tibble::tibble(
age = 40:110,
qx = seq(0.002, 1, length.out = 71)
)
list(male = male_table, female = female_table) |>
simulate_lifetimes(
ages = c(60, 58),
n_sim = 1000,
seed = 123
)
Sinking fund amortization schedule for a loan
Description
Builds a sinking fund schedule under a fixed loan rate and a fixed accumulation rate for the sinking fund.
Usage
sinking_fund_schedule(
principal,
n,
i_loan,
i_fund,
deposit = NULL,
tol = 1e-08
)
Arguments
principal |
Numeric scalar. Initial loan amount. |
n |
Positive integer. Number of periods. |
i_loan |
Numeric scalar. Effective interest rate per period on the loan. |
i_fund |
Numeric scalar. Effective interest rate per period on the sinking fund. |
deposit |
Optional numeric scalar. Level sinking-fund deposit per
period. If |
tol |
Numeric tolerance used for zero checks and final-balance checks. |
Details
The borrower pays:
interest on the loan each period, and
a level deposit into the sinking fund.
At maturity, the sinking fund is used to redeem the principal.
If deposit is NULL and i_fund is approximately zero,
the deposit is computed as principal / n.
Otherwise, the standard sinking-fund formula is used:
\text{deposit} = \frac{\text{principal}}{s_n}
where
s_n = \frac{(1+i_{\text{fund}})^n - 1}{i_{\text{fund}}}
Value
A tibble with one row per period and columns:
- period
Period index.
- loan_balance_start
Outstanding loan balance at the start of the period.
- interest_loan
Interest paid on the loan during the period.
- sinking_deposit
Deposit made into the sinking fund.
- fund_balance_start
Fund balance at the start of the period.
- interest_fund
Interest earned by the fund during the period.
- fund_balance_end_before_redemption
Fund balance before final redemption.
- redemption_from_fund
Amount withdrawn from the fund to redeem the loan at maturity.
- fund_balance_end
Fund balance after redemption.
- loan_balance_end
Outstanding loan balance after redemption.
- total_cashflow_borrower
Borrower's external cash outflow during the period.
See Also
Other amortization:
amort_schedule()
Examples
sinking_fund_schedule(
principal = 100000,
n = 12,
i_loan = 0.01,
i_fund = 0.008
)
sinking_fund_schedule(
principal = 50000,
n = 10,
i_loan = 0.02,
i_fund = 0,
deposit = NULL
)
SOA Illustrative Life Table
Description
This dataset is intended for reproducible examples, internal validation, and
benchmark tests for life-contingency functions such as annuity_x(),
insurance_x(), premium_x(), reserve_x(), annuity_xy(),
insurance_xy(), premium_xy(), and reserve_xy().
Usage
soa08lt
Format
A data frame with one row per integer age and the following columns:
- x
Integer age.
- lx
Number of lives surviving to exact age
x.- dx
Number of deaths between exact ages
xandx + 1.- qx
One-year probability of death between exact ages
xandx + 1.- px
One-year probability of survival from exact age
xtox + 1.
Details
A tidy version of the Society of Actuaries illustrative life table commonly used in life-contingencies examples and benchmark calculations.
The table is included as a convenient benchmark table for actuarial
calculations. The build script in data-raw/soa08lt.R constructs this tidy
dataset from the SOA illustrative actuarial table object distributed in the
lifecontingencies package.
Source
Society of Actuaries illustrative life table, commonly referenced in Bowers et al. (1997), Actuarial Mathematics, Appendix 2A.
References
Bowers, N. L., Gerber, H. U., Hickman, J. C., Jones, D. A., and Nesbitt, C. J. (1997). Actuarial Mathematics. Second edition. Society of Actuaries.
Spedicato, G. A. (2013). The lifecontingencies package: performing
financial and actuarial mathematics calculations in R.
Examples
data(soa08lt)
head(soa08lt)
annuity_x(
mortality_table = soa08lt,
age = 65,
rate = 0.06,
timing = "due"
)
Standardize an interest rate to the annual effective rate i
Description
Converts common interest-rate specifications to the equivalent
annual effective interest rate i.
Usage
standardize_interest(type = "effective", rate, m = 1)
Arguments
type |
Character vector indicating the rate type.
Must be one of |
rate |
Numeric vector of rate values. |
m |
Positive integer vector giving the compounding frequency
for nominal rates. Ignored for |
Details
The conversion formulas are:
- effective:
i = \text{rate}(identity)- nominal_interest:
i = (1 + j^{(m)}/m)^m - 1- nominal_discount:
i = (1 - d^{(m)}/m)^{-m} - 1- force:
i = e^{\delta} - 1
Input vectors must have length 1 or a common length.
Value
Numeric vector of annual effective rates. Missing values are propagated.
See Also
interest_equivalents,
discount_factor_spot
Other interest:
discount_factor_spot(),
forward_rate(),
interest_equivalents(),
yield_curve()
Examples
# Scalar cases
standardize_interest("nominal_interest", rate = 0.18, m = 4)
standardize_interest("nominal_discount", rate = 0.10, m = 12)
standardize_interest("force", rate = 0.12)
# Vectorized case
standardize_interest(
type = c("nominal_interest", "force", "effective"),
rate = c(0.06, 0.05, 0.04),
m = c(12, 1, 1)
)
# Use inside a data pipeline
if (requireNamespace("dplyr", quietly = TRUE) &&
requireNamespace("tibble", quietly = TRUE)) {
portfolio <- tibble::tibble(
policy_id = 1:3,
gross_rate = c(0.05, 0.08, 0.10),
rate_type = c("force", "nominal_interest", "nominal_discount"),
frequency = c(NA, 4, 12)
)
dplyr::mutate(
portfolio,
effective_rate = standardize_interest(
type = rate_type,
rate = gross_rate,
m = frequency
)
)
}
Summarise Monte Carlo simulation output
Description
Computes tidy summary statistics from simulated actuarial values.
Usage
summary_mc(
.data,
value_col = "present_value",
group_cols = NULL,
by = NULL,
probs = c(0.025, 0.5, 0.975),
var_probs = c(0.95, 0.99),
na_rm = TRUE
)
Arguments
.data |
A data.frame or tibble containing simulation output. |
value_col |
Character string. Name of the numeric column to summarise.
Defaults to |
group_cols |
Optional character vector of grouping columns. |
by |
Optional character vector of grouping columns. This is a convenient
alias for |
probs |
Numeric vector of probabilities for quantiles. |
var_probs |
Numeric vector of probabilities for VaR and TVaR. |
na_rm |
Logical. If |
Details
This function is intentionally generic. It can summarise simulated present values from annuities, insurances, premiums, reserves, losses, or any other numeric actuarial indicator stored in a tidy simulation table.
The function computes the number of simulations, mean, variance, standard deviation, standard error, minimum, maximum, selected quantiles, VaR, and TVaR.
TVaR is computed empirically as the mean of simulated values greater than or equal to the corresponding empirical VaR.
Value
A tibble with summary statistics.
See Also
Other simulation:
simulate_annuity_x(),
simulate_insurance_x()
Examples
sim <- tibble::tibble(
simulation_id = 1:5,
duration = c(0, 0, 1, 1, 1),
present_value = c(10, 12, 8, 15, 11)
)
summary_mc(sim)
summary_mc(sim, by = "duration")
Pure endowment (discounted survival): {}_tE_x
Description
Computes the actuarial present value of a pure endowment, i.e., the
expected present value of a payment of 1 made at time t if and only
if a life aged x survives to age x + t:
{}_tE_x = v^t \cdot {}_tp_x = (1 + i)^{-t} \cdot
\frac{\ell_{x+t}}{\ell_x}.
Usage
t_Ex(lt, x, t, i, frac, tidy = FALSE, check = TRUE, tol = 1e-10)
Arguments
lt |
A lifetable object as produced by |
x |
Integer age(s) at which the endowment starts. |
t |
Nonnegative numeric duration(s) in years (can be fractional). |
i |
Annual effective interest rate(s). Must satisfy |
frac |
Fractional-age assumption passed to |
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks on |
Details
The pure endowment is a fundamental building block in life contingency mathematics (Finan, Section 26.3.1). It serves as the actuarial discount factor, combining financial discounting with mortality:
{}_nE_x = v^n \cdot {}_np_x.
Key identities involving {}_nE_x:
Actuarial accumulated value:
\ddot{s}_{x:\overline{n}|} = \ddot{a}_{x:\overline{n}|} / {}_nE_x(Finan, Sec. 34).Endowment insurance decomposition:
A_{x:\overline{n}|} = A^1_{x:\overline{n}|} + {}_nE_x(Finan, Sec. 26.3.2).Deferred annuity:
{}_{n|}\ddot{a}_x = {}_nE_x \cdot \ddot{a}_{x+n}(Finan, Sec. 35).
For a constant force of mortality \mu and force of interest
\delta:
{}_nE_x = e^{-n(\mu + \delta)}
(Finan, Example 26.14).
The variance of the pure endowment random variable is (Finan, Sec. 26.3.1):
\mathrm{Var}(\bar{Z}^{\phantom{1}}_{\phantom{1}x:\overline{n}|})
= v^{2n} \cdot {}_np_x \cdot {}_nq_x.
Value
Numeric vector of {}_tE_x, or a tibble if
tidy = TRUE.
See Also
t_px for survival probabilities (without
discounting), insurance_x for term, whole life, and
endowment insurance APVs, annuity_x for life annuity
APVs that use {}_nE_x internally.
Examples
x <- 0:5
lx <- c(100000, 99500, 99000, 98200, 97000, 95000)
lt <- lifetable(x = x, lx = lx, omega = 5, close = TRUE)
# Basic pure endowment: 3_E_0 = v^3 * 3_p_0
t_Ex(lt, x = 0, t = 3, i = 0.06)
# Verify manually:
(1.06)^(-3) * t_px(lt, x = 0, t = 3)
# Finan Example 26.14 style: constant force mu, delta
# For exponential survival with mu = 0.05, delta = 0.10:
# 10_E_30 = exp(-10*(0.05 + 0.10)) = exp(-1.5)
lt_exp <- lifetable(x = 0:50, lx = 100000 * exp(-0.05 * (0:50)))
t_Ex(lt_exp, x = 30, t = 10, i = exp(0.10) - 1)
exp(-1.5) # theoretical value
# Finan Problem 26.16: 5-year pure endowment for (30), i = 6%
lt_ilt <- lifetable(
x = 30:35,
lx = c(9501381, 9486854, 9471591, 9455522, 9438571, 9420657)
)
t_Ex(lt_ilt, x = 30, t = 5, i = 0.06)
# = v^5 * l_35 / l_30 = (1.06)^(-5) * 9420657/9501381
# Vectorized: multiple ages at once
t_Ex(lt, x = c(0, 1, 2), t = 3, i = 0.05, tidy = TRUE)
# Use in a tidy pipeline
if (requireNamespace("dplyr", quietly = TRUE)) {
tibble::tibble(age = 0:4, term = c(5, 4, 3, 2, 1)) |>
dplyr::mutate(pure_endow = t_Ex(lt, x = age, t = term, i = 0.06))
}
t-year survival probability from a life table
Description
Computes the t-year survival probability
{}_t p_x = P[T(x) > t]
using an annual life table, allowing for fractional ages under standard actuarial assumptions.
Usage
t_px(lt, x, t, frac, tidy = FALSE, check = TRUE, tol = 1e-10)
Arguments
lt |
A lifetable object as produced by |
x |
Integer age(s) at which survival starts. |
t |
Nonnegative numeric duration(s) in years (can be fractional). |
frac |
Fractional-age assumption:
|
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks on |
Details
The integer-year survival is obtained directly from the life table (Finan, Section 22):
{}_n p_x = \frac{\ell_{x+n}}{\ell_x}
For non-integer durations, let t = n + s with n = \lfloor t \rfloor
and s \in [0,1). Then (Finan, Section 24):
{}_t p_x = {}_n p_x \times {}_s p_{x+n}
The fractional-year factor {}_s p_y depends on the assumption:
UDD (Finan, Sec. 24.1):
{}_s p_y = 1 - s \times q_yCF (Finan, Sec. 24.2):
{}_s p_y = (p_y)^sBalducci (Finan, Sec. 24.3):
{}_s p_y = \frac{p_y}{1 - (1 - s) \times q_y}
If x + t > \omega (the terminal age), the function returns 0
since no survival is possible beyond the table's limiting age.
Value
Numeric vector of {}_t p_x, or a tibble if
tidy = TRUE.
Two-life survival probability for independent lives
Description
Computes the survival probability for two independent lives with actuarial
ages x and y at time 0 under a joint-life or last-survivor
status.
Usage
t_pxy(lt, x, y, t, frac, status = c("joint", "last"))
Arguments
lt |
Either:
Each life table must contain columns |
x |
Integer actuarial age of the first life at time 0. |
y |
Integer actuarial age of the second life at time 0. |
t |
Nonnegative time (may be fractional). |
frac |
Fractional-age assumption: |
status |
Two-life status: |
Details
Independence is assumed throughout (Finan, Sections 56–57).
Joint-life status (Finan, Section 56): the status survives as long as both lives are alive.
{}_tp_{xy} = {}_tp_x \cdot {}_tp_y.
Last-survivor status (Finan, Section 57): the status survives as long as at least one life is alive.
{}_tp_{\overline{xy}} = {}_tp_x + {}_tp_y - {}_tp_x \cdot {}_tp_y.
Individual survival probabilities {}_tp_x and {}_tp_y are
computed via t_px, which supports fractional ages under UDD,
constant force, and Balducci assumptions (Finan, Section 24).
Value
A single numeric value.
See Also
t_px for single-life survival,
e_xy for joint-life expectancy,
annuity_xy for two-life annuity APVs,
insurance_xy for two-life insurance APVs.
Examples
lt <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)
# Joint life, 2.5 years, UDD (Finan, Sec. 56)
t_pxy(lt, x = 60, y = 62, t = 2.5, frac = "UDD", status = "joint")
# Verify: joint = product of marginals
t_px(lt, x = 60, t = 2.5, frac = "UDD") *
t_px(lt, x = 62, t = 2.5, frac = "UDD")
# Last survivor, 2.5 years, constant force (Finan, Sec. 57)
t_pxy(lt, x = 60, y = 62, t = 2.5, frac = "CF", status = "last")
# Same result if the same table is supplied explicitly for both lives
t_pxy(list(lt, lt), x = 60, y = 62, t = 2.5, frac = "UDD", status = "joint")
# Different life tables for the two lives
lt_m <- data.frame(
x = 60:66,
lx = c(100000, 98500, 96800, 94800, 92400, 89500, 86000)
)
lt_f <- data.frame(
x = 60:66,
lx = c(100000, 99000, 97800, 96400, 94700, 92700, 90300)
)
t_pxy(list(lt_m, lt_f), x = 60, y = 62, t = 2.5, frac = "UDD", status = "joint")
# Finan Example 56.2 style: integer survival
# 10_p_{50:60} = 10_p_50 * 10_p_60
lt_ilt <- data.frame(
x = 50:70,
lx = c(8950901, 8879913, 8804189, 8723382, 8637048,
8544731, 8445974, 8340310, 8227261, 8106334,
7977338, 7839775, 7693040, 7536522, 7369603,
7191658, 7002051, 6800139, 6585264, 6356752,
6114913)
)
t_pxy(lt_ilt, x = 50, y = 60, t = 10, status = "joint")
# Finan Problem 56.1: t_q_xy = t_q_x + t_q_y - t_q_x * t_q_y
p_joint <- t_pxy(lt, x = 60, y = 62, t = 3, status = "joint")
q_joint <- 1 - p_joint
qx <- 1 - t_px(lt, x = 60, t = 3)
qy <- 1 - t_px(lt, x = 62, t = 3)
c(q_joint = q_joint, q_sum = qx + qy - qx * qy) # should match
t-year death probability from a life table
Description
Computes the t-year death probability
{}_t q_x = \Pr[T(x) \le t] = 1 - {}_t p_x
using an annual life table, allowing for fractional ages under standard actuarial assumptions.
Usage
t_qx(lt, x, t, frac, tidy = FALSE, check = TRUE, tol = 1e-10)
Arguments
lt |
A lifetable object as produced by |
x |
Integer age(s) at which the interval starts. |
t |
Nonnegative numeric duration(s) in years (can be fractional). |
frac |
Fractional-age assumption:
|
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks on |
Details
This is a thin wrapper around t_px:
{}_t q_x = 1 - {}_t p_x.
The identity {}_tq_x = {}_td_x / \ell_x (Finan, Section 22) holds
for integer t, where {}_td_x = \ell_x - \ell_{x+t} is the
expected number of deaths between ages x and x+t.
For fractional durations, the result depends on the chosen assumption
(UDD, CF, or Balducci); see t_px for details and formulas
(Finan, Section 24).
The deferred death probability {}_{n|}q_x can be obtained as
(Finan, Section 23.4):
{}_{n|}q_x = {}_np_x \cdot q_{x+n} = {}_{n+1}q_x - {}_nq_x.
Value
Numeric vector of {}_t q_x, or a tibble if
tidy = TRUE.
See Also
t_px for the complementary survival probability,
t_Ex for the pure endowment (discounted survival),
e_x for life expectancy,
lifetable for building the life table input.
Examples
x <- 0:5
lx <- c(100000, 99500, 99000, 98200, 97000, 95000)
lt <- lifetable(x = x, lx = lx, omega = 5, close = TRUE)
# Integer death probability (Finan, Section 22)
t_qx(lt, x = 0, t = 3) # (l0 - l3) / l0
# t = 0 always returns 0
t_qx(lt, x = 0, t = 0)
# Fractional age under UDD (Finan, Section 24.1)
t_qx(lt, x = 0, t = 2.5, frac = "UDD")
# Finan Example 22.2a: number of deaths between ages 2 and 5
# 3_d_2 = l_2 - l_5 = 98995 - 97468 = 1527
lt_22 <- lifetable(
x = 0:5,
lx = c(100000, 99499, 98995, 98489, 97980, 97468)
)
t_qx(lt_22, x = 2, t = 3) * lt_22$lx[lt_22$x == 2] # 1527
# Deferred death probability (Finan, Section 23.4):
# 2|1_q_0 = 2_p_0 * q_2 = 3_q_0 - 2_q_0
t_qx(lt, x = 0, t = 3) - t_qx(lt, x = 0, t = 2)
# Vectorized with tidy output
t_qx(lt, x = c(0, 1), t = c(1.5, 2.2), frac = "Balducci", tidy = TRUE)
# Use in a tidy pipeline
if (requireNamespace("dplyr", quietly = TRUE)) {
tibble::tibble(age = c(0, 1, 2), duration = c(3, 2.5, 1.7)) |>
dplyr::mutate(
surv = t_px(lt, x = age, t = duration),
death = t_qx(lt, x = age, t = duration)
)
}
t-year probability of decrement by cause j: t_qxj
Description
Computes {}_t q_x^{(j)}, the probability that a life aged x
decrements by a specific cause j within t years, using a multiple
decrement table produced by md_table.
Usage
t_qxj(md, x, t, cause, frac = NULL, tidy = FALSE, check = TRUE, tol = 1e-10)
Arguments
md |
A multiple decrement table produced by |
x |
Numeric vector. Starting age(s) (integer-valued). |
t |
Numeric vector. Time horizon(s) in years (t >= 0). Can be non-integer
if |
cause |
Character. Name of the cause column in |
frac |
Character. Fractional-age assumption for non-integer |
tidy |
Logical. If |
check |
Logical. If |
tol |
Numeric tolerance for integer checks (default |
Details
Let q_x^{(j)} be the annual decrement probability for cause j and
q_x^{(\tau)} be the total decrement probability. For integer t,
{}_t q_x^{(j)} = \sum_{k=0}^{t-1} \left(\prod_{r=0}^{k-1} p_{x+r}^{(\tau)}\right) q_{x+k}^{(j)}.
For non-integer t = n + s with n = \lfloor t \rfloor and
s \in [0,1), this function supports fractional-age assumptions specified
by frac and uses the additional convention that the within-year cause
proportions remain constant:
{}_s q_x^{(j)} = w_j \, {}_s q_x^{(\tau)} where
w_j = q_x^{(j)} / q_x^{(\tau)} (and 0 when q_x^{(\tau)}=0).
Supported fractional-age assumptions for the total decrement:
-
"UDD":{}_s q_x^{(\tau)} = s q_x^{(\tau)}. -
"CF":{}_s p_x^{(\tau)} = (p_x^{(\tau)})^s. -
"Balducci":{}_s q_x^{(\tau)} = \frac{s q_x^{(\tau)}}{1-(1-s)q_x^{(\tau)}}.
Value
Numeric vector of {}_t q_x^{(j)} (or tibble if tidy=TRUE).
Examples
qx_df <- tibble::tibble(
x = 30:35,
q_death = c(0.001, 0.0012, 0.0014, 0.0017, 0.0020, 1.0000),
q_disability = c(0.002, 0.0021, 0.0022, 0.0023, 0.0024, 0.0000)
)
md <- md_table(qx_df, radix = 1e5, close = TRUE)
t_qxj(md, x = 30, t = 5, cause = "q_death")
t_qxj(md, x = 30, t = 2.5, cause = "q_death", frac = "CF", tidy = TRUE)
Validate a yield curve, compute discount factors, and optionally create a plot
Description
Builds a tibble-first representation of a discrete yield curve using annual effective spot rates and computes the corresponding discount factors.
Usage
yield_curve(
.data = NULL,
term = NULL,
spot = NULL,
col_term = "term",
col_spot = "spot",
plot = FALSE,
.out = "discount",
.out_plot = "yield_curve_plot",
.keep = c("all", "used", "none"),
.na = c("propagate", "error", "drop")
)
Arguments
.data |
A data.frame or tibble. If |
term |
Numeric vector of maturities when |
spot |
Numeric vector of annual effective spot rates when
|
col_term |
Name of the list-column containing maturities. |
col_spot |
Name of the list-column containing spot rates. |
plot |
Logical; if |
.out |
Name of the output list-column containing discount factors. |
.out_plot |
Name of the output list-column containing |
.keep |
One of |
.na |
NA handling policy: |
Details
Each row is treated as one curve (one case). For tibble input,
col_term and col_spot must be list-columns of equal-length
numeric vectors. When .data = NULL, term and spot
must be numeric vectors and a one-row tibble is returned.
The discount factors are computed as:
v_t = (1 + i_t)^{-t}
where i_t is the annual effective spot rate for maturity t.
If plot = TRUE, the function also returns a list-column of
ggplot2 objects showing the spot yield curve for each row.
Value
A tibble. By default it returns the original columns plus a new
list-column named by .out containing discount-factor vectors.
If plot = TRUE, it also adds a list-column named by .out_plot
containing ggplot2 objects.
References
Marcel B. Finan, A Basic Course in the Theory of Interest and Derivatives Markets: A Preparation for the Actuarial Exam FM/2, Section 53: The Term Structure of Interest Rates and Yield Curves.
Kellison, S. G. The Theory of Interest.
See Also
forward_rate,
discount_factor_spot, standardize_interest
Other interest:
discount_factor_spot(),
forward_rate(),
interest_equivalents(),
standardize_interest()
Examples
# Simple example
res <- yield_curve(
term = c(1, 2, 3, 4, 5),
spot = c(0.040, 0.045, 0.048, 0.050, 0.051),
plot = TRUE
)
res$yield_curve_plot[[1]]
# Medium example
curves <- tibble::tibble(
curve_id = c("A", "B"),
term = list(c(1, 2, 3), c(1, 3, 5)),
spot = list(c(0.04, 0.05, 0.06), c(0.03, 0.035, 0.04))
)
res2 <- yield_curve(
curves,
col_term = "term",
col_spot = "spot",
plot = TRUE,
.out = "v",
.out_plot = "curve_plot"
)
res2$curve_plot[[2]]