The ‘QuantBondCurves’ package offers a range of functions for valuing various asset types and generating financial curves. It covers fixed-coupon assets, floating notes and swaps, with varying payment frequencies. The package also enables the calibration of spot, instantaneous forward and basis curves, making it a powerful tool for accurate and flexible bond valuation and curve generation. The valuation and calibration techniques presented here are consistent with industry standards and incorporates author’s own calculations.
The coupon.dates()
function allows the user to calculate
the coupon payment dates of a given asset, based on its payment
frequency. In most cases, this process is straightforward. However,
certain scenarios require careful attention to detail. For the former
cases, the coupon.dates
function starts from
maturity
and crawls back using the annual payment frequency
freq
, until analysis.date
is reached:
coupon.dates(maturity = "2029-10-01", analysis.date = "2027-08-01", freq = 1, convention = "F")
#> $dates
#> [1] "2027-10-01" "2028-10-01" "2029-10-01"
#>
#> $effective.dates
#> [1] "2027-10-01" "2028-10-02" "2029-10-01"
As can be seen, the output is comprised of two vectors. Vector
$dates represents the dates on which coupon payments
are due and $effective.dates adjust each date if it
falls on a non-business day. This adjustment is done according to input
convention
, which offers the most commonly used conventions
in the industry. Please refer to the help viewer for details.
The maturity
input can either be expressed as a date or
as a numeric value in years. An example demonstrating the latter case is
provided below:
coupon.dates(maturity = 2, analysis.date = "2023-03-01", freq = 1)
#> $dates
#> [1] "2024-03-01" "2025-03-01"
#>
#> $effective.dates
#> [1] "2024-03-01" "2025-03-03"
For this example, maturity as a date is calculated from trade date
(i.e., going forward two years from “2023-03-01”). In this case, since
trade.date
input is not introduced, function assumes
analysis.date
is trade.date
.
As evidenced in the previous examples, for straightforward cases,
trade.date
of the asset is not required.
On the other hand, non trivial cases require both
trade.date
and maturity
as input. An example
is with financial assets that possess a distinct coupon period,
characterized by a different length compared to the remaining periods.
Below we present an example for a bond with a long first coupon:
coupon.dates(maturity = "2025-02-28", analysis.date = "2023-09-29",
asset.type = "Fixed Income", freq = 4,
trade.date = "2023-09-29", coupon.schedule = "LF")
#> $dates
#> [1] "2024-02-29" "2024-05-29" "2024-08-29" "2024-11-29" "2025-02-28"
#>
#> $effective.dates
#> [1] "2024-02-29" "2024-05-29" "2024-08-29" "2024-11-29" "2025-02-28"
In this example, coupon.schedule
is provided to
establish where the “outlier” coupon period has to be introduced; see
more in the help viewer.
Another detail about this function is the relationship between
asset.type
and freq
. The input
asset.type
is only required if the asset type is linked to
LIBOR (asset.type %in% c("LIBOR","LIBORSwaps")
), as in
these cases the actual trade.date
is two business days
after the introduced trade.date
. Otherwise, the information
in asset.type
is only used if freq
input is
not provided.
The coupons()
function calculates the cash flows for the
remaining life of an asset, following a date payment structure, a given
face value and a specified coupon rate.
Just like coupon.dates()
, most cases of coupon
calculation are straightforward. Once payment dates are known, only an
additional crawl back is required in order to establish the length of
the upcoming coupon period. For cases when the crawl back process is
inadequate (e.g., long first coupon bond), the coupons()
function offers the same solution of introducing trade.date
of the asset:
coupons(maturity = 4.08, analysis.date = "2021-02-28", coupon.rate = 0.03,
asset.type = "IBR", daycount = "ACT/360", trade.date = "2020-02-29",
coupon.schedule = "LF")
#> [1] 0.007500000 0.007666667 0.007666667 0.007583333 0.007500000 0.007666667
#> [7] 0.007666667 0.007583333 0.007500000 0.007666667 0.007666667 0.007583333
#> [13] 1.007583333
For this function, asset.type
is always required. For
asset.type
in (“LIBOR”, “IBR”,
“LIBORSwaps”,“UVRSwaps”, “IBRSwaps”) freq
is
assumed depending on the asset, if left blank. For asset.type in
(“FixedIncome”,“CCS”) freq
is never
assumed and must be introduced. Ultimately, when dealing with assets of
type “TES”, the coupon period is uniformly set to 1
year, meaning that the value specified for the daycount
input is disregarded. The remaining inputs are identical to those on the
coupon.dates()
function.
The discount.factors()
function allows the user to
calculate the discount factors of a given asset. Effective payment dates
(dates
) are required as input of the function, those can be
obtained from the $effective.dates vector with the
coupon.dates()
function.
Only for discount.factors()
, the parameter
freq
represents the compounding frequency to be used for
calculating the discrete discount factors (e.g., freq = 2
refers to semi-annual compounded discount rates). For any other function
in the ‘QuantBondCurves’ package, freq
represents the
annual frequency of coupon payments. Discrete compounded discount rates
option can be set with rate.type = 1
; alternatively, the
user can choose to work with continuously compounded rates
(rate.type = 0
).
Here is an example for calculating discount factors with monthly compounded discount rates:
The valuation.bonds()
function allows the user to value
bonds, fixed income securities, floating notes, spread income assets or
fixed legs of swaps. The input coupon.rate
can either
receive a unique number or a vector whose length matches the length of
coupon payments. A unique number should be used to value fixed income
assets, fixed legs of interest rate swaps or floating notes with the
method of using the floating rate observed on the previous coupon date,
common in several jurisdictions:
valuation.bonds(maturity = "2026-06-01", coupon.rate = 0.06, rates = 0.08,
principal = 1000, analysis.date= "2023-06-01")
#> [1] 948.4012
On the other hand, coupon.rate
as a vector can be used
to value floating notes with a forward rate for each coupon payment:
The bond.price2rate()
function is a useful tool that can
be used to calculate the internal rate of return (IRR) of a bond, after
price
as input is introduced:
The sens.bonds()
function allows the user to calculate
the sensitivity of a given bond to interest rates, either by inserting
in the price
input a bond price
(input = price
) or an IRR (input = rate
). The
output represents the percentage change in the price of the bond
resulting from a 1 basis point movement in interest rates. Here is an
example for the former case:
sens.bonds(input = "price", price = 1.02, maturity = "2023-01-03",
analysis.date = "2020-01-03", coupon.rate = 0.04,
principal = 1, asset.type = "FixedIncome", freq = 1,
rate.type = 1, daycount = "ACT/365", dirty = 1)
#> [1] 2.796038
For the latter case (input = rate
), price
can either receive a unique IRR or a rates vector that corresponds to
every coupon date:
The average.life()
function calculates the weighted
average life of a given bond. Similar to sens.bonds()
,
price
input can either receive a bond price
(input = price
) or an IRR (input = rate
).
Below we denote an example for the latter:
The curve.calibration()
function calibrates and returns
either a zero coupon bond or an instantaneous forward curve. The
calibration process is determined by parameters such as
npieces
, obj
, Weights
,
nsimul
, piece.term
, and
approximation
. Meanwhile, input data is determined by
yield.curve
, market.assets
,
noSpots
, and freq
. Finally, the
nodes
parameter specifies which terms (in years) are output
of the calibrated curve.
Delving into input data, yield.curve
is a vector with
the IRR’s of the financial assets, with names(yield.curve)
containing the maturity for each rate as numeric in years. If
noSpots = 3
, the first three arguments of
yield.curve
will be established as spot. The
market.assets
input is a matrix whose first column is the
coupon rate of each asset and the second column contains the maturities
of each asset as dates. For cases when the IRR’s of the assets are the
same as the coupon rate (e.g., IBR curve), the
market.assets
input can be left blank. Additionally, for
cases where market.assets
and yield.curve
are
required, maturity info can be left blank either in
names(yield.curve)
or in the second column of
market.assets
. Moreover, freq
parameter can be
provided as either a single value that applies to all input assets, or
as a vector in which each element corresponds to a specific asset.
Regarding calibration inputs, npieces
determines if the
calibration is done via a bootstrapping method or by minimization of the
residual sum of squares (RSS). If npieces = NULL
(its
default), then the bootstrapping method will output a number of curve
segments equal to the number of financial assets that have been input.
Below is an example for an instantaneous forward curve with
bootstrapping method for asset.type = TES
:
# The `yield.curve` input is created for the IRR's of the market assets.
yield.curve <- c(0.1233,0.1280,0.131,0.1315,0.132,0.1322,0.1325,0.1323,0.1321,0.132)
# The output terms desired are established.
nodes <- seq(0,10, by = 0.001)
# Since for TES, IRR's and coupon rates differ, `market.assets` input is required.
# Below it is constructed.
market.assets <- matrix(NA,nrow = 10,ncol = 2)
market.assets[1,2] <- "2020-01-03"
market.assets[2,2] <- "2021-01-03"
market.assets[3,2] <- "2022-01-03"
market.assets[4,2] <- "2023-01-03"
market.assets[5,2] <- "2024-01-03"
market.assets[6,2] <- "2025-01-03"
market.assets[7,2] <- "2026-01-03"
market.assets[8,2] <- "2027-01-03"
market.assets[9,2] <- "2028-01-03"
market.assets[10,2] <- "2029-01-03"
market.assets[1,1] <- 0.1233
market.assets[2,1] <- 0.1280
market.assets[3,1] <- 0.131
market.assets[4,1] <- 0.1315
market.assets[5,1] <- 0.132
market.assets[6,1] <- 0.1322
market.assets[7,1] <- 0.1325
market.assets[8,1] <- 0.1323
market.assets[9,1] <- 0.1321
market.assets[10,1] <- 0.132
# Calibration
curve.calibration(yield.curve = yield.curve, market.assets = market.assets,
analysis.date = "2019-01-03" , asset.type = "TES", freq = 1,
daycount = "ACT/365", fwd = 1, nodes = nodes, approximation = "constant")
Below, two results are presented. The first plot illustrates the results
of the previous example, while the second plot showcases the same
example but with a spot calibration. (fwd = 0):
Alternatively, if the bootstrapping method is not chosen, a curve with a
number of pieces specified will be optimized in order to minimize a
residual sum of squares (e.g, npieces = 4
). This residual
can be chosen with obj
, either by defining residual as the
difference between the market swap price and the estimated swap price
with the calibrated curve (obj = "Price"
) or by defining
the residual as the difference between the market IRR of the financial
asset and the estimated IRR with the calibrated curve. After the
residual is defined, the objective function to minimize is defined as
the dot product of Weights
vector and squared residuals. By
default, each financial asset is assigned equiprobable weights.
A problem that arises in the optimization process is that the
objective function isn’t differentiable with respect to the terms of the
curve segments. To address this problem, the nsimul
parameter can be used to establish the number of simulations to be
performed for possible configurations of terms for each curve segment.
These simulations are used as initial entries in the optimization
process, which can improve the chances of finding an optimal solution.
It is suggested to not use more than 20 simulations (nsimul
= 20) for instantaneous forward curve due to computational cost, unless
time is not an issue.
Alternatively, the user can define a unique term vector for every
piece with piece.term
. The piece.term
parameter in the yield curve construction represents the time to
maturity, in years, for a particular curve segment. It specifies the
distance in years from the analysis.date
to the term where
the segment ends.
Finally, either using bootstrapping method or (RSS), the user can
define if the curve to calibrate is linear piecewise
(approximation = "linear"
) or constant piecewise
(approximation = "constant"
). Below is an example for a
spot curve calibration using a predefined piece.term
instead of optimizing terms with nsimul
:
yield.curve <- c(0.103,0.1034,0.1092, 0.1161, 0.1233, 0.1280, 0.1310, 0.1320, 0.1325, 0.1320)
names(yield.curve) <- c(0,0.08,0.25,0.5,1,2,3,5,7,10)
nodes <- seq(0,10, by = 0.001)
# Calibration
curve.calibration (yield.curve = yield.curve, market.assets = NULL,
analysis.date = "2019-01-03", asset.type = "IBRSwaps",
freq = 4, npieces = 2, fwd = 0, obj = "Rates",
piece.term = 3, nodes = nodes, approximation = "linear")
Below, the result for the previous example and its equivalent with
instantaneous forward calibration is plotted:
An important detail for npieces
is that for
fwd = 0
, npieces
represents the amount of
segments besides all immutable spot segments. Therefore, for the
previous example, since for asset.type = "IBRSwaps"
the
default amount of noSpots
is 4, the actual amount of
npieces
of the curve will be 6
(noSpots
+npieces
). When fwd = 1
,
npieces
does represent the total amount of segments for the
calibrated curve. For instance, in the aforementioned plot, two curves
consisting of six pieces were plotted, but with different values of
npieces
: npieces
was set to 2 for
fwd = 0
, whereas it was set to 6 for
fwd = 1
.
Finally, is it important to note that piece.term
should
not include the last piece term, since it is assumed that the last term
coincides with the last maturity introduced in yield.curve
or market.assets
. Therefore, the piece.term
vector must always have a length equal to npieces
-
1.
The curve.calculation()
function performs
curve.calibration()
function for multiple analysis dates.
The main difference is that the series
input will replace
yield.curve
. The series
parameter represents a
matrix in which every column is a yield.curve
vector and
names(series)
contains the maturity for each rate as
numeric in years. Additionally, the rownames(series)
vector
must contain each analysis date desired. On the other hand,
market.assets
is a matrix just like in
curve.calibration()
, but now it must contain every single
asset of the series
matrix.
An additional feature of curve.calculation()
is that it
allows the user to merge a previous.curve
matrix with the
output matrix of calibrated curves. Below is an example where the
market.assets
input is required and
previous.curve
for analysis dates
“2014-01-01” and “2015-01-01” is
constant. In this case, since for these two dates curve is already
input, the calibration only takes place on “2016-01-01”
and “2017-01-01”:
# `previous.curve` input
previous.curve <- matrix(0.04,nrow = 2,ncol = 8)
rownames(previous.curve) <- c("2014-01-01","2015-01-01")
colnames(previous.curve) <- c(0, 0.25, 0.5, 1:5)
# `serie` input
serie <- matrix(NA,nrow = 4,ncol = 6)
rownames(serie) <- c("2014-01-01","2015-01-01","2016-01-01","2017-01-01")
colnames(serie) <- c(0, 0.08333, 0.25, 0.5, 1, 2)
serie[1,1] <- 0.04
serie[1,2] <- 0.05
serie[1,3] <- 0.06
serie[1,4] <- 0.065
serie[1,5] <- 0.07
serie[1,6] <- 0.075
serie[2,1] <- 0.03
serie[2,2] <- 0.04
serie[2,3] <- 0.05
serie[2,4] <- 0.063
serie[2,5] <- 0.074
serie[2,6] <- 0.08
serie[3,1] <- 0.06
serie[3,2] <- 0.065
serie[3,3] <- 0.07
serie[3,4] <- 0.08
serie[3,5] <- 0.084
serie[3,6] <- 0.09
serie[4,1] <- 0.02
serie[4,2] <- 0.03
serie[4,3] <- 0.04
serie[4,4] <- 0.042
serie[4,5] <- 0.045
serie[4,6] <- 0.05
# `market.assets` input
market.assets <- matrix(NA,nrow = 10,ncol = 2)
market.assets[1,1] <- 0.04
market.assets[2,1] <- 0.05
market.assets[3,1] <- 0.06
market.assets[4,1] <- 0.07
market.assets[5,1] <- 0.08
market.assets[6,1] <- 0.09
market.assets[7,1] <- 0.06
market.assets[8,1] <- 0.07
market.assets[9,1] <- 0.075
market.assets[10,1] <- 0.07
market.assets[1,2] <- "2016-01-01"
market.assets[2,2] <- "2016-02-01"
market.assets[3,2] <- "2016-04-01"
market.assets[4,2] <- "2016-07-01"
market.assets[5,2] <- "2017-01-01"
market.assets[6,2] <- "2017-02-01"
market.assets[7,2] <- "2017-04-01"
market.assets[8,2] <- "2017-07-01"
market.assets[9,2] <- "2018-01-01"
market.assets[10,2] <- "2019-01-01"
# Calculation
curve.calculation(serie = serie, market.assets = market.assets, noSpots = 1,
previous.curve = previous.curve, asset.type = "TES",
freq = 1, rate.type = 1, fwd = 0,
nodes = c(0, 0.25, 0.5, 1:5), approximation = "linear")
#> 0 0.25 0.5 1 2 3
#> 2014-01-01 0.04 0.04000000 0.0400000 0.04000000 0.04000000 0.04000000
#> 2015-01-01 0.04 0.04000000 0.0400000 0.04000000 0.04000000 0.04000000
#> 2016-01-01 0.06 0.06983019 0.0797851 0.08397703 0.09021406 0.09023113
#> 2017-01-01 0.02 0.04093220 0.0427312 0.04512698 0.05024461 0.05024461
#> 4 5
#> 2014-01-01 0.04000000 0.04000000
#> 2015-01-01 0.04000000 0.04000000
#> 2016-01-01 0.09023113 0.09023113
#> 2017-01-01 0.05024461 0.05024461
The spot2forward()
function allows the user to transform
a spot curve into a forward instantaneous curve. Every node introduced
in the spot
input is transformed into a forward node. The
maturity of each rate must be introduced in names(spot)
, as
numeric in years (counting from analysis.date
). Finally,
approximation
refers to the approximation of the initial
spot curve. Therefore, when transforming a spot piecewise linear curve
into an instantaneous forward curve, it is necessary to define
approximation = "linear"
. Below is an example:
# Inputs for calibration of spot curve
yield.curve <- c(0.015,0.0175, 0.0225, 0.0275, 0.0325, 0.0375,0.04,0.0425,0.045,0.0475,0.05)
names(yield.curve) <- c(0.5,1,2,3,4,5,6,7,8,9,10)
nodes <- seq(0,10,0.001)
# Calibration
spot <- curve.calibration (yield.curve = yield.curve, market.assets = NULL,
analysis.date = "2019-01-03" , asset.type = "IBRSwaps",
freq = 4, rate.type = 0, fwd = 0, npieces = NULL,
nodes = nodes, approximation = "linear")
# Spot to Forward
dates <- names(spot)
spot2forward(dates, spot, approximation = "linear")
The fwd2spot()
function allows the user to transform a
forward instantaneous curve into a spot curve. Every node introduced in
fwd
input is transformed into a spot node. Maturity of each
rate as numeric in years must be introduced in names(fwd)
.
Just like spot2forward()
, approximation
refers
to the approximation of the initial input forward curve. Below is an
example:
# Inputs for calibration of forward curve
yield.curve <- c(0.015,0.0175, 0.0225, 0.0275, 0.0325, 0.0375,0.04,0.0425,0.045,0.0475,0.05)
names(yield.curve) <- c(0.5,1,2,3,4,5,6,7,8,9,10)
nodes <- seq(0,10,0.001)
# Calibration
fwd <- curve.calibration (yield.curve = yield.curve, market.assets = NULL,
analysis.date = "2019-01-03", asset.type = "LIBORSwaps",
freq = 4, rate.type = 0, daycount = "ACT/365",
npieces = NULL, fwd = 1, nodes = nodes,
approximation = "linear")
# Forward to Spot
dates <- names(fwd)
fwd2spot(dates, fwd, approximation = "linear")
The basis.curve()
function calibrates a “discount basis
rate” curve according to data of cross currency swaps. It follows a
similar structure with the same features as
curve.calibration()
. The main difference is that the data
input is swaps
, a matrix that contains relevant information
for every Cross Currency Swap (CCS). In detail, each row represents a
swap and the columns represent, respectively: maturity, legs, coupon
rate of local leg, coupon rate of foreign leg, spread of local leg,
spread of variable leg, principal of local leg and principal of variable
leg. Columns in swaps
can be placed in any order, but every
column must be labeled with the following labels:
colnames(swaps) <- c("Mat" ,"Legs", "C1" , "C2", "spread1", "spread2", "prin1", "prin2")
.
Basis curve can be calibrated with any type of Cross Currency Swap,
either fixed local leg vs. fixed foreign leg (Legs = FF
),
fixed local leg vs. variable foreign leg (Legs = FV
),
variable local leg vs. a fixed foreign leg (Legs = VF
) or
variable local leg vs. variable foreign leg
(Legs = VV
).
Additionally, there is a new parameter: ex.rate
. It
represents the exchange rate between the two currencies involved in the
CCS on analysis.date
. In the next example, both new inputs
are created:
ex.rate <- 4814
swaps <- rbind(c("2024-03-01", "FF", 0.07 , 0.0325, NA , NA , 2000 * ex.rate, 2000),
c("2025-03-01", "VV", NA , NA , 0.015, 0.0175, 2000 * ex.rate, 2000),
c("2026-03-01", "FF", 0.075, 0.03 , NA , NA , 5000000, 5000000 / ex.rate),
c("2027-03-01", "VV", NA , NA , 0.01 , 0.015 , 5000000, 5000000 / ex.rate),
c("2028-03-01", "FF", 0.08 ,0.035 , NA , NA , 3000000, 3000000 / ex.rate),
c("2029-03-01", "VV", NA , NA , 0.01 , 0.0125, 3000000, 3000000 / ex.rate))
colnames(swaps) <- c("Mat" ,"Legs", "C1" , "C2", "spread1", "spread2", "prin1", "prin2")
Below, an example of piecewise linear basis curve calibration using the RSS method is performed. Additionally, the constant calibration alternative is added to the plot:
# Inputs for calibration of spot curve
yield.curve <- c(0.015,0.0175, 0.0225, 0.0275, 0.0325, 0.0375,0.04,0.0425,0.045,0.0475,0.05)
names(yield.curve) <- c(0.5,1,2,3,4,5,6,7,8,9,10)
nodes <- seq(0,10,0.001)
# Calibration of local spot curve
rates <- curve.calibration (yield.curve = yield.curve, market.assets = NULL,
analysis.date = "2019-01-03" , asset.type = "IBRSwaps",
freq = 4, rate.type = 0, fwd = 0, npieces = NULL,
obj = "Price", nodes = nodes, approximation = "linear")
# Calibration of Basis Curve
nodes <- seq(0,10,0.001)
basis.curve(swaps = swaps, ex.rate = 4814, analysis.date = "2023-03-01",
rates = rates, rates2 = rates / 4, freq = c(2,2,2,2,1,1),
rate.type = 1, npieces = 4, obj = "Price", Weights = NULL,
nsimul = 10, nodes = nodes, approximation = "linear")
The valuation.swaps()
function offers the possibility of
valuing an Interest Rate Swap (IRS) or a Cross Currency Swap (CCS). For
the former case, coupon.rate
input is used to value the
fixed leg, spread
is an optional input for the variable leg
and rates
vector is used for discounting the cashflows. If
analysis.date
doesn’t belong to a coupon date,
float.rate
must be introduced and represents the variable
rate observed on the previous coupon date.
In the next example, the analysis.date
is inside the
coupon dates. Therefore, even if float.rate
is introduced,
its effect on output is null since the function automatically
establishes that float.rate
is equivalent to the first
entry of rates
input:
valuation.swaps(maturity = "2026-07-01", analysis.date = "2023-01-01",
asset.type = "IBRSwaps", freq = 4, coupon.rate = 0.04,
rates = rep(0.04,14), float.rate = 500)
#> [1] -0.001966146
For cases where analysis.date
doesn’t belong to a coupon
date, the float.rate
input is necessary to value the
upcoming coupon of variable leg:
valuation.swaps(maturity = "2026-07-01", analysis.date = "2023-02-01",
asset.type = "IBRSwaps", freq = 4, coupon.rate = 0.04,
rates = rep(0.04,14), float.rate = 0.042)
#> [1] -0.001482435
In the context of Cross Currency Swaps (CCS),
coupon.rate
is used for valuation of local fixed legs. The
spread
parameter represents the spread for the local
variable leg, while rates
are the discount rates for the
local leg. The float.rate
parameter denotes the variable
local rate observed on the previous coupon date and just like in the IRS
case, it is only required if local leg is variable and
analysis.date
doesn’t belong to a coupon date. In parallel,
coupon.rate2
, spread2
, rates2
and
float.rate2
represent the same attributes but for foreign
legs.
rates2
input is only necessary if the foreign leg is a
variable leg, in order to transform the variable into a fixed foreign
leg. After which, basis.rates
input is used as the discount
foreign rates to value the fixed foreign leg. For rates
,
rates2
and basis.rates
,
curve.calculation()
and basis.curve()
can be
used. These three inputs can either be a vector with a rate for every
coupon date, or a curve that contains nodes with, at least, three
decimals:
# Curve Calibration for `rates` input
yield.curve <- c(0.103,0.1034,0.1092, 0.1161, 0.1233, 0.1280, 0.1310, 0.1320, 0.1325)
names(yield.curve) <- c(0,0.08,0.25,0.5,1,2,3,5,6)
nodes <- seq(0, 10, by = 0.001) # Our curve has nodes with three decimals.
rates <- curve.calibration (yield.curve = yield.curve, market.assets = NULL,
analysis.date = "2023-03-01", asset.type = "IBRSwaps",
freq = 4, rate.type = 0, daycount = "ACT/365", fwd = 0,
npieces = NULL, obj = "Rates", nsimul = nsimul,
nodes = nodes, approximation = "constant")
# Curve Calibration for `basis.rates` input
nodes <- seq(0, 10, by = 0.001)
rates2 <- rates/4 # It is assumed foreign curve is proportional to local spot curve.
# Swaps input for calibration
ex.rate <- 4814
swaps <- rbind(c("2024-03-01", "FF", 0.07 , 0.0325, NA , NA , 2000 * ex.rate, 2000),
c("2025-03-01", "VV", NA , NA , 0.015, 0.0175, 2000 * ex.rate, 2000),
c("2026-03-01", "FF", 0.075, 0.03 , NA , NA , 5000000, 5000000 / ex.rate),
c("2027-03-01", "VV", NA , NA , 0.01 , 0.015 , 5000000, 5000000 / ex.rate),
c("2028-03-01", "FF", 0.08 ,0.035 , NA , NA , 3000000, 3000000 / ex.rate),
c("2029-03-01", "VV", NA , NA , 0.01 , 0.0125, 3000000, 3000000 / ex.rate))
colnames(swaps) <- c("Mat" ,"Legs", "C1" , "C2", "spread1", "spread2", "prin1", "prin2")
# Calibration
basis.rates <- basis.curve(swaps, ex.rate = 4814, analysis.date = "2023-03-01",
rates = rates, rates2 = rates2, freq = c(2,2,2,2,1,1),
rate.type = 1, npieces = NULL, obj = "Price",
Weights = NULL, nodes = nodes, approximation = "linear")
Below, an example of a CCS with two variable legs is shown; hence,
all three input curves (rates, rates2, basis.rates
) are
required:
# Valuation
valuation.swaps (maturity = "2024-03-01", analysis.date = "2023-03-01", asset.type = "CCS",
freq = 2, coupon.rate = NA, rates = rates, float.rate = NULL, spread = 0.015,
principal = 2000 * ex.rate, Legs = "VV", ex.rate = ex.rate,
basis.rates = basis.rates, coupon.rate2 = NA, rates2 = rates2,
float.rate2 = NULL, spread2 = 0.0175, principal2 = 2000, rate.type = 0,
daycount = "ACT/365", loc = "BOG")
#> [1] 442310.2