Skip to contents

Computes the actuarial present value of a discrete annuity contingent on two independent lives, using compact actuarial notation.

Usage

annuity_xy(
  lt,
  x = NULL,
  y = NULL,
  i = NULL,
  i_type = "effective",
  m = 1L,
  status = c("joint", "last"),
  benefit = NULL,
  n = Inf,
  h = 0L,
  k = 1L,
  timing = c("immediate", "due"),
  woolhouse = c("none", "first", "second"),
  frac,
  tidy = FALSE,
  ...
)

Arguments

lt

A life table, a list of two life tables list(lt_x, lt_y), or a tidyact_life_contract object created by life_contract. Each life table must contain columns x and lx.

x

Integer actuarial age for the first life.

y

Integer actuarial age for the second life.

i

Numeric scalar. Annual interest-rate input.

i_type

Character string indicating the interest-rate type. Allowed values are "effective", "nominal_interest", "nominal_discount", and "force".

m

Positive integer. Conversion frequency for nominal rates. Ignored for i_type = "effective" and i_type = "force".

status

Character string. Use "joint" for joint-life payments while both lives are alive, or "last" for last-survivor payments while at least one life is alive. Ignored when benefit is supplied.

benefit

Optional list with numeric scalar weights both, x_only, and y_only. These weights define the payment rate according to the survival state of the two lives. Weights may be signed; an expected payment is then a benefit weight, not necessarily a probability.

n

Term in years. Use Inf for the maximum horizon allowed by the life tables. For exact k-thly valuation (woolhouse = "none"), fractional terms are allowed when n * k is an integer. Woolhouse approximations require an integer number of years.

h

Nonnegative integer deferment period in years.

k

Positive integer. Number of payments per year. The annuity is normalized to an annual payment rate of 1, so each individual payment has amount 1 / k.

timing

Payment timing. Use "immediate" or "due".

woolhouse

Woolhouse approximation for k > 1: "none", "first", or "second".

frac

Fractional-age assumption for exact k-thly computation: "UDD", "CF", "CML", or "Balducci".

tidy

Logical scalar. If FALSE, returns a numeric APV. If TRUE, returns a one-row tibble.

...

Transitional compatibility for older calls using mortality_table, age_x, age_y, rate, rate_type, cohort, term_years, deferment_years, payments_per_year, and output.

Value

If tidy = FALSE, a numeric scalar.

If tidy = TRUE, a one-row tibble with input values, standardized interest rate, term used, and APV.

Details

The function supports joint-life, last-survivor, and state-based reversionary-style payments. The life table input may be either one common table for both lives or a list of two life tables, one for each life.

This function follows the compact actuarial notation used throughout tidyactuarial: lt is the life table input, x and y are the two actuarial ages, i is the interest-rate input, i_type is the interest-rate type, m is the conversion frequency for nominal rates, n is the term, h is the deferment period, and k is the payment frequency.

The function assumes independent future lifetimes. For state-based benefits, the expected payment at time \(t\) is $$ b_{\text{both}}\,{}_tp_x{}_tp_y + b_{x\text{ only}}\,{}_tp_x(1-{}_tp_y) + b_{y\text{ only}}\,{}_tp_y(1-{}_tp_x). $$

When benefit = NULL, status = "joint" uses benefit = list(both = 1, x_only = 0, y_only = 0), while status = "last" uses benefit = list(both = 1, x_only = 1, y_only = 1).

For k-thly payments, the function values a payment rate of 1 per year, so each payment has size \(1/k\). For exact valuation, a fractional term is admissible whenever it contains a whole number of payments: $$nk \in \mathbb{N}.$$

Both lives are alive at issue; both, x_only and y_only refer to occupation at each payment date, without restarting both lives after a death. A designated reversion to y uses both=0, x_only=0, y_only=1; this differs from payments to either sole survivor and from a joint-and-survivor annuity paying while both are alive as well.

Exact valuation sums signed state payments on dates h+j/k, with each payment equal to its state rate divided by k. Cumulative death probabilities use direct survivor-count decrements and fractional interval masses, avoiding cancellation in 1-S. Original log interest, probabilities and monetary weights are combined before exponentiating when necessary.

Woolhouse approximations apply only to standard joint-life/last-survivor statuses. Put F(t)=exp(-g*t)w(t), where w is the status survival from issue and g is the input rate's annual log accumulation. First order subtracts (1-1/k)(F(h)-F(h+n))/2 from the annual due annuity. Second order also subtracts (1-1/k^2)((g+eta(h))F(h)-(g+eta(h+n))F(h+n))/12, with eta(t)=-log(w(t+1)/w(t)), a forward annual estimate, not an exact instantaneous force. Second order requires positive supported weights and the following year at both borders; otherwise use none or first. Immediate subtracts (F(h)-F(h+n))/k from the due approximation. A negative approximation is reported with a warning, without clipping.

Tables must satisfy the annual consistency requirements of t_pxy; issue ages must have positive lx for nonempty valuations. Payment grids must be finite, representable and no larger than R's integer range. One call values one contract; benefit vectors describe states, not scenarios. The existing table-horizon convention is retained: each marginal survival becomes zero beyond its last recorded age. On open tables this is truncation, not evidence of death, and may affect last-survivor or reversionary weights after the shorter horizon. Extend the tables for those interpretations. Infinite n means available horizon, not extrapolated lifetime coverage.

Examples

lt <- data.frame(
  x  = 60:66,
  lx = c(100000, 99000, 97500, 95500, 93000, 90000, 86000)
)

# Joint-life annuity-due
annuity_xy(
  lt = lt,
  x = 60,
  y = 62,
  i = 0.05,
  status = "joint",
  timing = "due"
)
#> [1] 3.52856

# Last-survivor annuity-due
annuity_xy(
  lt = lt,
  x = 60,
  y = 62,
  i = 0.05,
  status = "last",
  timing = "due"
)
#> [1] 5.240099

# Different life tables for the two lives
lt_m <- lt
lt_f <- data.frame(
  x  = 60:66,
  lx = c(100000, 99200, 98100, 96500, 94500, 92000, 89000)
)

annuity_xy(
  lt = list(lt_m, lt_f),
  x = 60,
  y = 62,
  i = 0.05,
  status = "joint",
  timing = "due"
)
#> [1] 3.553047

# State-based reversionary-style payments
annuity_xy(
  lt = list(lt_m, lt_f),
  x = 60,
  y = 62,
  i = 0.05,
  benefit = list(both = 0, x_only = 1, y_only = 0),
  timing = "due"
)
#> [1] 0.8752761