Skip to contents

Computes the annual effective forward rates for every consecutive interval implied by an observed spot yield curve. Unlike forward_rate(), which evaluates a selected interval, forward_curve() returns the complete adjacent-interval schedule in a tidy, long-format tibble.

Usage

forward_curve(
  .data = NULL,
  t = NULL,
  i = NULL,
  col_t = "t",
  col_i = "i",
  col_curve = NULL,
  i_type = "effective",
  m = 1L,
  include_origin = TRUE
)

Arguments

.data

Optional data frame or tibble containing spot maturities and rates, in numeric columns or corresponding list-columns.

t

Numeric vector of maturities in years if .data = NULL.

i

Numeric vector of spot rates if .data = NULL.

col_t

Name of the maturity column in .data.

col_i

Name of the spot-rate column in .data.

col_curve

Optional name of a curve-identifier column in .data. Required to distinguish multiple curves stored as numeric node rows; optional for list-column input.

i_type

Spot-rate convention: "effective", "nominal_interest", "nominal_discount" or "force". Scalar for multiple curves, or scalar/nodewise for a single curve.

m

Conversion frequency for nominal rates. A positive integer; scalar for multiple curves, or scalar/nodewise for one curve.

include_origin

Logical scalar. If TRUE (default), include the initial interval from time zero when the first observed node is positive.

Value

A tibble, with one row per forward interval and columns curve, t_start, t_end, duration, v_start, v_end, and f. f is an annual effective forward rate and duration is measured in years.

Details

Let \(0=t_0<t_1<\cdots<t_n\) denote maturities, and let \(v(t_j)\) be their spot discount factors. For each consecutive interval, $$(1+f_{t_{j-1},t_j})^{t_j-t_{j-1}}= \frac{v(t_{j-1})}{v(t_j)},$$ so the annual effective forward rate is $$f_{t_{j-1},t_j}=\left( \frac{v(t_{j-1})}{v(t_j)}\right)^{1/(t_j-t_{j-1})}-1.$$ The function evaluates log discount-factor ratios from original log spot growth rates, retaining small rates even when reported discounts round to 1. Equal spot log rates are preserved across intervals with a large common origin. Discount factors are obtained from the existing discount_factor_spot() function, which standardizes the chosen interest-rate convention. Negative spot and forward rates are supported when the discount factors are positive and finite.

With include_origin = TRUE, the first interval starts at time zero and uses the exact identity \(v(0)=1\). A supplied zero-maturity node is retained without duplication. With include_origin = FALSE, only intervals between supplied nodes are returned.

Maturities are sorted within each curve. Repeated, nonfinite or negative maturities, missing or nonfinite rates, and invalid discount factors are rejected. No interpolation, extrapolation, curve fitting or forward-rate forecast is performed. Intervals may have unequal lengths. Spot-to-forward relationships represent no-arbitrage implications of the supplied discount factors, not predictions of realized future rates.

Accepted input shapes are: direct numeric t and i vectors; a data frame with numeric t and i columns (one node per row, optionally partitioned using col_curve); or a data frame with t and i list-columns (one complete curve per row), such as yield_curve() output. Each curve produces one or more rows. curve is a character identifier: an ID from col_curve, or the input curve's sequential number if no ID is supplied. If more than one curve is supplied, i_type and m must be scalars; for a single curve they can instead have one value per node.

Examples

# A forward curve for consecutive annual spot maturities.
forward_curve(t = 1:3, i = c(0.04, 0.045, 0.05))
#> # A tibble: 3 × 7
#>   curve t_start t_end duration v_start v_end      f
#>   <chr>   <dbl> <dbl>    <dbl>   <dbl> <dbl>  <dbl>
#> 1 1           0     1        1   1     0.962 0.04  
#> 2 1           1     2        1   0.962 0.916 0.0500
#> 3 1           2     3        1   0.916 0.864 0.0601

# The output of yield_curve() can be used directly in a pipe.
yield_curve(t = c(1, 2, 3), i = c(0.04, 0.045, 0.05)) |>
  forward_curve()
#> # A tibble: 3 × 7
#>   curve t_start t_end duration v_start v_end      f
#>   <chr>   <dbl> <dbl>    <dbl>   <dbl> <dbl>  <dbl>
#> 1 1           0     1        1   1     0.962 0.04  
#> 2 1           1     2        1   0.962 0.916 0.0500
#> 3 1           2     3        1   0.916 0.864 0.0601

# Multiple curves, using tidyverse-friendly list-columns.
tibble::tibble(
  scenario = c("base", "stress"),
  t = list(c(1, 2, 3), c(1, 3, 5)),
  i = list(c(0.04, 0.045, 0.05), c(0.05, 0.055, 0.06))
) |>
  forward_curve(col_curve = "scenario")
#> # A tibble: 6 × 7
#>   curve  t_start t_end duration v_start v_end      f
#>   <chr>    <dbl> <dbl>    <dbl>   <dbl> <dbl>  <dbl>
#> 1 base         0     1        1   1     0.962 0.04  
#> 2 base         1     2        1   0.962 0.916 0.0500
#> 3 base         2     3        1   0.916 0.864 0.0601
#> 4 stress       0     1        1   1     0.952 0.05  
#> 5 stress       1     3        2   0.952 0.852 0.0575
#> 6 stress       3     5        2   0.852 0.747 0.0675