1 Introduction

In investment management, the basic portfolio optimization problem is to maximize expected return subject to risk being less than some prescribed limit. A large number of linear constraints can be added and they serve to regularize the solution, that is to say, reduce over-concentration and make stop the target portfolio moving too much as the market moves. We can also deal with the risk and constraints side including transaction costs. Broadly, in a fixed income setting this reduces to constraints on interest rates and credit duration, sectors, credits, taxes and maturities. But what about expected return? Expected return is not a simple thing to quantify, but at least in fixed income it can be broken down into several components, some of which are easier than others:

[1] Yield (or carry), which is most easily understood as the PL we derive from sitting on the position while the market remains still. In more detail that does not mean the bond price remaining fixed, because there is a pull-to-par effect. For a premium bond, the coupon exceeds the yield, but we lose money as time progresses and the price moves towards par. Similarly for a discount bond. So, it really means the yield curve remaining as it is.

[2] The whole market moves. In \(2022\), for example, maximizing yield led to the lowest return, not the highest. Interest rates and credit spreads both rose, causing mark-to-market losses. Those who stayed in cash did best, with the exception of those who eked out a small profit by sitting on very short-dated bonds that have since matured or are about to.

[3] Individual issuers move. We can buy a three-year bond at \(75\), with a yield of \(25\%\), but that is not much use if it defaults. Back this summer, we were looking at a particular issuer, and its \(2025s\) was trading at pretty much that level. We thought it was not a good investment since its debt to capital ratio was above the optimal level1 by a factor of \(2x\). Now it is trading in the low \(50s\) and the yield is ~\(40\%\) or so. But yield does not equate to expected return.

From what we have seen, [2] and [3] ingredients are much more difficult than [1], and still investment banks, which purport to provide fixed income portfolio advisory to clients, go no further than maximization of yield. I would not recommend anyone to do that. The problem with maximizing yield is that we are likely to be in the worse credits, or longer duration, and both are going to hurt. One useful early warning sign is the risk premium2, which is the reward we get for investing, and when it is high we should be paid a higher spread.

But how to translate all of these into a portfolio? There are a couple of approaches. The first allows us to carry on maximizing yield if we must but with a risk constraint. So we need some measure of how geared to the market we are, and the obvious criterion is the \(CSW10\) measure, which is sensitivity to spread increasing by \(10\%\). The higher the duration, or the wider the spread, the more risk we will be running. We can decide how much risk we run by comparing with a benchmark, e.g. \(100\%\) makes us market weight, \(50\%\) is severely underweight, and so on.

The second allows us to reprice the intended portfolio not off today’s yield curve, but off a wider one that anticipates higher yields in the future. This solution for navigating widening markets, in which yields are on the rise, is a bit more formal but allows us to put in a view of where we think credit markets are likely to go. We simply calculate total return, taking an anticipated yield movement into account. So the \(ER\) is not just the yield, from that must be subtracted the mark-to-market loss associated with the yield curve widening. It can also be done sector by sector, so for example we can push all industrial names wider if we want. Finally, it can be done at single-name level.

When we maximize \(ER\) we will naturally screen out long-duration bonds, though we will not necessarily avoid bad credits. If we just maximize yield, then we have a fundamental problem avoiding them, because by definition they are the highest-yielding instruments around. Maximizing expected return is likely to be better. But even so, keeping them out is hard, simply because even with the possibility of default factored in, they can still look quite attractive, unless we downgrade them severely.

The lines of defense against bad credits are:

  • Lower issuer concentration on worse credits. Like the \(1-2-3\) rule which says no more than \(3\%\) for \(IG\), \(2\%\) for \(BB\), \(1\%\) for \(B\) (and possibly \(0.5\%\) for \(CCC\)). Larger institutional mandates will require tighter constraints in grounds of practicality, i.e. liquidity will be a problem.

  • Credit analysts indicate whether they are confident about an issuer, by specifying lower concentration where necessary—and total avoidance of names that are fundamentally non-analyzable.

  • Where available, using the implied equity risk premium3 to tell us what it thinks. It can point to situations where the equity market is overpriced, in which case the credit market is telling us to short the stock, but sometimes it is the bond market that is being overoptimistic.

  • Knowing where the models are have nothing useful to say about a credit4, particularly in idiosyncratic binary situations that are make-or-break for a firm.
  • Cutting names that we no longer understand.

Any credit research team that works properly must be able to address the following \(3\) key questions:

Credit analysts are great at coming with answers to where do we think it should trade, though such answers might be wrong, particularly if the model does not explore a wide enough range of scenarios. They are far worse at answering why does it trade where it does. And without answering the why there can be no answer to what is the catalyst that takes it from one to the other. For that they need to know where each issuer is in the capital structure. And second, what do they think about the company’s excess return on invested capital. Let us start with the second question first. Scaling profits to invested capital yields accounting returns, and comparing those returns to costs of funding, we get excess returns, shorthand for the value created or destroyed by growth. Now suppose a company is about to increase its leverage to fund growth, and the credit market is pricing most of that event in. By using the current leverage without regard to that likely increase we will get an implied asset vol that is too high (to match the credit spread), making the credit look overly attractive. But it won’t be, and if the market has not priced in the full leverage increase, the spread will likely widen further, and the investor will likely lose money.

2 Inputs

2.1 Bonds

The bond information table contains general information about each issue. For example, if a $\(1000\) dollar par bond is selling at a discount for $\(975\), enter price as $\(97.50\). For treasuries, you can enter prices as a dollar figure and number of \(32\)nds by using a - instead of a .. For example, a price of \(101-08\) is interpreted as \(101\) and \(8/32\) or \(101.25\). This conversion also supports formats such as \(99-10+\) (with + meaning add \(1/16\)) and \(99-102\) (with the \(2\) at the end indicating additional \(2/256\)). Also, price should be a clean price rather than a dirty price and should not include any markup for accrued interest. It is ok if price includes a dealer markup. Alternately, you could subtract out the markup from the price and enter it explicitly as a fee. If the price already includes the markup, do not reenter the fee separately (unless you decrease the price appropriately first). Also, do not include accrued interest in the fees. Finally, enter the number of bonds purchased assuming $\(1000\) par value per bond.

inputs_lst$bonds_rng %>%
  map_dfc(~.x) %>%
  format.dt.f(.)

2.2 Yields

Information about current bond yields. This information attempts to facilitate how the bond is priced compared to the current market yield curve by extrapolation. LY Premium5, for example, is calculated by comparing the yield on the bond with the yield returned by plugging the bond’s maturity into the market yield curve entered in the yield curve table.

# https://rpubs.com/rafael_nicolas/global_interest_rates_dynamics_inflation
inputs_lst$yields_rng %>%
  reduce(left_join, by="tenor") %>%
  format.dt.f(., ron_vars=NA, perc_vars=names(.)[-1])

2.3 Maturities

Desired portfolio percentage for various timeframes. The time brackets (tenors) are user customizable.

inputs_lst$maturity_rng %>%
  map_dfc(~.x) %>%
  format.dt.f(., ron_vars=NA, perc_vars="desired")

2.4 Bond Types

Desired portfolio percentage for each bond type (muni, corp, tres, agency).

inputs_lst$types_rng %>%
  map_dfc(~.x) %>%
  format.dt.f(., ron_vars=NA, perc_vars=c("tax", "desired"))

2.5 Quality

Desired portfolio percentage for each credit tier6:

# https://github.com/rnfermincota/academic/tree/main/research/traditional_assets/database
# https://rpubs.com/rafael_nicolas/crp
inputs_lst$quality_rng %>%
  map_dfc(~.x) %>%
  format.dt.f(., ron_vars=NA, perc_vars="desired")

3 Output

3.1 Calculations

The bonds.port.calculations.f function possesses an extremely comprehensive and dynamic algorithm to assist its user in fixed income portfolio management. The function produces a wide selection of outputs and provides information on both the individual bonds in the portfolio, and the overall portfolio. The function allows users to perform sensitivity analysis on the portfolio by adding and taking away bonds from the portfolio to analyze the overall effect. The function is dynamic and allows the option to input more variables above what is required to further enhance the analysis generated with just the required inputs. The function will also automatically adjust its output matrix to any changes made, whether it be by adding more bonds to the portfolio or adding an optional input.

# bonds.port.calculations.f for tracking a bond portfolio. Analytic measures for each bond in the portfolio such as Duration, Bond Risk, and Sensitivity to changes in interest rates.
rst_lst=bonds.port.calculations.f(inputs_lst)
rst_lst$bonds_port_calcs_tbl %>%
  format.dt.f(.)

3.1.1 Purchase Information

This section contains the purchase information for the issue.

bonds_port_information_lst=bonds.port.information.tbl.f(rst_lst$bonds_port_calcs_tbl)

bonds_port_information_lst$general_bond_information %>%
  cbind(bonds_port_information_lst$purchase_information) %>%
  format.dt.f(.)

3.1.2 Current Valuation

This section contains several computed columns that contain information about the various financial properties of the bond and the portfolio. There is some magic in the computation of the “Fair YTM”. This column attempts to indicate how the bond is priced compared to the current market yield curve by extrapolation. We need the current market yield curve data for this field to be meaningful.

bonds_port_information_lst$general_bond_information %>%
  cbind(bonds_port_information_lst$current_valuation) %>%
  format.dt.f(.)

3.1.3 Bond Risk Analysis

This section contains a few risk analytics that show modified duration, bond risk, and bond sensitivity. Together, these metrics can be used to gauge the portfolio’s sensitivity to changes in interest rates. In particular, the bond risk field attempts to estimate the change in market value in each bond and the portfolio overall that would likely result from a \(100\) basis point (\(1\%\)) change in interest rates.

bonds_port_information_lst$general_bond_information %>%
  cbind(bonds_port_information_lst$bond_risk_analysis) %>%
  format.dt.f(.)

Interest rate sensitivity (bond risk) is determined by \(dP=(MDuration)\times(dy)-0.5\times convexity \times (dy)^2\). This takes into account the overstatement of sensitivity in modified duration (straight line estimate of relationship) by removing the excess in duration that is caused by the convexity of a best fit parabola fitted to the bond’s yield curve.

Finally, the Modified Duration and Bond Sensitivity fields are used to give an indication of what might happen to the value of each holding and the portfolio overall as interest rates change.

3.2 Cash Flows

This table shows the monthly and quarterly cash flows that the portfolio will provide. After tax cash flows from each bond are reduced based on the tax rate that applies to the bond’s type (ie. muni, tres, agency, corp). Tax rates are taken from the types table.

rst_lst$bonds_port_coupons_tbl %>%
  format.dt.f(., ron_vars=names(.)[-1], perc_vars=NA)

3.3 Bond Types

This table displays the amount of the portfolio that’s in each type of Bond (muni, corp, tres, agency) and also shows the current portfolio yield by bond type. Average yield, duration, and credit quality per bond type are also displayed.

rst_lst$bonds_port_types_tbl %>%
  format.dt.f(., ron_vars=names(.)[c(5:6, 11:12)], perc_vars=names(.)[c(2:3, 7:9)])

3.4 Credit Quality

Breaks down portfolio value by credit quality and shows yields for each credit tier. Cumulative percent in the percent of the portfolio that has a credit rating as good or better than the rating for the given row.

rst_lst$bonds_port_quality_tbl %>%
  format.dt.f(., ron_vars=names(.)[c(4:5, 10:11)], perc_vars=names(.)[c(2:3, 6:8)])

3.5 Maturities

This table displays the amount of the portfolio that will be maturing in various timeframes. Also, for all the bonds maturing within a given time bracket, average yield and credit quality are computed.

rst_lst$bonds_port_maturity_tbl %>%
  format.dt.f(., ron_vars=names(.)[c(6, 11:13)], perc_vars=names(.)[c(3:4, 7:9)])

4 References

4.1 Present Values

4.1.1 annuity.pv.f

Purpose:

The annuity.pv.f function utilizes rate.equivalent.f, along with the interest rate, number of periods, installments (cash flow), cash flow at the end of the annuity, a boolean for immediate start, frequency of annuity payments, and frequency of compounding of interest rates as inputs. To calculate the present value.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
n.periods Numeric The number of periods in the annuity.
installment Numeric The installment (cash flow) per period.
terminal.payment Numeric Any cash flow at the end of the annuity. For example, a bullet repayment at maturity of the unamortized principal.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.

Code Example:

# Find the present value at 10% of 15 annual installments of 1 each period:
annuity.pv.f(rate=10e-2, n.periods=15)
[1] 7.60608

# Find present value at 10% of 15 annual installments of 1 each period where the annuity 
# starts immediately:
annuity.pv.f(rate=10e-2, n.periods=15, immediate.start = TRUE)
[1] 8.366687

4.1.2 annuity.fv.f

Purpose:

The annuity.pv.f function utilizes rate.equivalent.f, along with the interest rate, number of periods, installments (cash flow), cash flow at the end of the annuity, a boolean for immediate start, frequency of annuity payments, and frequency of compounding of interest rates as inputs. To calculate the future value.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
n.periods Numeric The number of periods in the annuity.
installment Numeric The installment (cash flow) per period.
terminal.payment Numeric Any cash flow at the end of the annuity. For example, a bullet repayment at maturity of the unamortized principal.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
# Find the future value at 10% of 15 annual installments of 1 each period:
annuity.fv.f(rate=10e-2, n.periods=15)
[1] 31.77248

# Find future value at 10% of 15 annual installments of 1 each period where the annuity 
# starts immediately:
annuity.fv.f(rate=10e-2, n.periods=15, immediate.start = TRUE)
[1] 34.94973

4.1.3 annuity.installment.f

Purpose:

The annuity.installment.f function utilizes rate.equivalent.f, along with the interest rate, number of periods, present value, future value, installments (cash flow), cash flow at the end of the annuity, a boolean for immediate start, frequency of annuity payments, and frequency of compounding of interest rates as inputs. To calculate the annual installment.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
n.periods Numeric The number of periods in the annuity.
pv Numeric The present value of all the cash flows including the terminal payment.
fv Numeric The future value (at the end of the annuity) of all the cash flows including the terminal payment.
installment Numeric The installment (cash flow) per period.
terminal.payment Numeric Any cash flow at the end of the annuity. For example, a bullet repayment at maturity of the unamortized principal.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.

Code Example:

# Suppose a loan of 10,000 is to be repaid in 8 equal annual installments and the 
# interest rate is 9% annually compounded. Find the annual installment:
annuity.installment.f(rate=9e-2, pv=10000, n.periods=8)
[1] 1806.744

4.1.4 annuity.periods.f

Purpose:

The annuity.periods.f function utilizes rate.equivalent.f, along with the interest rate, the number of periods, present value, future value, installment, cash flow at the end of the annuity, a boolean for immediate start, frequency of annuity payments, frequency of compounding of interest rates, and round to an integer number. To calculate the the time when an annuity actually pays out to an annuity holder.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
n.periods Numeric The number of periods in the annuity.
pv Numeric The present value of all the cash flows including the terminal payment.
fv Numeric The future value (at the end of the annuity) of all the cash flows including the terminal payment.
installment Numeric The installment (cash flow) per period.
terminal.payment Numeric Any cash flow at the end of the annuity. For example, a bullet repayment at maturity of the unamortized principal.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# The number of periods is 8, and present value is equal to 10,000 
# interest rate is 9% annually compounded. Find the annuity period:
annuity.installment.f(rate=9e-2, n.periods = 8, pv = 10000)
[1] 1806.744

4.1.5 annuity.rate.f

Purpose:

The annuity.rate.f utilizes the rate.equivalent.f function, along with the interest rate, number of periods, present value, future value, installments (cash flow), cash flow at the end of the annuity, a boolean for immediate start, frequency of annuity payments, and frequency of compounding of interest rates as inputs. To calculate the interest rates.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
n.periods Numeric The number of periods in the annuity.
pv Numeric The present value of all the cash flows including the terminal payment.
fv Numeric The future value (at the end of the annuity) of all the cash flows including the terminal payment.
installment Numeric The installment (cash flow) per period.
terminal.payment Numeric Any cash flow at the end of the annuity. For example, a bullet repayment at maturity of the unamortized principal.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.

Code Example:

# Find the semi-annually compounded rate if the above annuity has present value of 50,000:
annuity.rate.f(pv=50000, installment = 450, n.periods=360, cf.freq=12, comp.freq=2)
[1] 0.1052609

# Negative interest rates are handled without difficulty:
annuity.rate.f(pv=250000, installment = 450, n.periods=360, cf.freq=12, comp.freq=2)
[1] -0.02685041

4.1.6 annuity.installment.breakup.f

Purpose:

The annuity.installment.breakup.f function utilizes annuity.installment.f, rate.equivalent.f, and annuity.fv.f functions. In addition, it utilizes the interest rate, number of periods, present value, a boolean for immediate start, frequency of annuity payments, frequency of compounding of interest rates, and period as inputs. For which the installment needs to be broken up to accurately calculate the principal and interest components.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
n.periods Numeric The number of periods in the annuity.
pv Numeric The present value of all the cash flows including the terminal payment.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Breakup the 5th installment of above annuity into its principal and interest components:
AIB <- annuity.installment.breakup.f(rate=9e-2, pv=10000, n.periods=8, period.no=5)
unlist(AIB) # we use unlist to print the result more compactly
opening.principal     interest.part    principal.part closing.principal 
        5853.3437          526.8009         1279.9428         4573.4009 

4.1.7 npv.f

Purpose:

The npv.f function utilizes rate.equivalent.f, along with cash flow, interest rate, frequency of annuity payments, frequency of compounding of interest rates, optional vector of timing of cash flows, and boolean for immediate start as inputs. To calculate Net Present Value for cash flows with different cash flow and compounding conventions.

Variable Type Description
cf Numeric Vector of cash flows.
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
cf.t Numeric Optional vector of timing (in years) of cash flows. If omitted regular sequence of years is assumed.

Code Example:

# Find Net Present Value (NPV) at 5% of cash flows of 100, 250 and 300 in years 
# 1, 2 and 3 respectively:
npv.f(cf=c(100,250,300), rate=5e-2)
[1] 581.1467

# Find NPV at 10% of cash flows of 1, 3 and 2 in years 0.3, 1.9 and 2.5
# respectively (this is known in XNPV in spreadsheet software):
npv.f(cf=c(1,3,2), rate=10e-2, cf.t=c(0.3,1.9,2.5))
[1] 5.050866

4.2 Compounding

4.2.1 rate.equivalent.f

Purpose:

Converts an interest rate from one compounding convention to another (for example from semi-annual to monthly compounding or from annual to continuous compounding). Using the interest rate, frequency of compounding of the given interest rate, and frequency of compounding to which the given interest rate is to be converted.

Variable Type Description
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
from.freq Numeric Frequency of compounding of the given interest rate: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Equivalent rate with different compounding frequency
# Convert 10% monthly compounded rate to equivalent semi-annually compounded rate
rate.equivalent.f(10e-2, from.freq = 12, to.freq = 2)
[1] 0.1021066
# Convert 15% annually compounded rate to equivalent continuously compounded rate
rate.equivalent.f(15e-2, from.freq = 1, to.freq = Inf)
[1] 0.1397619

4.3 Roots

4.3.1 bisection.root.f

Purpose:

Find zero of a function by bracketing the zero and then using bisection. Tries to find the zero of a function by using the bisection method (uniroot). To call uniroot, the zero must be bracketed by finding two points at which the function value has opposite signs. The main code in this function is a grid search to find such a pair of points. A geometric grid of points between lower and guess and also between guess and upper. This grid is searched for two neighboring points across which the function changes sign. This brackets the root, and then we try to locate the root by calling uniroot.

Variable Type Description
f Function The function whose zero is to be found. An R function object that takes one numeric argument and returns a numeric value. In an IRR application, this will be the NPV function. In an implied volatility application, the value will be the option price.
guess Numeric The starting value (guess) from which the solver starts searching for the root. Must be positive.
lower Numeric The lower end of the interval within which to search for the root. Must be positive.
upper Numeric The upper end of the interval within which to search for the root. Must be positive.
nstep Numeric The number of steps in the grid search to bracket the zero.

Code Example:

# For example, the values of 1, 7, 12 and 13 do not bracket the root of sin(x) 
# since the function is positive at all these values, but `bisection.root` finds the 
# root given these numbers as the interval and the guess value.
bisection.root.f(sin, guess = 7, lower=1, upper=13) 
[1] 6.283185

4.3.2 newton.raphson.root.f

Purpose:

The newton.raphson.root.f function is a general root finder which can find the zero of any function whose derivative is available. If the iterations converge to a genuine root (within the limits of toler), then it returns the root that was found.

Variable Type Description
f Function The function whose zero is to be found. An R function object that takes one numeric argument and returns list of two components (value and gradient). In an IRR application, these two components will be the NPV and the DV01/10000. In an implied volatility application, the components will be the option price and the vega. See also the examples below.
guess Numeric The starting value (guess) from which the solver starts searching for the IRR.
lower Numeric The lower end of the interval within which to search for the root. Must be positive.
upper Numeric The upper end of the interval within which to search for the root. Must be positive.
max.iter Numeric The maximum number of iterations of the Newton-Raphson procedure.
toler Numeric The criterion to determine whether a zero has been found. If the value of the function exceeds toler in absolute value, then NA is returned with a warning.

Code Example:

# Find root of the equation sin(x) = cos(x). (Analytically, the root is known to be pi/4)
newton.raphson.root.f(function(x){list(value=sin(x)-cos(x), gradient=cos(x)+sin(x))}) 
[1] 0.7853982

# Try to find root of equation which has no real root: sin(x) = -2
newton.raphson.root.f(function(x){list(value=2 + sin(x), gradient=cos(x))}) 
[1] NA

4.4 IRR Functions

4.4.1 irr.f

Purpose:

The irr.f function computes IRR (Internal Rate of Return) for cash flows with different cash flow and compounding conventions.

Variable Type Description
cf Numeric Vector of cash flows.
interval Numeric the interval c(lower, upper) within which to search for the IRR
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
cf.t Numeric Optional vector of timing (in years) of cash flows. If omitted regular sequence of years is assumed.
r.guess Numeric The starting value (guess) from which the solver starts searching for the IRR
toler Numeric The argument toler for irr.solve. The IRR is regarded as correct if abs(NPV) is less than toler. Otherwise the irr function returns NA.
convergence Numeric The argument convergence for irr.solve
max.iter Numeric The argument max.iter for irr.solve.

Code Example:

# https://rpubs.com/rafael_nicolas/irr_mirr

# Find the rate of interest (IRR) if a loan of 600 is repaid in two annual 
# installments of 300 and 400:
irr.f(c(-600,300,400))
[1] 0.1039126

# Negative interest rates are handled without difficulty
irr.f(c(-600,100,400))
[1] -0.09592852

# Find the rate of interest if a loan of 450 is repaid by paying 100
# after 0.3 years, 300 after 1.9 years and 200 after 2.5 years (this is
# known in XIRR in spreadsheet software)
irr.f(cf=c(-450,100,300,200), cf.t=c(0, 0.3,1.9,2.5)) 
[1] 0.1746131

# Find multiple IRRs by providing interval within which to search for the IRR:
irr.f(cf=c(100, -230, 132), interval = c(0.05, 0.17)) 
[1] 0.1

4.4.2 irr.solver.f

Purpose:

The irr.solver.f function computes the internal rate of return at which the net present value equals zero.

Variable Type Description
f Function The function whose zero is to be found. An R function object that takes one numeric argument and returns a list of two components (value and gradient). In the IRR applications, these two components will be the NPV and its derivative.
interval Numeric the interval c(lower, upper) within which to search for the IRR.
r.guess Numeric The starting value (guess) from which the solver starts searching for the IRR
toler Numeric The argument toler for irr.solve. The IRR is regarded as correct if abs(NPV) is less than toler. Otherwise the irr function returns NA.
convergence Numeric The argument convergence for irr.solve
max.iter Numeric The argument max.iter for irr.solve.

Code Example:

# Function to find Internal Rate of Return (IRR) at which the net present value equals zero
# Equation f(x) = 10(1+x)^3 - 5(1+x)^2 - 7 
fx = function(x){list(value= 10*(1+x)^3-5*(1+x)^2-7, gradient=30*(1+x)^2-10*(1+x))}
irr.solver.f(f = fx, interval = c(0.01, 0.5), r.guess = 0.2)
[1] 0.08960416

4.5 Day Count Functions

4.5.1 yearFraction.f

Purpose:

The yearFraction.f function calculates the fraction of the year represented by the number of whole days between two dates depending upon its convention. The result will be given as a decimal.

Variable Type Description
d1 Date/String The starting date of period for day counts.
d2 Date/String The ending date of period for day counts.
r1 Date/String The starting date of reference period for ACT/ACT day counts.
r2 Date/String The ending date of reference period for ACT/ACT day counts.
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
# Calculates the fraction of the year given two dates
yearFraction.f(d1 = "2022-02-02", d2 = "2022-05-05", r1 = "2022-02-02", r2 = "2022-05-05")
[1] 0.2583333

4.5.2 daycount.actual.f

Purpose:

The daycount.actual.f function utilizes d1 and d2 as inputs to calculate the difference between two dates in numbers of days.

Variable Type Description
d1 Date/String The starting date of period for day counts.
d2 Date/String The ending date of period for day counts.

Code Example:

# Find the day count difference between two dates
d1 = "2020-05-17"; d2 = "2020-10-24"
daycount.actual.f(d1 = d1, d2 = d2)
[1] 160

4.5.3 daycount.30.360.f

Purpose:

The daycount.30.360 function utilizes d1 and d2 as inputs calculate the difference between two dates in numbers of days. In addition, it converts all the months that have 31 days to 30, making the year to have a total of 360 days instead of 365. This function will be use depending on the interest of the user.

Variable Type Description
d1 Date/String | The starting date of period for day counts.
d2 Date/String | The ending date of period for day counts.

Code Example:

# Find the difference between two dates given a certain variant, and "30/360"
d1 = "2020-05-17"; d2 = "2020-10-24"
daycount.30.360.f(d1 = d1, d2 = d2, variant = "IT")
[1] 157

4.5.4 edate.f

Purpose:

The edate.f function will take a date and a number of months as inputs, to add or subtract the number of months to the given date. The output will be the date with the numbers of months added or subtracted.

Variable Type Description
dates Date/String Any date.
# Find date eight months prior to 17 May 2005:
edate.f("2005-05-17", -8) 
[1] "2004-09-17"

# Find date four months after 28 February 2007. Note that the output is the last 
# day of June and not the 28th June
edate.f("2007-02-28", 4) 
[1] "2007-06-28"

4.6 Coupons Functions

4.6.1 coupons.dates.f

Purpose:

The coupons.date.f function utilizes coupons.n.f and edate.f, along with the settlement date, maturity date, and frequency of coupon payments as inputs. To calculate the number of dates given the settlement and maturity dates. The number of dates will vary depending on the frequency, it could show annually, semi-annual or monthly.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.

Code Example:

# Find all the coupon dates of a bond annually:
coupons.dates.f(settle="2020-04-15", mature="2022-04-01", freq = 1)
[1] "2021-04-01" "2022-04-01"

# Find all the coupon dates of a bond semi-annually:
coupons.dates.f(settle="2020-04-15", mature="2022-04-01", freq = 2)
[1] "2020-10-01" "2021-04-01" "2021-10-01" "2022-04-01"

# Find all the coupon dates of a bond monthly:
coupons.dates.f(settle="2020-04-15", mature="2022-04-01", freq = 12)
 [1] "2020-05-01" "2020-06-01" "2020-07-01" "2020-08-01" "2020-09-01"
 [6] "2020-10-01" "2020-11-01" "2020-12-01" "2021-01-01" "2021-02-01"
[11] "2021-03-01" "2021-04-01" "2021-05-01" "2021-06-01" "2021-07-01"
[16] "2021-08-01" "2021-09-01" "2021-10-01" "2021-11-01" "2021-12-01"
[21] "2022-01-01" "2022-02-01" "2022-03-01" "2022-04-01"

4.6.2 coupons.n.f

Purpose:

The coupons.n.f function utilizes edate.f, along with the settlement date, maturity date, and frequency of coupon payments as inputs. To calculate the numbers of payments needed from the settlement date till the maturity. The number of payments will vary depending on the frequency, it could show annually, semi-annual or monthly.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.

Code Example:

# Find the number of coupons payments annually:
coupons.dates.f(settle="2020-04-15", mature="2022-04-01", freq = 1)
[1] "2021-04-01" "2022-04-01"

# Find the number of coupons payments semi-annually:
coupons.dates.f(settle="2020-04-15", mature="2022-04-01", freq = 2)
[1] "2020-10-01" "2021-04-01" "2021-10-01" "2022-04-01"

# Find the number of coupons payments monthly:
coupons.dates.f(settle="2020-04-15", mature="2022-04-01", freq = 12)
 [1] "2020-05-01" "2020-06-01" "2020-07-01" "2020-08-01" "2020-09-01"
 [6] "2020-10-01" "2020-11-01" "2020-12-01" "2021-01-01" "2021-02-01"
[11] "2021-03-01" "2021-04-01" "2021-05-01" "2021-06-01" "2021-07-01"
[16] "2021-08-01" "2021-09-01" "2021-10-01" "2021-11-01" "2021-12-01"
[21] "2022-01-01" "2022-02-01" "2022-03-01" "2022-04-01"

4.6.3 coupons.next.f

Purpose:

The coupons.next.f function utilizes coupons.n.f and edate.f functions, along the settlement date, maturity date, and frequency of coupon payments as inputs. To calculate the date for the next payment given the settlement and maturity. The date of the next payment will vary depending on the frequency, it could show annually, semi-annual or monthly.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.

Code Example:

# Find a specific coupon date for payments done annually:
coupons.next.f(settle="2020-04-15", mature="2022-04-01", freq = 1)
[1] "2021-04-01"

# Find a specific coupon date for payments done semi-annually:
coupons.next.f(settle="2020-04-15", mature="2022-04-01", freq = 2)
[1] "2020-10-01"

# Find a specific coupon date for payments done monthly:
coupons.next.f(settle="2020-04-15", mature="2022-04-01", freq = 12)
[1] "2020-05-01"

4.6.4 coupons.prev.f

The coupons.next.f function utilizes coupons.n.f and edate.f functions, along the settlement date, maturity date, and frequency of coupon payments as inputs. To calculate the date for the previous payment given the settlement and maturity. The date of the previous payment will vary depending on the frequency, it could show annually, semi-annual or monthly.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
# Find a specific coupon date for payments done annually:
coupons.prev.f(settle="2020-08-15", mature="2022-01-01", freq = 1)
[1] "2020-01-01"

# Find a specific coupon date for payments done semi-annually:
coupons.prev.f(settle="2020-08-15", mature="2022-01-01", freq = 2)
[1] "2020-07-01"

# Find a specific coupon date for payments done monthly:
coupons.prev.f(settle="2020-08-15", mature="2022-01-01", freq = 12)
[1] "2020-08-01"

4.6.5 bond.tcf.f

Purpose:

The bond.tcf.f function utilizes coupons.next.f, coupons.prev.f, and yearFraction.f along with the settlement date, maturity date, coupon rate, frequency of coupon payments, convention, and redemption value as inputs. To calculate the cashflow dates in number of years, cash flows, and accrued interest for the any given settlement and maturity date.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.

Code Example:

# Calculates t: A vector of cash flow dates in number of years,
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 1)$t
[1] 0.9611111 1.9611111
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 2)$t
[1] 0.4611111 0.9611111 1.4611111 1.9611111
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 12)$t
 [1] 0.04444444 0.12777778 0.21111111 0.29444444 0.37777778 0.46111111
 [7] 0.54444444 0.62777778 0.71111111 0.79444444 0.87777778 0.96111111
[13] 1.04444444 1.12777778 1.21111111 1.29444444 1.37777778 1.46111111
[19] 1.54444444 1.62777778 1.71111111 1.79444444 1.87777778 1.96111111

# Calculates cf: A vector of cash flows
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 1)$cf
[1]  10 110
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 2)$cf
[1]   5   5   5 105
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 12)$cf
 [1]   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333
 [7]   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333
[13]   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333
[19]   0.8333333   0.8333333   0.8333333   0.8333333   0.8333333 100.8333333

# Calculates accrued: The accrued interest
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 1)$accrued
[1] 0.3888889
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 2)$accrued
[1] 0.3888889
bond.tcf.f(settle = "2020-04-15", mature = "2022-04-01", coupon = 0.10, freq = 12)$accrued
[1] 0.3888889

4.6.6 bond.tenors.f

Purpose:

The bond.tenors.f function utilizes yearFraction.f function, along with the settlement date, maturity date, frequency of coupon payments, and convention. To calculate the maturities time vector in years.

Variable Type Description
SETTLEMENT Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
MATURITIES Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
FREQUENCY Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.

Code Example:

# Return maturity time vector in years
bond.tenors.f(SETTLEMENT = "2020-04-15", MATURITIES = c("2022-04-01", "2022-03-01"), CONVENTION = "30/360")
[1] 1.961111 1.877778

4.7 Bond Pricing Functions

4.7.1 bond.price.f

Purpose:

The bond.price.f function utilizes bond.tcf.f and rate.equivalent.f, along settlement date, maturity date, coupon rate, frequency of coupon payments, yield to maturity of the bond, convention, frequency of compounding of interest rates, and principal amount as inputs. To calculate the cash or clean price of a security that pays periodic interest.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
redemption_value Numeric The principal amount that the bond will pay on maturity or call. Typically necessary when the bond is expected to be called at premium to par.

]

Code Example:

# Find price on 15 April 2012 of 8% bond maturing on 1 January 2022 at 8.8843% Clean Price
bond.price.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, yield=8.8843e-2, freq=1, output = 0)
[1] 94.33212

# Find price on 15 April 2012 of 8% bond maturing on 1 January 2022 at 8.8843% Cash Price
bond.price.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, yield=8.8843e-2, freq=1, output = 1)
[1] 96.64323

# Same bond with annual coupon and semi-annually compounded yield. We set the `comp-freq` 
# argument to use a compounding frequency different from the coupon frequency):
bond.price.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, yield=8.8843e-2, freq=1, comp.freq=2)
[1] 93.13936

4.7.2 bond.prices.f

Purpose:

The bond.prices.f function utilizes bond.tcf.f and rate.equivalent.f, along settlement date, maturity date, coupon rate, frequency of coupon payments, yield to maturity of the bond, convention, frequency of compounding of interest rates, and principal amount as inputs. To calculate multiple cash or clean price of a security that pays periodic interest.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
redemption_value Numeric The principal amount that the bond will pay on maturity or call. Typically necessary when the bond is expected to be called at premium to par.

Code Example:

# Find prices of many bonds with one function call
bond.prices.f(settle="2012-01-01", mature=c("2022-01-01", "2032-01-01"), coupon=c(0.09, 0.08,0.07,0.06), yield=0.10)
[1] 93.76889 82.84091 81.26262 65.68183

4.8 Bond Yield Functions

4.8.1 bond.yield.f

Purpose:

The bond.yield.f function utilizes bond.tcf.f function, along with the bond’s settlement date, maturity date, coupon rate, frequency of coupon payments,clean price, yield, day count convention, frequency of compounding of interest rates, and redemption value as inputs. To calculate the yield to maturity of a bond.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
price Numeric The value of the bond less accrued coupon.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# A bond with 5% coupon is callable at 102 on 1-Jan-2023 and is trading at an yield 
# to call of 6% on 1-Jan-2018. The price is:
bond.yield.f(settle='2018-01-01', mature='2023-01-01', coupon=5e-2, price=101, redemption_value = 102)
[1] 0.05126732
# Compute the yield given the price:
bond.yield.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, price=95) 
[1] 0.08772274
# Negative yields are handled properly:
bond.yield.f(settle="2012-04-15", mature="2017-01-01", coupon=1e-2, price=120) 
[1] -0.02927941
# Yield to call:
bond.yield.f(settle='2018-01-01', mature='2023-01-01', coupon=5e-2, price=101, redemption_value = 102)
[1] 0.05126732

4.8.2 bond.yields.f

Purpose:

The bond.yields.f function utilizes bond.tcf.f function, along with the bond’s settlement date, maturity date, coupon rate, frequency of coupon payments,clean price, yield, day count convention, frequency of compounding of interest rates, and redemption value as inputs. To calculate multiple yields to maturity of a bond.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
price Numeric The value of the bond less accrued coupon.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Find yields of many bonds with one function call
bond.yields.f(settle="2012-01-01", mature=c("2022-01-01", "2032-01-01"), coupon=c(0.09, 0.08,0.07,0.06), price=c(94, 83, 81, 65))
[1] 0.09961336 0.09978548 0.10048382 0.10110332

4.8.3 bond.current.yield.f

Purpose:

The bond.current.yield.f function utilizes bond.tcf.f function, along the settlement date, maturity date, coupon rate, frequency of coupon payments, value of the bond less acrrued coupon, the yield of the maturity, day count conventions, frequency of compounding of interest rates, and the principal amount that the bond will pay on maturity or call as inputs. To calculate the current yield of a security that pays periodic interest

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
price Numeric The value of the bond less accrued coupon.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Returns the yield of a security that pays periodic interest:
bond.current.yield.f(settle='2018-01-01', mature='2023-01-01', coupon=5e-2, price=101, redemption_value = 100)
[1] 0.04950495

4.8.4 bond.effective.yield.f

Purpose:

The bond.effective.yield.f function utilizes bond.yield.f function, along the settlement date, maturity date, coupon rate, frequency of coupon payments, value of the bond less accrued coupon, the yield of the maturity, day count conventions, frequency of compounding of interest rates, and the principal amount that the bond will pay on maturity or call as inputs. To calculate the effective yield of a security that pays periodic interest

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
price Numeric The value of the bond less accrued coupon.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Returns the effective yield of a security that pays periodic interest
bond.effective.yield.f(settle='2018-01-01', mature='2023-01-01', coupon=5e-2, price=101, redemption_value = 100)
[1] 0.04829772

4.8.5 bond.call.yield.f

Purpose:

The bond.call.yield.f function utilizes bond.yield.f function, To calculate multiple yields to maturity of a bond.

Variable Type Description
clean_price Numeric The value of the bond less accrued coupon.
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
call_date Date/String The date when a callable bond can be redeemed for a specifc call price before its maturity date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
call_price Numeric The price at which the issuer of a callable security has the right to buy back that security from an investor or creditor.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
# A bond with 5% coupon is callable at 102 on 1-Jan-2023 on 1-Jan-2018. 
# With a clean price of 101. The price is:
bond.call.yield.f(settle='2018-01-01', call_date='2023-01-01', coupon=5e-2, clean_price=101, call_price = 102)
[1] 0.05126732

4.8.6 bond.real.yield.f

Purpose:

The bond.real.yield.f functions utilizes the bond.price.f and bond.tenors.f functions, along the settlement date, maturity date, sell date, reinvestment rate, coupon rate, desired yield, entry price, frequency of coupon payments, convention, and frequency of compounding of interest rates as inputs. To calculate the effective annual interest rate, given the reinvestment rate of the coupons.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
sell_date Date/String The sell date on which the bond will have to be sold.
reinv_rate Numeric The reinvestment rate of the coupon.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
target_yield Numeric The desired yield for the swap of the bonds.
entry_price Numeric The entry price for the bond.
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
# Returns the effective annual interest rate, given the reinvestment rate of the coupons
bond.real.yield.f(settle='2018-01-01', mature='2023-01-01', sell_date='2020-09-23', 
                  reinv_rate=0.03, coupon=0.05, target_yield=0.06, entry_price=101)
[1] 0.04247999

4.9 Bond Duration Functions

4.9.1 duration.f

Purpose:

The duration.f function utilizes rate.equivalent.f, along the cash flows, interest rate, frequency of annuity payments, frequency of compounding of interest rates, vector of timing of cash flows, a boolean for immediate start, and a boolean for modified duration as inputs. To calculate the sensitivity of the price of a bond or other debt instrument to a change in interest rate.

Variable Type Description
cf Numeric Vector of cash flows.
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
cf.t Numeric Optional vector of timing (in years) of cash flows. If omitted regular sequence of years is assumed.
immediate.start Boolean Logical variable which is TRUE for immediate annuities (the first installment is due immediately) and FALSE for deferred annuities (the first installment is due at the end of the first period).

Code Example:

# Find duration of a stream of cash flows of 100, 250 and 300 in years 1, 2 and 3 
# when the interest rate is 5%:
duration.f(cf=c(100,250,300), rate=5e-2)
[1] 2.282051

# Find modified duration of same cash flows
duration.f(cf=c(100,250,300), rate=5e-2, modified=TRUE)
[1] 2.173382

4.9.2 bond.duration.f

Purpose:

The bond.duration.f function utilizes bond.tcf.f, yearFraction.f, and duration.f, along settlement date, maturity date, coupon rate, frequency of coupon payments, yield to maturity of the bond, convention, boolean for modified duration, frequency of compounding of interest rates, and redemption value as inputs. To calculate the Macaulay duration of a bond.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
modified Boolean A logical value used in duration. TRUE to return Modified Duration, FALSE otherwise.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Duration and convexity on 15 April 2012 of 8% bond maturing on 1 January 2022 at
# 8.8843% yield (semi-annual coupons and semi-annually compounded yield)
bond.duration.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, yield=8.8843e-2)
[1] 6.678709

# Modified duration of same bond:
bond.duration.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, yield=8.8843e-2, modified=TRUE)
[1] 6.394649

4.9.3 bond.durations.f

Purpose:

The bond.duration.f function utilizes bond.tcf.f, yearFraction.f, and duration.f, along settlement date, maturity date, coupon rate, frequency of coupon payments, yield to maturity of the bond, convention, boolean for modified duration, frequency of compounding of interest rates, and redemption value as inputs. To calculate with multiple coupon rates and maturity dates the Macaulay duration of a bond.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
modified Boolean A logical value used in duration. TRUE to return Modified Duration, FALSE otherwise.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Find duration of many bonds with one function call:
bond.durations.f(settle="2012-01-01", mature=c("2022-01-01", "2032-01-01"), coupon=c(0.09, 0.08,0.07,0.06),  yield=0.10)
[1] 6.681623 9.385458 7.029001 9.959342

4.9.4 convexity.f

Purpose:

The convexity.f function utilizes rate.equivalent.f, along cash flows, interest rates, frequency of annuity payments, frequency of compounding of interest rates, years of cash flows, and boolean of immediate start as inputs. To accurately calculate the convexity of a bond, which is really important to assess risk.

Variable Type Description
cf Numeric Vector of cash flows.
rate Numeric The interest rate in decimal (0.10 or 10e-2 for 10%)
cf.freq Numeric Frequency of annuity payments: 1 for annual, 2 for semi-annual, 12 for monthly.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.
cf.t Numeric Optional vector of timing (in years) of cash flows. If omitted regular sequence of years is assumed.

Code Example:

# Find convexity of a stream of cash flows of 100, 250 and 300 in years 1, 2 and 3 
# when the interest rate is 5%:
convexity.f(cf=c(100,250,300), rate=5e-2)
[1] 7.638159

4.9.5 bond.convexity.f

Purpose:

The bond.convexity.f function utilizes bond.tcf.f and convexity.f, along with the settlement date, maturity date, coupon rate, frequency of coupon payments, yield to maturity of the bond, convention, frequency of compounding of interest rates, and redemption value as inputs. To calculate the sensitivity of a bond’s duration to changes in yield.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Convexity on 15 April 2012 of 8% bond maturing on 1 January 2022 at
# 8.8843% yield (semi-annual coupons and semi-annually compounded yield)
bond.convexity.f(settle="2012-04-15", mature="2022-01-01", coupon=8e-2, yield=8.8843e-2)
[1] 54.78545

4.9.6 bond.convexities.f

Purpose:

The bond.convexities.f function utilizes bond.tcf.f and convexity.f, along with the settlement date, maturity date, coupon rate, frequency of coupon payments, yield to maturity of the bond, convention, frequency of compounding of interest rates, and redemption value as inputs. To calculate the sensitivity of a bond’s duration to changes in yield. Given multiple maturity dates, and coupon rates.

Variable Type Description
settle Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
mature Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
coupon Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
freq Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
yield Numeric The yield to maturity of the bond.
convention String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
comp.freq Numeric Frequency of compounding of interest rates: 1 for annual, 2 for semi-annual, 12 for monthly, Inf for continuous compounding.

Code Example:

# Find convexity of many bonds with one function call:
bond.convexities.f(settle="2012-01-01", mature=c("2022-01-01", "2032-01-01"), coupon=c(0.09, 0.08,0.07,0.06),  yield=0.10)
[1] 6.646147 9.266692 6.993713 9.832219

4.9.7 yield.sensitivity.f

Purpose:

The yield.sensitivity.f function is to generate bond prices given a range of specified yields. As the name of the function suggests it provides sensitivity analysis on bond price movements relative to changes in interest rates. The function utilizes bond portfolio management measurements for risk, duration and convexity, in order to provide a robust analysis for its user.

Variable Type Description
CLEAN_PRICE Numeric The price of a coupon bond not including accrued interest payments.
SETTLEMENT Date/String The settlement date for which the bond is traded. Can be a character string or object that can be converted into date using as.Date.
MATURITY Date/String The maturity date of the bond. Can be a character string or any object that can be converted into date using as.Date.
COUPON Numeric The coupon rate in decimal (0.10 or 10e-2 for 10%)
FREQUENCY Numeric The frequency of coupon payments: 1 for annual, 2 for semi-annual, 12 for monthly.
REDEMPTION Numeric The principal amount that the bond will pay on maturity or call. Typically necessary when the bond is expected to be called at premium to par.
COUNT_BASIS String Implements 30/360, ACT/360, ACT/360 and 30/360E day count conventions.
YIELD_MIN Numeric The minimum yield to maturity of the bond.
YIELD_MAX Numeric The maximum yield to maturity of the bond.

Code Example:

tbl_out <- yield.sensitivity.f(
  CLEAN_PRICE=104,
  SETTLEMENT=as.Date("2021-10-01"),
  MATURITY=as.Date("2030-10-01"),
  COUPON=0.06,
  FREQUENCY=2,
  REDEMPTION=100,
  COUNT_BASIS=c("30/360"),
  YIELD_MIN=0.04,
  YIELD_MAX=0.1,
  YIELD_DELTA=0.001
) %>% 
  as_tibble(.)

tbl_out %>%
  format.dt.f(
    ron_vars=names(.)[c(2, 4, 8)],
    perc_vars=names(.)[-c(2, 4, 8)]
  )

  1. Capital allocation via optimal financing mix and excess returns: https://www.linkedin.com/pulse/capital-allocation-via-optimal-financing-mix-excess-fermin-cota/↩︎

  2. The risk premiums that we observe are a function of how much risk capital there is in play, with risk premiums going up when risk capital becomes scarcer and down, when risk capital is more plentiful. In the bond and loan market access to risk capital will determine default spreads on bonds, with lower rated bonds feeling the pain more intensely when risk capital is withdrawn or moves to the side lines. Not only will default spreads widen more for lower-rated bonds, but there will be less bond issuances by riskier companies. In the equity market, the equity risk premium is the price of risk, and its movements will track shifts in risk capital, increasing as risk capital becomes scarcer. A pull back in risk capital is often the catalyst for market corrections, where price not only converges back on value, but often overshoots in the other direction (creating under valuations). It behooves both investors and traders to therefore track movements in risk capital, since it will determine when long term bets on value will pay off for the former, and the timing of entry into and exit from markets for the latter.↩︎

  3. The forward market equity risk premia can be derived by solving for the discount rate that equates future cash flows to the current market price: https://www.linkedin.com/posts/rnfc_covid-19-implied-equity-risk-premium-activity-6663866086435487744-dPJW/↩︎

  4. Optimal financing mix for a total of \(41,489\) companies globally: https://www.linkedin.com/pulse/optimal-financing-mix-total-41489-companies-globally-fermin-cota/↩︎

  5. The yield premium that this bond offers compared to similar maturity bond based on market yield curve↩︎

  6. index moody sp tier description
    1 FDIC FDIC 1 FDIC Insured Issue
    2 Aaa AAA 1 Prime. Maximum Safety
    3 Aa1 AA+ 2 High Grade. High Quality
    4 Aa2 AA 2 High Grade. High Quality
    5 Aa3 AA- 2 High Grade. High Quality
    6 A1 A+ 3 Upper Medium Grade
    7 A2 A 3 Upper Medium Grade
    8 A3 A- 3 Upper Medium Grade
    9 Baa1 BBB+ 4 Lower Medium Grade
    10 Baa2 BBB 4 Lower Medium Grade
    11 Baa3 BBB- 4 Lower Medium Grade
    12 Ba1 BB+ 5 Non Investment Grade
    13 Ba2 BB 5 Speculative
    14 Ba3 BB- 5 Speculative
    15 B1 B+ 6 Highly Speculative
    16 B2 B 6 Highly Speculative
    17 B3 B- 6 Highly Speculative
    18 Caa CCC+ 7 Substantial Risk
    19 Caa1 CCC 7 In Poor Standing
    20 Caa2 CCC- 7 In Poor Standing
    21 Ca CC 7 Extremely Speculative
    22 C C 7 May be in Default
    23 NR NR 8 Unknown rating or unrated bond
    ↩︎