Nonlevel Coupons

TmVal can handle nonlevel coupons as well. This feature can be found in some bonds that offer increasing coupon payments to hedge against inflation.

Examples

Suppose we have a five year 1,000 bond with a redemption amount of 1,250. Annual coupons start at 5% and then increase by 2% each year. If the bond is priced to yield 5% compounded annually, what is the price of the bond?

We can solve this problem with TmVal by providing a list of tuples to the alpha argument of the Bond class. Each tuple in the list is an ordered pair that represents a coupon rate and the beginning of the time period in which it is applicable.

In this case, we want [(.05, 0), (.051, 1), (.05202, 2), (.0530604,3), (.054121608, 4)].

We also need to supply a corresponding list to the cfreq argument of equal length to the list provided to alpha. Since that list has 5 elements, and for each of those periods we have one coupon payment, we set cfreq=[1,1,1,1,1]. While this particular example has all 1s, this feature allows us to model more complex bonds where the coupon frequency is not the same for each period.

In [1]: from tmval import Bond

In [2]: t = 5

In [3]: alpha_r = [.05 * 1.02 ** x for x in range(t)]

In [4]: alpha_t = [x for x in range(t)]

In [5]: alphas = [(x, y) for x, y in zip(alpha_r, alpha_t)]

In [6]: bd = Bond(
   ...:     face=1000,
   ...:     red=1250,
   ...:     alpha=alphas,
   ...:     cfreq=[1] * 5,
   ...:     gr=.05,
   ...:     term=t
   ...: )
   ...: 

# coupon amounts
In [7]: print(bd.coupons.amounts)
[50.0, 51.00000000000001, 52.02, 53.06040000000001, 54.121608]

# coupon times
In [8]: print(bd.coupons.times)
[1.0, 2.0, 3.0, 4.0, 5.0]

In [9]: print(bd.price)
1204.280709414385