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.007583333For 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.4012On 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.796038For 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.05024461The 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.001966146For 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.001482435In 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