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.
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.
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(.)
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 Premium
5, 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])
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")
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"))
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")
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(.)
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(.)
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(.)
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.
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)
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)])
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)])
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)])
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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"
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"
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"
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"
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"
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)]
)
Capital allocation via optimal financing mix and excess returns: https://www.linkedin.com/pulse/capital-allocation-via-optimal-financing-mix-excess-fermin-cota/↩︎
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.↩︎
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/↩︎
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/↩︎
The yield premium that this bond offers compared to similar maturity bond based on market yield curve↩︎
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 |