Skip to contents

Computes Macaulay duration and modified-duration measures for a level coupon bond valued under a flat yield-to-maturity assumption, using compact actuarial notation.

Usage

bond_duration(
  face,
  c,
  n,
  k = 1L,
  y_effective_per_period = NULL,
  y = NULL,
  y_type = "effective",
  y_m = 1L,
  R = NULL,
  tol = 1e-10,
  check = TRUE
)

Arguments

face

Numeric scalar. Face value of the bond.

c

Numeric scalar. Annual coupon rate as a proportion.

n

Numeric scalar. Time to maturity in years.

k

Positive integer. Number of coupon payments per year.

y_effective_per_period

Optional numeric scalar. Effective yield per coupon period.

y

Optional numeric scalar. Annual yield rate value.

y_type

Character string indicating the annual yield type: "effective", "nominal_interest", "nominal_discount", or "force".

y_m

Positive integer. Conversion frequency for nominal annual yields.

R

Numeric scalar. Redemption value at maturity. If NULL, defaults to face.

tol

Finite positive numeric scalar. Tolerance used to check maturity alignment with integer coupon periods, not a monetary price threshold.

check

Logical scalar. If TRUE, performs input validation.

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.

k

Coupon frequency.

n_periods

Total number of coupon periods.

Details

Assumptions:

  • Coupons are paid in arrears at regular intervals.

  • n * k must be an integer.

  • Stub periods are not supported.

  • Valuation is at a coupon date with no accrued interest.

  • A single flat yield is used to discount all cash flows.

Yield input conventions:

  • If y_effective_per_period is supplied, it is interpreted as the effective yield per coupon period.

  • Otherwise, y, y_type, and y_m define an annual yield specification. Calculations retain its original log growth; reported effective equivalents remain subject to numeric range.

This function follows the compact bond notation used in tidyactuarial: face is the face value, c is the annual coupon rate, n is the time to maturity, k is the coupon frequency, y is the annual yield input, and R is the redemption value.

Let \(j\) be the effective yield per coupon period, \(k\) the number of coupon payments per year, and let cash flows \(C_r\) occur at coupon periods \(r = 1, \dots, N\). With \(v = 1/(1+j)\) and \(P = \sum_r C_r v^r\):

Macaulay duration in coupon periods: $$D_p = \frac{\sum_{r=1}^{N} r C_r v^r}{P}.$$

Macaulay duration in years is \(D = D_p / k\).

Modified duration with respect to \(j\), in coupon periods, is \(D^*_j = D_p / (1 + j)\).

Modified duration with respect to the annual effective rate \(i\), in years, is \(D^*_i = D / (1 + i)\), where \(i = (1+j)^k - 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. The two coefficients must use the same yield coordinate: pair modified_duration_periods_j with the period convexity for changes in \(j\). Annual effective duration requires annual effective convexity, obtained using the chain rule or convexity_cash_flow() on year times. Prices use original log yields and moments use relative present values. Nonzero-term bonds require a positive finite price and finite risk measures; calendar tolerance does not define a monetary zero. Reported equivalent yields may reach -1 or Inf by rounding while the specification remains usable. At zero term, immediate redemption and zero risk follow the existing convention.

Examples

bond_duration(
  face = 100,
  c = 0.08,
  n = 5,
  k = 2,
  y = 0.06,
  y_type = "effective"
)
#> # A tibble: 1 × 9
#>   price macaulay_duration_periods macaulay_duration_years modified_duration_pe…¹
#>   <dbl>                     <dbl>                   <dbl>                  <dbl>
#> 1  109.                      8.51                    4.26                   8.27
#> # ℹ abbreviated name: ¹​modified_duration_periods_j
#> # ℹ 5 more variables: modified_duration_years_i <dbl>, yield_per_period <dbl>,
#> #   yield_effective_annual <dbl>, k <int>, n_periods <int>

bond_duration(
  face = 1000,
  c = 0.05,
  n = 10,
  k = 2,
  y_effective_per_period = 0.03
)
#> # A tibble: 1 × 9
#>   price macaulay_duration_periods macaulay_duration_years modified_duration_pe…¹
#>   <dbl>                     <dbl>                   <dbl>                  <dbl>
#> 1  926.                      15.8                    7.89                   15.3
#> # ℹ abbreviated name: ¹​modified_duration_periods_j
#> # ℹ 5 more variables: modified_duration_years_i <dbl>, yield_per_period <dbl>,
#> #   yield_effective_annual <dbl>, k <int>, n_periods <int>